M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/sysimg.jlญ# This file is a part of Julia. License is MIT: https://julialang.org/license Core.include(Main, "Base.jl") using .Base # Set up Main module using Base.MainInclude # ans, err, and sometimes Out import Base.MainInclude: eval, include # Ensure this file is also tracked pushfirst!(Base._included_files, (@__MODULE__, abspath(@__FILE__))) # set up depot & load paths to be able to find stdlib packages Base.init_depot_path() Base.init_load_path() if Base.is_primary_base_module # load some stdlib packages but don't put their names in Main let # Loading here does not call __init__(). This leads to uninitialized RNG # state which causes rand(::UnitRange{Int}) to hang. This is a workaround: task = current_task() task.rngState0 = 0x5156087469e170ab task.rngState1 = 0x7431eaead385992c task.rngState2 = 0x503e1d32781c2608 task.rngState3 = 0x3a77f7189200c20b task.rngState4 = 0x5502376d099035ae # Stdlibs sorted in dependency, then alphabetical, order by contrib/print_sorted_stdlibs.jl # Run with the `--exclude-jlls` option to filter out all JLL packages stdlibs = [ # No dependencies :ArgTools, :Artifacts, :Base64, :CRC32c, :FileWatching, :Libdl, :Logging, :Mmap, :NetworkOptions, :SHA, :Serialization, :Sockets, :Unicode, # 1-depth packages :LinearAlgebra, :Markdown, :Printf, :Random, :Tar, # 2-depth packages :Dates, :Future, :InteractiveUtils, :LibGit2, :UUIDs, # 3-depth packages :REPL, :TOML, # 4-depth packages :LibCURL, # 5-depth packages :Downloads, # 6-depth packages :Pkg, ] # PackageCompiler can filter out stdlibs so it can be empty maxlen = maximum(textwidth.(string.(stdlibs)); init=0) tot_time_stdlib = 0.0 # use a temp module to avoid leaving the type of this closure in Main m = Module() GC.@preserve m begin print_time = @eval m (mod, t) -> (print(rpad(string(mod) * " ", $maxlen + 3, "โ”€")); Base.time_print(stdout, t * 10^9); println()) print_time(Base, (Base.end_base_include - Base.start_base_include) * 10^(-9)) Base._track_dependencies[] = true tot_time_stdlib = @elapsed for stdlib in stdlibs tt = @elapsed Base.require(Base, stdlib) print_time(stdlib, tt) end for dep in Base._require_dependencies dep[3] == 0.0 && continue push!(Base._included_files, dep[1:2]) end empty!(Base._require_dependencies) Base._track_dependencies[] = false print_time("Stdlibs total", tot_time_stdlib) end # Clear global state empty!(Core.ARGS) empty!(Base.ARGS) empty!(LOAD_PATH) Base.init_load_path() # want to be able to find external packages in userimg.jl ccall(:jl_clear_implicit_imports, Cvoid, (Any,), Main) tot_time_userimg = @elapsed (isfile("userimg.jl") && Base.include(Main, "userimg.jl")) tot_time_base = (Base.end_base_include - Base.start_base_include) * 10.0^(-9) tot_time = tot_time_base + tot_time_stdlib + tot_time_userimg println("Sysimage built. Summary:") print("Base โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ "); Base.time_print(stdout, tot_time_base * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_base / tot_time) * 100); println("%") print("Stdlibs โ”€โ”€โ”€โ”€โ”€ "); Base.time_print(stdout, tot_time_stdlib * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_stdlib / tot_time) * 100); println("%") if isfile("userimg.jl") print("Userimg โ”€โ”€โ”€โ”€โ”€ "); Base.time_print(stdout, tot_time_userimg * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_userimg / tot_time) * 100); println("%") end print("Total โ”€โ”€โ”€โ”€โ”€โ”€โ”€ "); Base.time_print(stdout, tot_time * 10^9); println(); empty!(LOAD_PATH) empty!(DEPOT_PATH) end empty!(Base.TOML_CACHE.d) Base.TOML.reinit!(Base.TOML_CACHE.p, "") @eval Sys begin BINDIR = "" STDLIB = "" end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/Base.jl้K# This file is a part of Julia. License is MIT: https://julialang.org/license baremodule Base using Core.Intrinsics, Core.IR # to start, we're going to use a very simple definition of `include` # that doesn't require any function (except what we can get from the `Core` top-module) const _included_files = Array{Tuple{Module,String},1}(Core.undef, 1) function include(mod::Module, path::String) ccall(:jl_array_grow_end, Cvoid, (Any, UInt), _included_files, UInt(1)) Core.arrayset(true, _included_files, (mod, ccall(:jl_prepend_cwd, Any, (Any,), path)), arraylen(_included_files)) Core.println(path) ccall(:jl_uv_flush, Nothing, (Ptr{Nothing},), Core.io_pointer(Core.stdout)) Core.include(mod, path) end include(path::String) = include(Base, path) # from now on, this is now a top-module for resolving syntax const is_primary_base_module = ccall(:jl_module_parent, Ref{Module}, (Any,), Base) === Core.Main ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Base, is_primary_base_module) # The @inline/@noinline macros that can be applied to a function declaration are not available # until after array.jl, and so we will mark them within a function body instead. macro inline() Expr(:meta, :inline) end macro noinline() Expr(:meta, :noinline) end # Try to help prevent users from shooting them-selves in the foot # with ambiguities by defining a few common and critical operations # (and these don't need the extra convert code) getproperty(x::Module, f::Symbol) = (@inline; getglobal(x, f)) getproperty(x::Type, f::Symbol) = (@inline; getfield(x, f)) setproperty!(x::Type, f::Symbol, v) = error("setfield! fields of Types should not be changed") getproperty(x::Tuple, f::Int) = (@inline; getfield(x, f)) setproperty!(x::Tuple, f::Int, v) = setfield!(x, f, v) # to get a decent error getproperty(x, f::Symbol) = (@inline; getfield(x, f)) function setproperty!(x, f::Symbol, v) ty = fieldtype(typeof(x), f) val = v isa ty ? v : convert(ty, v) return setfield!(x, f, val) end dotgetproperty(x, f) = getproperty(x, f) getproperty(x::Module, f::Symbol, order::Symbol) = (@inline; getglobal(x, f, order)) function setproperty!(x::Module, f::Symbol, v, order::Symbol=:monotonic) @inline ty = Core.get_binding_type(x, f) val = v isa ty ? v : convert(ty, v) return setglobal!(x, f, val, order) end getproperty(x::Type, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order)) setproperty!(x::Type, f::Symbol, v, order::Symbol) = error("setfield! fields of Types should not be changed") getproperty(x::Tuple, f::Int, order::Symbol) = (@inline; getfield(x, f, order)) setproperty!(x::Tuple, f::Int, v, order::Symbol) = setfield!(x, f, v, order) # to get a decent error getproperty(x, f::Symbol, order::Symbol) = (@inline; getfield(x, f, order)) function setproperty!(x, f::Symbol, v, order::Symbol) @inline ty = fieldtype(typeof(x), f) val = v isa ty ? v : convert(ty, v) return setfield!(x, f, val, order) end function swapproperty!(x, f::Symbol, v, order::Symbol=:not_atomic) @inline ty = fieldtype(typeof(x), f) val = v isa ty ? v : convert(ty, v) return Core.swapfield!(x, f, val, order) end function modifyproperty!(x, f::Symbol, op, v, order::Symbol=:not_atomic) @inline return Core.modifyfield!(x, f, op, v, order) end function replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) @inline ty = fieldtype(typeof(x), f) val = desired isa ty ? desired : convert(ty, desired) return Core.replacefield!(x, f, expected, val, success_order, fail_order) end convert(::Type{Any}, Core.@nospecialize x) = x convert(::Type{T}, x::T) where {T} = x include("coreio.jl") eval(x) = Core.eval(Base, x) eval(m::Module, x) = Core.eval(m, x) # init core docsystem import Core: @doc, @__doc__, WrappedException, @int128_str, @uint128_str, @big_str, @cmd if isdefined(Core, :Compiler) import Core.Compiler.CoreDocs Core.atdoc!(CoreDocs.docm) end include("exports.jl") if false # simple print definitions for debugging. enable these if something # goes wrong during bootstrap before printing code is available. # otherwise, they just just eventually get (noisily) overwritten later global show, print, println show(io::IO, x) = Core.show(io, x) print(io::IO, a...) = Core.print(io, a...) println(io::IO, x...) = Core.println(io, x...) end """ time_ns() -> UInt64 Get the time in nanoseconds. The time corresponding to 0 is undefined, and wraps every 5.8 years. """ time_ns() = ccall(:jl_hrtime, UInt64, ()) start_base_include = time_ns() # A warning to be interpolated in the docstring of every dangerous mutating function in Base, see PR #50824 const _DOCS_ALIASING_WARNING = """ !!! warning Behavior can be unexpected when any mutated argument shares memory with any other argument. """ ## Load essential files and libraries include("essentials.jl") include("ctypes.jl") include("gcutils.jl") include("generator.jl") include("reflection.jl") include("options.jl") # define invoke(f, T, args...; kwargs...), without kwargs wrapping # to forward to invoke function Core.kwcall(kwargs::NamedTuple, ::typeof(invoke), f, T, args...) @inline # prepend kwargs and f to the invoked from the user T = rewrap_unionall(Tuple{Core.Typeof(kwargs), Core.Typeof(f), (unwrap_unionall(T)::DataType).parameters...}, T) return invoke(Core.kwcall, T, kwargs, f, args...) end # invoke does not have its own call cache, but kwcall for invoke does setfield!(typeof(invoke).name.mt, :max_args, 3, :monotonic) # invoke, f, T, args... # define applicable(f, T, args...; kwargs...), without kwargs wrapping # to forward to applicable function Core.kwcall(kwargs::NamedTuple, ::typeof(applicable), @nospecialize(args...)) @inline return applicable(Core.kwcall, kwargs, args...) end function Core._hasmethod(@nospecialize(f), @nospecialize(t)) # this function has a special tfunc (TODO: make this a Builtin instead like applicable) tt = rewrap_unionall(Tuple{Core.Typeof(f), (unwrap_unionall(t)::DataType).parameters...}, t) return Core._hasmethod(tt) end # core operations & types include("promotion.jl") include("tuple.jl") include("expr.jl") include("pair.jl") include("traits.jl") include("range.jl") include("error.jl") # core numeric operations & types ==(x, y) = x === y include("bool.jl") include("number.jl") include("int.jl") include("operators.jl") include("pointer.jl") include("refvalue.jl") include("cmem.jl") include("refpointer.jl") # now replace the Pair constructor (relevant for NamedTuples) with one that calls our Base.convert delete_method(which(Pair{Any,Any}, (Any, Any))) @eval function (P::Type{Pair{A, B}})(@nospecialize(a), @nospecialize(b)) where {A, B} @inline return $(Expr(:new, :P, :(a isa A ? a : convert(A, a)), :(b isa B ? b : convert(B, b)))) end # The REPL stdlib hooks into Base using this Ref const REPL_MODULE_REF = Ref{Module}() include("checked.jl") using .Checked function cld end function fld end # Lazy strings include("strings/lazy.jl") # array structures include("indices.jl") include("array.jl") include("abstractarray.jl") include("subarray.jl") include("views.jl") include("baseext.jl") include("ntuple.jl") include("abstractdict.jl") include("iddict.jl") include("idset.jl") include("iterators.jl") using .Iterators: zip, enumerate, only using .Iterators: Flatten, Filter, product # for generators using .Iterators: Stateful # compat (was formerly used in reinterpretarray.jl) include("namedtuple.jl") # For OS specific stuff # We need to strcat things here, before strings are really defined function strcat(x::String, y::String) out = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), Core.sizeof(x) + Core.sizeof(y)) GC.@preserve x y out begin out_ptr = unsafe_convert(Ptr{UInt8}, out) unsafe_copyto!(out_ptr, unsafe_convert(Ptr{UInt8}, x), Core.sizeof(x)) unsafe_copyto!(out_ptr + Core.sizeof(x), unsafe_convert(Ptr{UInt8}, y), Core.sizeof(y)) end return out end include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "build_h.jl")) # include($BUILDROOT/base/build_h.jl) include(strcat((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include($BUILDROOT/base/version_git.jl) # numeric operations include("hashing.jl") include("rounding.jl") using .Rounding include("div.jl") include("float.jl") include("twiceprecision.jl") include("complex.jl") include("rational.jl") include("multinverses.jl") using .MultiplicativeInverses include("abstractarraymath.jl") include("arraymath.jl") include("slicearray.jl") # SIMD loops sizeof(s::String) = Core.sizeof(s) # needed by gensym as called from simdloop include("simdloop.jl") using .SimdLoop # map-reduce operators include("reduce.jl") ## core structures include("reshapedarray.jl") include("reinterpretarray.jl") include("bitarray.jl") include("bitset.jl") if !isdefined(Core, :Compiler) include("docs/core.jl") Core.atdoc!(CoreDocs.docm) end include("multimedia.jl") using .Multimedia # Some type include("some.jl") include("dict.jl") include("abstractset.jl") include("set.jl") # Strings include("char.jl") include("strings/basic.jl") include("strings/string.jl") include("strings/substring.jl") # Initialize DL_LOAD_PATH as early as possible. We are defining things here in # a slightly more verbose fashion than usual, because we're running so early. const DL_LOAD_PATH = String[] let os = ccall(:jl_get_UNAME, Any, ()) if os === :Darwin || os === :Apple if Base.DARWIN_FRAMEWORK push!(DL_LOAD_PATH, "@loader_path/Frameworks") end push!(DL_LOAD_PATH, "@loader_path") end end include("osutils.jl") include("c.jl") # Core I/O include("io.jl") include("iobuffer.jl") # strings & printing include("intfuncs.jl") include("strings/strings.jl") include("regex.jl") include("parse.jl") include("shell.jl") include("show.jl") include("arrayshow.jl") include("methodshow.jl") # multidimensional arrays include("cartesian.jl") using .Cartesian include("multidimensional.jl") include("broadcast.jl") using .Broadcast using .Broadcast: broadcasted, broadcasted_kwsyntax, materialize, materialize!, broadcast_preserving_zero_d, andand, oror # missing values include("missing.jl") # version include("version.jl") # system & environment include("sysinfo.jl") include("libc.jl") using .Libc: getpid, gethostname, time, memcpy, memset, memmove, memcmp # These used to be in build_h.jl and are retained for backwards compatibility. # NOTE: keep in sync with `libblastrampoline_jll.libblastrampoline`. const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") const liblapack_name = libblas_name # Logging include("logging.jl") using .CoreLogging # Concurrency include("linked_list.jl") include("condition.jl") include("threads.jl") include("lock.jl") include("channels.jl") include("partr.jl") include("task.jl") include("threads_overloads.jl") include("weakkeydict.jl") include("env.jl") # functions defined in Random function rand end function randn end # I/O include("libuv.jl") include("asyncevent.jl") include("iostream.jl") include("stream.jl") include("filesystem.jl") using .Filesystem include("cmd.jl") include("process.jl") include("ttyhascolor.jl") include("secretbuffer.jl") # core math functions include("floatfuncs.jl") include("math.jl") using .Math const (โˆš)=sqrt const (โˆ›)=cbrt const (โˆœ)=fourthroot # now switch to a simple, race-y TLS, relative include for the rest of Base delete_method(which(include, (Module, String))) let SOURCE_PATH = "" global function include(mod::Module, path::String) prev = SOURCE_PATH::String path = normpath(joinpath(dirname(prev), path)) Core.println(path) ccall(:jl_uv_flush, Nothing, (Ptr{Nothing},), Core.io_pointer(Core.stdout)) push!(_included_files, (mod, abspath(path))) SOURCE_PATH = path result = Core.include(mod, path) SOURCE_PATH = prev return result end end # reduction along dims include("reducedim.jl") # macros in this file rely on string.jl include("accumulate.jl") include("permuteddimsarray.jl") using .PermutedDimsArrays # basic data structures include("ordering.jl") using .Order # Combinatorics include("sort.jl") using .Sort # BinaryPlatforms, used by Artifacts. Needs `Sort`. include("binaryplatforms.jl") # Fast math include("fastmath.jl") using .FastMath function deepcopy_internal end # enums include("Enums.jl") using .Enums # BigInts include("gmp.jl") using .GMP # float printing: requires BigInt include("ryu/Ryu.jl") using .Ryu # BigFloats include("mpfr.jl") using .MPFR include("combinatorics.jl") # irrational mathematical constants include("irrationals.jl") include("mathconstants.jl") using .MathConstants: โ„ฏ, ฯ€, pi # metaprogramming include("meta.jl") # Stack frames and traces include("stacktraces.jl") using .StackTraces # experimental API's include("experimental.jl") # utilities include("deepcopy.jl") include("download.jl") include("summarysize.jl") include("errorshow.jl") include("initdefs.jl") # worker threads include("threadcall.jl") # code loading include("uuid.jl") include("pkgid.jl") include("toml_parser.jl") include("linking.jl") include("loading.jl") # misc useful functions & macros include("timing.jl") include("util.jl") include("client.jl") include("asyncmap.jl") # deprecated functions include("deprecated.jl") # # Some additional basic documentation include("docs/basedocs.jl") # Documentation -- should always be included last in sysimg. include("docs/Docs.jl") using .Docs if isdefined(Core, :Compiler) && is_primary_base_module Docs.loaddocs(Core.Compiler.CoreDocs.DOCS) end # finally, now make `include` point to the full version for m in methods(include) delete_method(m) end # This method is here only to be overwritten during the test suite to test # various sysimg related invalidation scenarios. a_method_to_overwrite_in_test() = inferencebarrier(1) # These functions are duplicated in client.jl/include(::String) for # nicer stacktraces. Modifications here have to be backported there include(mod::Module, _path::AbstractString) = _include(identity, mod, _path) include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexpr, mod, _path) # External libraries vendored into Base Core.println("JuliaSyntax/src/JuliaSyntax.jl") include(@__MODULE__, "JuliaSyntax/src/JuliaSyntax.jl") end_base_include = time_ns() const _sysimage_modules = PkgId[] in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules # Precompiles for Revise and other packages # TODO: move these to contrib/generate_precompile.jl # The problem is they don't work there for match = _methods(+, (Int, Int), -1, get_world_counter()) m = match.method delete!(push!(Set{Method}(), m), m) copy(Core.Compiler.retrieve_code_info(Core.Compiler.specialize_method(match), typemax(UInt))) empty!(Set()) push!(push!(Set{Union{GlobalRef,Symbol}}(), :two), GlobalRef(Base, :two)) (setindex!(Dict{String,Base.PkgId}(), Base.PkgId(Base), "file.jl"))["file.jl"] (setindex!(Dict{Symbol,Vector{Int}}(), [1], :two))[:two] (setindex!(Dict{Base.PkgId,String}(), "file.jl", Base.PkgId(Base)))[Base.PkgId(Base)] (setindex!(Dict{Union{GlobalRef,Symbol}, Vector{Int}}(), [1], :two))[:two] (setindex!(IdDict{Type, Union{Missing, Vector{Tuple{LineNumberNode, Expr}}}}(), missing, Int))[Int] Dict{Symbol, Union{Nothing, Bool, Symbol}}(:one => false)[:one] Dict(Base => [:(1+1)])[Base] Dict(:one => [1])[:one] Dict("abc" => Set())["abc"] pushfirst!([], sum) get(Base.pkgorigins, Base.PkgId(Base), nothing) sort!([1,2,3]) unique!([1,2,3]) cumsum([1,2,3]) append!(Int[], BitSet()) isempty(BitSet()) delete!(BitSet([1,2]), 3) deleteat!(Int32[1,2,3], [1,3]) deleteat!(Any[1,2,3], [1,3]) Core.svec(1, 2) == Core.svec(3, 4) any(t->t[1].line > 1, [(LineNumberNode(2,:none), :(1+1))]) # Code loading uses this sortperm(mtime.(readdir(".")), rev=true) # JLLWrappers uses these Dict{UUID,Set{String}}()[UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}() get!(Set{String}, Dict{UUID,Set{String}}(), UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")) eachindex(IndexLinear(), Expr[]) push!(Expr[], Expr(:return, false)) vcat(String[], String[]) k, v = (:hello => nothing) precompile(indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int)) precompile(indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int, Int)) # Preferences uses these precompile(get_preferences, (UUID,)) precompile(record_compiletime_preference, (UUID, String)) get(Dict{String,Any}(), "missing", nothing) delete!(Dict{String,Any}(), "missing") for (k, v) in Dict{String,Any}() println(k) end break # only actually need to do this once end if is_primary_base_module # Profiling helper # triggers printing the report and (optionally) saving a heap snapshot after a SIGINFO/SIGUSR1 profile request # Needs to be in Base because Profile is no longer loaded on boot const PROFILE_PRINT_COND = Ref{Base.AsyncCondition}() function profile_printing_listener() profile = nothing try while true wait(PROFILE_PRINT_COND[]) profile = @something(profile, require(PkgId(UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"), "Profile"))) invokelatest(profile.peek_report[]) if Base.get_bool_env("JULIA_PROFILE_PEEK_HEAP_SNAPSHOT", false) === true println(stderr, "Saving heap snapshot...") fname = invokelatest(profile.take_heap_snapshot) println(stderr, "Heap snapshot saved to `$(fname)`") end end catch ex if !isa(ex, InterruptException) @error "Profile printing listener crashed" exception=ex,catch_backtrace() end end end function __init__() # Base library init reinit_stdio() Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback # initialize loading init_depot_path() init_load_path() init_active_project() append!(empty!(_sysimage_modules), keys(loaded_modules)) if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES") MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"]) end # Profiling helper @static if !Sys.iswindows() # triggering a profile via signals is not implemented on windows cond = Base.AsyncCondition() Base.uv_unref(cond.handle) PROFILE_PRINT_COND[] = cond ccall(:jl_set_peek_cond, Cvoid, (Ptr{Cvoid},), PROFILE_PRINT_COND[].handle) errormonitor(Threads.@spawn(profile_printing_listener())) end _require_world_age[] = get_world_counter() # Prevent spawned Julia process from getting stuck waiting on Tracy to connect. delete!(ENV, "JULIA_WAIT_FOR_TRACY") if get_bool_env("JULIA_USE_FLISP_PARSER", false) === false JuliaSyntax.enable_in_core!() end nothing end # enable threads support @eval PCRE PCRE_COMPILE_LOCK = Threads.SpinLock() end # Ensure this file is also tracked @assert !isassigned(_included_files, 1) _included_files[1] = (parentmodule(Base), abspath(@__FILE__)) end # baremodule Base M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/coreio.jl-# This file is a part of Julia. License is MIT: https://julialang.org/license print(xs...) = print(stdout, xs...) println(xs...) = println(stdout, xs...) println(io::IO) = print(io, '\n') function show end function repr end struct DevNull <: IO end const devnull = DevNull() write(::DevNull, ::UInt8) = 1 unsafe_write(::DevNull, ::Ptr{UInt8}, n::UInt)::Int = n close(::DevNull) = nothing wait_close(::DevNull) = wait() bytesavailable(io::DevNull) = 0 let CoreIO = Union{Core.CoreSTDOUT, Core.CoreSTDERR} global write(io::CoreIO, x::UInt8) = Core.write(io, x) global unsafe_write(io::CoreIO, x::Ptr{UInt8}, nb::UInt) = Core.unsafe_write(io, x, nb) CoreIO = Union{CoreIO, DevNull} global read(::CoreIO, ::Type{UInt8}) = throw(EOFError()) global isopen(::CoreIO) = true global isreadable(::CoreIO) = false global iswritable(::CoreIO) = true global flush(::CoreIO) = nothing global eof(::CoreIO) = true global wait_readnb(::CoreIO, nb::Int) = nothing end stdin::IO = devnull stdout::IO = Core.stdout stderr::IO = Core.stderr N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/exports.jlx7# This file is a part of Julia. License is MIT: https://julialang.org/license export # Modules Meta, StackTraces, Sys, Libc, Docs, Threads, Iterators, Broadcast, MathConstants, # Types AbstractChannel, AbstractIrrational, AbstractMatrix, AbstractRange, AbstractSet, AbstractSlices, AbstractUnitRange, AbstractVector, AbstractVecOrMat, Array, AbstractMatch, AbstractPattern, AbstractDict, BigFloat, BigInt, BitArray, BitMatrix, BitVector, CartesianIndex, CartesianIndices, LinearIndices, Channel, Cmd, Colon, Complex, ComplexF64, ComplexF32, ComplexF16, ComposedFunction, ColumnSlices, DenseMatrix, DenseVecOrMat, DenseVector, Dict, Dims, Enum, ExponentialBackOff, IndexCartesian, IndexLinear, IndexStyle, InsertionSort, BitSet, IOBuffer, IOStream, LinRange, Irrational, LazyString, Matrix, MergeSort, Missing, NTuple, IdDict, OrdinalRange, Pair, PartialQuickSort, PermutedDimsArray, QuickSort, Rational, Regex, RegexMatch, Returns, RoundFromZero, RoundDown, RoundingMode, RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, RoundToZero, RoundUp, RowSlices, Set, Some, Slices, StepRange, StepRangeLen, StridedArray, StridedMatrix, StridedVecOrMat, StridedVector, SubArray, SubString, SubstitutionString, Timer, UnitRange, Val, VecOrMat, Vector, VersionNumber, WeakKeyDict, # Ccall types Cchar, Cdouble, Cfloat, Cint, Cintmax_t, Clong, Clonglong, Cptrdiff_t, Cshort, Csize_t, Cssize_t, Cuchar, Cuint, Cuintmax_t, Culong, Culonglong, Cushort, Cwchar_t, Cstring, Cwstring, # Exceptions CanonicalIndexError, CapturedException, CompositeException, DimensionMismatch, EOFError, InvalidStateException, KeyError, MissingException, ProcessFailedException, TaskFailedException, SystemError, StringIndexError, # Global constants and variables ARGS, C_NULL, DEPOT_PATH, ENDIAN_BOM, ENV, LOAD_PATH, PROGRAM_FILE, stderr, stdin, stdout, VERSION, devnull, # Mathematical constants Inf, Inf16, Inf32, Inf64, NaN, NaN16, NaN32, NaN64, im, ฯ€, pi, โ„ฏ, # Operators !, !=, โ‰ , !==, โ‰ก, โ‰ข, xor, โŠป, nand, nor, โŠผ, โŠฝ, %, รท, &, *, +, -, /, //, <, <:, <<, <=, โ‰ค, ==, >, >:, >=, โ‰ฅ, >>, >>>, \, ^, |, |>, ~, :, =>, โˆ˜, # scalar math @evalpoly, evalpoly, abs, abs2, acos, acosd, acosh, acot, acotd, acoth, acsc, acscd, acsch, angle, asec, asecd, asech, asin, asind, asinh, atan, atand, atanh, big, binomial, bitreverse, bitrotate, bswap, cbrt, fourthroot, ceil, cis, cispi, clamp, cld, cmp, complex, conj, copysign, cos, cosc, cosd, cosh, cospi, cot, cotd, coth, count_ones, count_zeros, csc, cscd, csch, deg2rad, denominator, div, divrem, eps, exp, exp10, exp2, expm1, exponent, factorial, fld, fld1, fldmod, fldmod1, flipsign, float, tryparse, floor, fma, frexp, gcd, gcdx, hypot, imag, inv, invmod, isapprox, iseven, isfinite, isinf, isinteger, isnan, isodd, ispow2, isqrt, isreal, issubnormal, iszero, isone, lcm, ldexp, leading_ones, leading_zeros, log, log10, log1p, log2, maxintfloat, mod, mod1, modf, mod2pi, muladd, nextfloat, nextpow, nextprod, numerator, one, oneunit, powermod, prevfloat, prevpow, rad2deg, rationalize, real, floatmax, floatmin, reim, reinterpret, rem, rem2pi, round, sec, secd, sech, sign, signbit, signed, significand, sin, sinc, sincos, sincosd, sincospi, sind, sinh, sinpi, sqrt, tan, tand, tanh, tanpi, trailing_ones, trailing_zeros, trunc, unsafe_trunc, typemax, typemin, unsigned, widemul, zero, โˆš, โˆ›, โˆœ, โ‰ˆ, โ‰‰, # arrays axes, broadcast!, broadcast, cat, checkbounds, checkindex, circcopy!, circshift, circshift!, clamp!, conj!, copy!, copyto!, diff, cumprod, cumprod!, cumsum, cumsum!, accumulate, accumulate!, eachcol, eachindex, eachrow, eachslice, extrema!, extrema, fill!, fill, first, hcat, hvcat, hvncat, indexin, argmax, argmin, invperm, invpermute!, isassigned, isperm, issorted, last, mapslices, max, maximum!, maximum, min, minimum!, minimum, minmax, ndims, ones, parent, parentindices, partialsort, partialsort!, partialsortperm, partialsortperm!, permute!, permutedims, permutedims!, prod!, prod, promote_shape, range, reshape, reverse!, reverse, rot180, rotl90, rotr90, size, selectdim, sort!, sort, sortperm, sortperm!, sortslices, dropdims, stack, step, stride, strides, sum!, sum, to_indices, vcat, vec, view, zeros, # search, find, match and related functions contains, eachmatch, endswith, findall, findfirst, findlast, findmax, findmin, findmin!, findmax!, findnext, findprev, match, occursin, searchsorted, searchsortedfirst, searchsortedlast, insorted, startswith, # linear algebra var"'", # to enable syntax a' for adjoint adjoint, transpose, kron, kron!, # bitarrays falses, trues, # dequeues append!, insert!, pop!, popat!, prepend!, push!, resize!, popfirst!, pushfirst!, # collections all!, all, allequal, allunique, any!, any, firstindex, collect, count!, count, delete!, deleteat!, keepat!, eltype, empty!, empty, lastindex, filter!, filter, foldl, foldr, foreach, get, get!, getindex, getkey, haskey, in, intersect!, intersect, isdisjoint, isempty, issubset, issetequal, keys, keytype, length, map!, map, mapfoldl, mapfoldr, mapreduce, merge!, mergewith!, merge, mergewith, pairs, reduce, setdiff!, setdiff, setindex!, similar, sizehint!, splice!, symdiff!, symdiff, union!, union, unique!, unique, values, valtype, โˆˆ, โˆ‰, โˆ‹, โˆŒ, โІ, โŠˆ, โŠŠ, โЇ, โЉ, โŠ‹, โˆฉ, โˆช, # strings ascii, bitstring, bytes2hex, chomp, chop, chopprefix, chopsuffix, codepoint, codeunit, codeunits, digits, digits!, eachsplit, escape_string, hex2bytes, hex2bytes!, isascii, iscntrl, isdigit, isletter, islowercase, isnumeric, isprint, ispunct, isspace, isuppercase, isxdigit, lowercase, lowercasefirst, isvalid, join, lpad, lstrip, ncodeunits, ndigits, nextind, prevind, repeat, replace, replace!, repr, reverseind, rpad, rsplit, rstrip, split, string, strip, textwidth, thisind, titlecase, transcode, unescape_string, uppercase, uppercasefirst, # text output IOContext, displaysize, dump, print, println, printstyled, show, showerror, sprint, summary, # logging @debug, @info, @warn, @error, # bigfloat & precision precision, rounding, setprecision, setrounding, get_zero_subnormals, set_zero_subnormals, # iteration iterate, enumerate, # re-exported from Iterators zip, only, # object identity and equality copy, deepcopy, hash, identity, isbits, isequal, ismutable, ismutabletype, isless, isunordered, ifelse, objectid, sizeof, # tasks and conditions Condition, current_task, islocked, istaskdone, istaskstarted, istaskfailed, lock, @lock, notify, ReentrantLock, schedule, task_local_storage, trylock, unlock, yield, yieldto, wait, timedwait, asyncmap, asyncmap!, errormonitor, # channels take!, put!, isready, fetch, bind, # missing values coalesce, @coalesce, ismissing, missing, skipmissing, @something, something, isnothing, nonmissingtype, # time sleep, time, time_ns, # errors backtrace, catch_backtrace, current_exceptions, error, rethrow, retry, systemerror, # stack traces stacktrace, # types convert, getproperty, setproperty!, swapproperty!, modifyproperty!, replaceproperty!, fieldoffset, fieldname, fieldnames, fieldcount, fieldtypes, hasfield, propertynames, hasproperty, isabstracttype, isbitstype, isprimitivetype, isstructtype, isconcretetype, isdispatchtuple, oftype, promote, promote_rule, promote_type, instances, supertype, typeintersect, typejoin, widen, # syntax esc, gensym, @kwdef, macroexpand, @macroexpand1, @macroexpand, parse, # help and reflection code_typed, code_lowered, fullname, functionloc, isconst, isinteractive, hasmethod, methods, nameof, parentmodule, pathof, pkgdir, pkgversion, names, which, @isdefined, @invoke, invokelatest, @invokelatest, # loading source files __precompile__, evalfile, include_string, include_dependency, # RTS internals GC, finalizer, finalize, precompile, # misc atexit, atreplinit, exit, ntuple, splat, # I/O and events close, closewrite, countlines, eachline, readeach, eof, fd, fdio, flush, gethostname, htol, hton, ismarked, isopen, isreadonly, ltoh, mark, bytesavailable, ntoh, open, peek, pipeline, Pipe, PipeBuffer, position, RawFD, read, read!, readavailable, readbytes!, readchomp, readdir, readline, readlines, readuntil, redirect_stdio, redirect_stderr, redirect_stdin, redirect_stdout, reset, seek, seekend, seekstart, skip, skipchars, take!, truncate, unmark, unsafe_read, unsafe_write, write, # multimedia I/O AbstractDisplay, display, displayable, TextDisplay, istextmime, MIME, @MIME_str, popdisplay, pushdisplay, redisplay, showable, HTML, Text, # paths and file names abspath, basename, dirname, expanduser, contractuser, homedir, isabspath, isdirpath, joinpath, normpath, realpath, relpath, splitdir, splitdrive, splitext, splitpath, # filesystem operations cd, chmod, chown, cp, ctime, diskstat, download, filemode, filesize, gperm, hardlink, isblockdev, ischardev, isdir, isfifo, isfile, islink, ismount, ispath, isreadable, issetgid, issetuid, issocket, issticky, iswritable, lstat, mkdir, mkpath, mktemp, mktempdir, mtime, mv, operm, pwd, readlink, rm, samefile, stat, symlink, tempdir, tempname, touch, uperm, walkdir, # external processes detach, getpid, ignorestatus, kill, process_exited, process_running, run, setenv, addenv, setcpuaffinity, success, withenv, # C interface @cfunction, @ccall, cglobal, disable_sigint, pointer, pointer_from_objref, unsafe_wrap, unsafe_string, reenable_sigint, unsafe_copyto!, unsafe_load, unsafe_modify!, unsafe_pointer_to_objref, unsafe_replace!, unsafe_store!, unsafe_swap!, # implemented in Random module rand, randn, # Macros # parser internal @__FILE__, @__DIR__, @__LINE__, @__MODULE__, @int128_str, @uint128_str, @big_str, @cmd, # `commands` # notation for certain types @b_str, # byte vector @r_str, # regex @s_str, # regex substitution string @v_str, # version number @raw_str, # raw string with no interpolation/unescaping @NamedTuple, @Kwargs, @lazy_str, # lazy string # documentation @text_str, @html_str, @doc, # output @show, # profiling @time, @showtime, @timed, @timev, @elapsed, @allocated, @allocations, # tasks @sync, @async, @task, @threadcall, # metaprogramming utilities @generated, @gensym, @eval, @deprecate, # performance annotations @boundscheck, @inbounds, @fastmath, @simd, @inline, @noinline, @nospecialize, @specialize, @polly, @assert, @atomic, @atomicswap, @atomicreplace, @__dot__, @enum, @label, @goto, @view, @views, @static Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/essentials.jlฎ~# This file is a part of Julia. License is MIT: https://julialang.org/license import Core: CodeInfo, SimpleVector, donotdelete, compilerbarrier, arrayref const Callable = Union{Function,Type} const Bottom = Union{} # Define minimal array interface here to help code used in macros: length(a::Array) = arraylen(a) # This is more complicated than it needs to be in order to get Win64 through bootstrap eval(:(getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1))) eval(:(getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@inline; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)))) ==(a::GlobalRef, b::GlobalRef) = a.mod === b.mod && a.name === b.name """ AbstractSet{T} Supertype for set-like types whose elements are of type `T`. [`Set`](@ref), [`BitSet`](@ref) and other types are subtypes of this. """ abstract type AbstractSet{T} end """ AbstractDict{K, V} Supertype for dictionary-like types with keys of type `K` and values of type `V`. [`Dict`](@ref), [`IdDict`](@ref) and other types are subtypes of this. An `AbstractDict{K, V}` should be an iterator of `Pair{K, V}`. """ abstract type AbstractDict{K,V} end ## optional pretty printer: #const NamedTuplePair{N, V, names, T<:NTuple{N, Any}} = Pairs{Symbol, V, NTuple{N, Symbol}, NamedTuple{names, T}} #export NamedTuplePair macro _gc_preserve_begin(arg1) Expr(:gc_preserve_begin, esc(arg1)) end macro _gc_preserve_end(token) Expr(:gc_preserve_end, esc(token)) end """ @nospecialize Applied to a function argument name, hints to the compiler that the method implementation should not be specialized for different types of that argument, but instead use the declared type for that argument. It can be applied to an argument within a formal argument list, or in the function body. When applied to an argument, the macro must wrap the entire argument expression, e.g., `@nospecialize(x::Real)` or `@nospecialize(i::Integer...)` rather than wrapping just the argument name. When used in a function body, the macro must occur in statement position and before any code. When used without arguments, it applies to all arguments of the parent scope. In local scope, this means all arguments of the containing function. In global (top-level) scope, this means all methods subsequently defined in the current module. Specialization can reset back to the default by using [`@specialize`](@ref). ```julia function example_function(@nospecialize x) ... end function example_function(x, @nospecialize(y = 1)) ... end function example_function(x, y, z) @nospecialize x y ... end @nospecialize f(y) = [x for x in y] @specialize ``` !!! note `@nospecialize` affects code generation but not inference: it limits the diversity of the resulting native code, but it does not impose any limitations (beyond the standard ones) on type-inference. Use [`Base.@nospecializeinfer`](@ref) together with `@nospecialize` to additionally suppress inference. # Example ```julia julia> f(A::AbstractArray) = g(A) f (generic function with 1 method) julia> @noinline g(@nospecialize(A::AbstractArray)) = A[1] g (generic function with 1 method) julia> @code_typed f([1.0]) CodeInfo( 1 โ”€ %1 = invoke Main.g(_2::AbstractArray)::Float64 โ””โ”€โ”€ return %1 ) => Float64 ``` Here, the `@nospecialize` annotation results in the equivalent of ```julia f(A::AbstractArray) = invoke(g, Tuple{AbstractArray}, A) ``` ensuring that only one version of native code will be generated for `g`, one that is generic for any `AbstractArray`. However, the specific return type is still inferred for both `g` and `f`, and this is still used in optimizing the callers of `f` and `g`. """ macro nospecialize(vars...) if nfields(vars) === 1 # in argument position, need to fix `@nospecialize x=v` to `@nospecialize (kw x v)` var = getfield(vars, 1) if isa(var, Expr) && var.head === :(=) var.head = :kw end end return Expr(:meta, :nospecialize, vars...) end """ @specialize Reset the specialization hint for an argument back to the default. For details, see [`@nospecialize`](@ref). """ macro specialize(vars...) if nfields(vars) === 1 # in argument position, need to fix `@specialize x=v` to `@specialize (kw x v)` var = getfield(vars, 1) if isa(var, Expr) && var.head === :(=) var.head = :kw end end return Expr(:meta, :specialize, vars...) end """ @isdefined s -> Bool Tests whether variable `s` is defined in the current scope. See also [`isdefined`](@ref) for field properties and [`isassigned`](@ref) for array indexes or [`haskey`](@ref) for other mappings. # Examples ```jldoctest julia> @isdefined newvar false julia> newvar = 1 1 julia> @isdefined newvar true julia> function f() println(@isdefined x) x = 3 println(@isdefined x) end f (generic function with 1 method) julia> f() false true ``` """ macro isdefined(s::Symbol) return Expr(:escape, Expr(:isdefined, s)) end """ nameof(m::Module) -> Symbol Get the name of a `Module` as a [`Symbol`](@ref). # Examples ```jldoctest julia> nameof(Base.Broadcast) :Broadcast ``` """ nameof(m::Module) = ccall(:jl_module_name, Ref{Symbol}, (Any,), m) function _is_internal(__module__) if ccall(:jl_base_relative_to, Any, (Any,), __module__)::Module === Core.Compiler || nameof(__module__) === :Base return true end return false end # can be used in place of `@assume_effects :total` (supposed to be used for bootstrapping) macro _total_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, #=:consistent=#true, #=:effect_free=#true, #=:nothrow=#true, #=:terminates_globally=#true, #=:terminates_locally=#false, #=:notaskstate=#true, #=:inaccessiblememonly=#true)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, #=:consistent=#true, #=:effect_free=#true, #=:nothrow=#false, #=:terminates_globally=#true, #=:terminates_locally=#false, #=:notaskstate=#false, #=:inaccessiblememonly=#true)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, #=:consistent=#false, #=:effect_free=#false, #=:nothrow=#true, #=:terminates_globally=#false, #=:terminates_locally=#false, #=:notaskstate=#false, #=:inaccessiblememonly=#false)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, #=:consistent=#false, #=:effect_free=#false, #=:nothrow=#false, #=:terminates_globally=#false, #=:terminates_locally=#true, #=:notaskstate=#false, #=:inaccessiblememonly=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() return _is_internal(__module__) && Expr(:meta, Expr(:purity, #=:consistent=#false, #=:effect_free=#true, #=:nothrow=#false, #=:terminates_globally=#false, #=:terminates_locally=#true, #=:notaskstate=#false, #=:inaccessiblememonly=#false)) end # another version of inlining that propagates an inbounds context macro _propagate_inbounds_meta() return Expr(:meta, :inline, :propagate_inbounds) end function iterate end """ convert(T, x) Convert `x` to a value of type `T`. If `T` is an [`Integer`](@ref) type, an [`InexactError`](@ref) will be raised if `x` is not representable by `T`, for example if `x` is not integer-valued, or is outside the range supported by `T`. # Examples ```jldoctest julia> convert(Int, 3.0) 3 julia> convert(Int, 3.5) ERROR: InexactError: Int64(3.5) Stacktrace: [...] ``` If `T` is a [`AbstractFloat`](@ref) type, then it will return the closest value to `x` representable by `T`. ```jldoctest julia> x = 1/3 0.3333333333333333 julia> convert(Float32, x) 0.33333334f0 julia> convert(BigFloat, x) 0.333333333333333314829616256247390992939472198486328125 ``` If `T` is a collection type and `x` a collection, the result of `convert(T, x)` may alias all or part of `x`. ```jldoctest julia> x = Int[1, 2, 3]; julia> y = convert(Vector{Int}, x); julia> y === x true ``` See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@ref). """ function convert end # ensure this is never ambiguous, and therefore fast for lookup convert(T::Type{Union{}}, x...) = throw(ArgumentError("cannot convert a value to Union{} for assignment")) convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled # (due to fields typed as `Type`, which is generally a bad idea) # These end up being called during bootstrap and then would be invalidated if not for the following: convert(::Type{String}, x::String) = x """ @eval [mod,] ex Evaluate an expression with values interpolated into it using `eval`. If two arguments are provided, the first is the module to evaluate in. """ macro eval(ex) return Expr(:escape, Expr(:call, GlobalRef(Core, :eval), __module__, Expr(:quote, ex))) end macro eval(mod, ex) return Expr(:escape, Expr(:call, GlobalRef(Core, :eval), mod, Expr(:quote, ex))) end # use `@eval` here to directly form `:new` expressions avoid implicit `convert`s # in order to achieve better effects inference @eval struct Pairs{K, V, I, A} <: AbstractDict{K, V} data::A itr::I Pairs{K, V, I, A}(data, itr) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :(data isa A ? data : convert(A, data)), :(itr isa I ? itr : convert(I, itr)))) Pairs{K, V}(data::A, itr::I) where {K, V, I, A} = $(Expr(:new, :(Pairs{K, V, I, A}), :data, :itr)) Pairs{K}(data::A, itr::I) where {K, I, A} = $(Expr(:new, :(Pairs{K, eltype(A), I, A}), :data, :itr)) Pairs(data::A, itr::I) where {I, A} = $(Expr(:new, :(Pairs{eltype(I), eltype(A), I, A}), :data, :itr)) end pairs(::Type{NamedTuple}) = Pairs{Symbol, V, NTuple{N, Symbol}, NamedTuple{names, T}} where {V, N, names, T<:NTuple{N, Any}} """ Iterators.Pairs(values, keys) <: AbstractDict{eltype(keys), eltype(values)} Transforms an indexable container into a Dictionary-view of the same data. Modifying the key-space of the underlying data may invalidate this object. """ Pairs argtail(x, rest...) = rest """ tail(x::Tuple)::Tuple Return a `Tuple` consisting of all but the first component of `x`. See also: [`front`](@ref Base.front), [`rest`](@ref Base.rest), [`first`](@ref), [`Iterators.peel`](@ref). # Examples ```jldoctest julia> Base.tail((1,2,3)) (2, 3) julia> Base.tail(()) ERROR: ArgumentError: Cannot call tail on an empty tuple. ``` """ tail(x::Tuple) = argtail(x...) tail(::Tuple{}) = throw(ArgumentError("Cannot call tail on an empty tuple.")) function unwrap_unionall(@nospecialize(a)) while isa(a,UnionAll) a = a.body end return a end function rewrap_unionall(@nospecialize(t), @nospecialize(u)) if !isa(u, UnionAll) return t end return UnionAll(u.var, rewrap_unionall(t, u.body)) end function rewrap_unionall(t::Core.TypeofVararg, @nospecialize(u)) isdefined(t, :T) || return t if !isa(u, UnionAll) return t end T = rewrap_unionall(t.T, u) if !isdefined(t, :N) || t.N === u.var return Vararg{T} end return Vararg{T, t.N} end # replace TypeVars in all enclosing UnionAlls with fresh TypeVars function rename_unionall(@nospecialize(u)) if !isa(u, UnionAll) return u end var = u.var::TypeVar body = UnionAll(var, rename_unionall(u.body)) nv = TypeVar(var.name, var.lb, var.ub) return UnionAll(nv, body{nv}) end # remove concrete constraint on diagonal TypeVar if it comes from troot function widen_diagonal(@nospecialize(t), troot::UnionAll) body = ccall(:jl_widen_diagonal, Any, (Any, Any), t, troot) end function isvarargtype(@nospecialize(t)) return isa(t, Core.TypeofVararg) end function isvatuple(@nospecialize(t)) t = unwrap_unionall(t) if isa(t, DataType) n = length(t.parameters) return n > 0 && isvarargtype(t.parameters[n]) end return false end function unwrapva(@nospecialize(t)) isa(t, Core.TypeofVararg) || return t return isdefined(t, :T) ? t.T : Any end function unconstrain_vararg_length(va::Core.TypeofVararg) # construct a new Vararg type where its length is unconstrained, # but its element type still captures any dependencies the input # element type may have had on the input length return Vararg{unwrapva(va)} end typename(a) = error("typename does not apply to this type") typename(a::DataType) = a.name function typename(a::Union) ta = typename(a.a) tb = typename(a.b) ta === tb || error("typename does not apply to unions whose components have different typenames") return tb end typename(union::UnionAll) = typename(union.body) _tuple_error(T::Type, x) = (@noinline; throw(MethodError(convert, (T, x)))) convert(::Type{T}, x::T) where {T<:Tuple} = x function convert(::Type{T}, x::NTuple{N,Any}) where {N, T<:Tuple} # First see if there could be any conversion of the input type that'd be a subtype of the output. # If not, we'll throw an explicit MethodError (otherwise, it might throw a typeassert). if typeintersect(NTuple{N,Any}, T) === Union{} _tuple_error(T, x) end function cvt1(n) @inline Tn = fieldtype(T, n) xn = getfield(x, n, #=boundscheck=#false) xn isa Tn && return xn return convert(Tn, xn) end return ntuple(cvt1, Val(N))::NTuple{N,Any} end # optimizations? # converting to tuple types of fixed length #convert(::Type{T}, x::T) where {N, T<:NTuple{N,Any}} = x #convert(::Type{T}, x::NTuple{N,Any}) where {N, T<:NTuple{N,Any}} = # ntuple(n -> convert(fieldtype(T, n), x[n]), Val(N)) #convert(::Type{T}, x::Tuple{Vararg{Any}}) where {N, T<:NTuple{N,Any}} = # throw(MethodError(convert, (T, x))) # converting to tuple types of indefinite length #convert(::Type{Tuple{Vararg{V}}}, x::Tuple{Vararg{V}}) where {V} = x #convert(::Type{NTuple{N, V}}, x::NTuple{N, V}) where {N, V} = x #function convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} # @isdefined(V) || (V = fieldtype(T, 1)) # return map(t -> convert(V, t), x) #end #function convert(T::Type{NTuple{N, V}}, x::NTuple{N, Any}) where {N, V} # @isdefined(V) || (V = fieldtype(T, 1)) # return map(t -> convert(V, t), x) #end # short tuples #convert(::Type{Tuple{}}, ::Tuple{}) = () #convert(::Type{Tuple{S}}, x::Tuple{S}) where {S} = x #convert(::Type{Tuple{S, T}}, x::Tuple{S, T}) where {S, T} = x #convert(::Type{Tuple{S, T, U}}, x::Tuple{S, T, U}) where {S, T, U} = x #convert(::Type{Tuple{S}}, x::Tuple{Any}) where {S} = (convert(S, x[1]),) #convert(::Type{Tuple{S, T}}, x::Tuple{Any, Any}) where {S, T} = (convert(S, x[1]), convert(T, x[2]),) #convert(::Type{Tuple{S, T, U}}, x::Tuple{Any, Any, Any}) where {S, T, U} = (convert(S, x[1]), convert(T, x[2]), convert(U, x[3])) #convert(::Type{Tuple{}}, x::Tuple) = _tuple_error(Tuple{}, x) #convert(::Type{Tuple{S}}, x::Tuple) = _tuple_error(Tuple{S}, x) #convert(::Type{Tuple{S, T}}, x::Tuple{Any, Any}) where {S, T} =_tuple_error(Tuple{S, T}, x) #convert(::Type{Tuple{S, T, U}}, x::Tuple{Any, Any, Any}) where {S, T, U} = _tuple_error(Tuple{S, T, U}, x) """ oftype(x, y) Convert `y` to the type of `x` i.e. `convert(typeof(x), y)`. # Examples ```jldoctest julia> x = 4; julia> y = 3.; julia> oftype(x, y) 3 julia> oftype(y, x) 4.0 ``` """ oftype(x, y) = y isa typeof(x) ? y : convert(typeof(x), y)::typeof(x) unsigned(x::Int) = reinterpret(UInt, x) signed(x::UInt) = reinterpret(Int, x) """ cconvert(T,x) Convert `x` to a value to be passed to C code as type `T`, typically by calling `convert(T, x)`. In cases where `x` cannot be safely converted to `T`, unlike [`convert`](@ref), `cconvert` may return an object of a type different from `T`, which however is suitable for [`unsafe_convert`](@ref) to handle. The result of this function should be kept valid (for the GC) until the result of [`unsafe_convert`](@ref) is not needed anymore. This can be used to allocate memory that will be accessed by the `ccall`. If multiple objects need to be allocated, a tuple of the objects can be used as return value. Neither `convert` nor `cconvert` should take a Julia object and turn it into a `Ptr`. """ function cconvert end cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases cconvert(::Type{Union{}}, x...) = convert(Union{}, x...) cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method unsafe_convert(::Type{P}, x::Ptr) where {P<:Ptr} = convert(P, x) """ reinterpret(::Type{Out}, x::In) Change the type-interpretation of the binary data in the isbits value `x` to that of the isbits type `Out`. The size (ignoring padding) of `Out` has to be the same as that of the type of `x`. For example, `reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a [`Float32`](@ref). ```jldoctest julia> reinterpret(Float32, UInt32(7)) 1.0f-44 julia> reinterpret(NTuple{2, UInt8}, 0x1234) (0x34, 0x12) julia> reinterpret(UInt16, (0x34, 0x12)) 0x1234 julia> reinterpret(Tuple{UInt16, UInt8}, (0x01, 0x0203)) (0x0301, 0x02) ``` !!! warning Use caution if some combinations of bits in `Out` are not considered valid and would otherwise be prevented by the type's constructors and methods. Unexpected behavior may result without additional validation. """ function reinterpret(::Type{Out}, x) where {Out} if isprimitivetype(Out) && isprimitivetype(typeof(x)) return bitcast(Out, x) end # only available when Base is fully loaded. return _reinterpret(Out, x) end """ sizeof(T::DataType) sizeof(obj) Size, in bytes, of the canonical binary representation of the given `DataType` `T`, if any. Or the size, in bytes, of object `obj` if it is not a `DataType`. See also [`Base.summarysize`](@ref). # Examples ```jldoctest julia> sizeof(Float32) 4 julia> sizeof(ComplexF64) 16 julia> sizeof(1.0) 8 julia> sizeof(collect(1.0:10.0)) 80 julia> struct StructWithPadding x::Int64 flag::Bool end julia> sizeof(StructWithPadding) # not the sum of `sizeof` of fields due to padding 16 julia> sizeof(Int64) + sizeof(Bool) # different from above 9 ``` If `DataType` `T` does not have a specific size, an error is thrown. ```jldoctest julia> sizeof(AbstractArray) ERROR: Abstract type AbstractArray does not have a definite size. Stacktrace: [...] ``` """ sizeof(x) = Core.sizeof(x) """ ifelse(condition::Bool, x, y) Return `x` if `condition` is `true`, otherwise return `y`. This differs from `?` or `if` in that it is an ordinary function, so all the arguments are evaluated first. In some cases, using `ifelse` instead of an `if` statement can eliminate the branch in generated code and provide higher performance in tight loops. # Examples ```jldoctest julia> ifelse(1 > 2, 1, 2) 2 ``` """ ifelse(condition::Bool, x, y) = Core.ifelse(condition, x, y) # simple Array{Any} operations needed for bootstrap @eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i) """ esc(e) Only valid in the context of an [`Expr`](@ref) returned from a macro. Prevents the macro hygiene pass from turning embedded variables into gensym variables. See the [Macros](@ref man-macros) section of the Metaprogramming chapter of the manual for more details and examples. """ esc(@nospecialize(e)) = Expr(:escape, e) """ @boundscheck(blk) Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref). !!! note The function in which `@boundscheck` is written must be inlined into its caller in order for `@inbounds` to have effect. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> @inline function g(A, i) @boundscheck checkbounds(A, i) return "accessing (\$A)[\$i]" end; julia> f1() = return g(1:2, -1); julia> f2() = @inbounds return g(1:2, -1); julia> f1() ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] Stacktrace: [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 [2] checkbounds at ./abstractarray.jl:420 [inlined] [3] g at ./none:2 [inlined] [4] f1() at ./none:1 [5] top-level scope julia> f2() "accessing (1:2)[-1]" ``` !!! warning The `@boundscheck` annotation allows you, as a library writer, to opt-in to allowing *other code* to remove your bounds checks with [`@inbounds`](@ref). As noted there, the caller must verifyโ€”using information they can accessโ€”that their accesses are valid before using `@inbounds`. For indexing into your [`AbstractArray`](@ref) subclasses, for example, this involves checking the indices against its [`axes`](@ref). Therefore, `@boundscheck` annotations should only be added to a [`getindex`](@ref) or [`setindex!`](@ref) implementation after you are certain its behavior is correct. """ macro boundscheck(blk) return Expr(:if, Expr(:boundscheck), esc(blk)) end """ @inbounds(blk) Eliminates array bounds checking within expressions. In the example below the in-range check for referencing element `i` of array `A` is skipped to improve performance. ```julia function sum(A::AbstractArray) r = zero(eltype(A)) for i in eachindex(A) @inbounds r += A[i] end return r end ``` !!! warning Using `@inbounds` may return incorrect results/crashes/corruption for out-of-bounds indices. The user is responsible for checking it manually. Only use `@inbounds` when it is certain from the information locally available that all accesses are in bounds. In particular, using `1:length(A)` instead of `eachindex(A)` in a function like the one above is _not_ safely inbounds because the first index of `A` may not be `1` for all user defined types that subtype `AbstractArray`. """ macro inbounds(blk) return Expr(:block, Expr(:inbounds, true), Expr(:local, Expr(:(=), :val, esc(blk))), Expr(:inbounds, :pop), :val) end """ @label name Labels a statement with the symbolic label `name`. The label marks the end-point of an unconditional jump with [`@goto name`](@ref). """ macro label(name::Symbol) return esc(Expr(:symboliclabel, name)) end """ @goto name `@goto name` unconditionally jumps to the statement at the location [`@label name`](@ref). `@label` and `@goto` cannot create jumps to different top-level statements. Attempts cause an error. To still use `@goto`, enclose the `@label` and `@goto` in a block. """ macro goto(name::Symbol) return esc(Expr(:symbolicgoto, name)) end # SimpleVector getindex(v::SimpleVector, i::Int) = (@_foldable_meta; Core._svec_ref(v, i)) function length(v::SimpleVector) @_total_meta t = @_gc_preserve_begin v len = unsafe_load(Ptr{Int}(pointer_from_objref(v))) @_gc_preserve_end t return len end firstindex(v::SimpleVector) = 1 lastindex(v::SimpleVector) = length(v) iterate(v::SimpleVector, i=1) = (length(v) < i ? nothing : (v[i], i + 1)) eltype(::Type{SimpleVector}) = Any keys(v::SimpleVector) = OneTo(length(v)) isempty(v::SimpleVector) = (length(v) == 0) axes(v::SimpleVector) = (OneTo(length(v)),) axes(v::SimpleVector, d::Integer) = d <= 1 ? axes(v)[d] : OneTo(1) function ==(v1::SimpleVector, v2::SimpleVector) length(v1)==length(v2) || return false for i = 1:length(v1) v1[i] == v2[i] || return false end return true end map(f, v::SimpleVector) = Any[ f(v[i]) for i = 1:length(v) ] getindex(v::SimpleVector, I::AbstractArray) = Core.svec(Any[ v[i] for i in I ]...) unsafe_convert(::Type{Ptr{Any}}, sv::SimpleVector) = convert(Ptr{Any},pointer_from_objref(sv)) + sizeof(Ptr) """ isassigned(array, i) -> Bool Test whether the given array has a value associated with index `i`. Return `false` if the index is out of bounds, or has an undefined reference. # Examples ```jldoctest julia> isassigned(rand(3, 3), 5) true julia> isassigned(rand(3, 3), 3 * 3 + 1) false julia> mutable struct Foo end julia> v = similar(rand(3), Foo) 3-element Vector{Foo}: #undef #undef #undef julia> isassigned(v, 1) false ``` """ function isassigned end function isassigned(v::SimpleVector, i::Int) @boundscheck 1 <= i <= length(v) || return false return true end """ Colon() Colons (:) are used to signify indexing entire objects or dimensions at once. Very few operations are defined on Colons directly; instead they are converted by [`to_indices`](@ref) to an internal vector type (`Base.Slice`) to represent the collection of indices they span before being used. The singleton instance of `Colon` is also a function used to construct ranges; see [`:`](@ref). """ struct Colon <: Function end const (:) = Colon() """ Val(c) Return `Val{c}()`, which contains no run-time data. Types like this can be used to pass the information between functions through the value `c`, which must be an `isbits` value or a `Symbol`. The intent of this construct is to be able to dispatch on constants directly (at compile time) without having to test the value of the constant at run time. # Examples ```jldoctest julia> f(::Val{true}) = "Good" f (generic function with 1 method) julia> f(::Val{false}) = "Bad" f (generic function with 2 methods) julia> f(Val(true)) "Good" ``` """ struct Val{x} end Val(x) = Val{x}() """ invokelatest(f, args...; kwargs...) Calls `f(args...; kwargs...)`, but guarantees that the most recent method of `f` will be executed. This is useful in specialized circumstances, e.g. long-running event loops or callback functions that may call obsolete versions of a function `f`. (The drawback is that `invokelatest` is somewhat slower than calling `f` directly, and the type of the result cannot be inferred by the compiler.) !!! compat "Julia 1.9" Prior to Julia 1.9, this function was not exported, and was called as `Base.invokelatest`. """ function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...) kwargs = merge(NamedTuple(), kwargs) if isempty(kwargs) return Core._call_latest(f, args...) end return Core._call_latest(Core.kwcall, kwargs, f, args...) end """ invoke_in_world(world, f, args...; kwargs...) Call `f(args...; kwargs...)` in a fixed world age, `world`. This is useful for infrastructure running in the user's Julia session which is not part of the user's program. For example, things related to the REPL, editor support libraries, etc. In these cases it can be useful to prevent unwanted method invalidation and recompilation latency, and to prevent the user from breaking supporting infrastructure by mistake. The current world age can be queried using [`Base.get_world_counter()`](@ref) and stored for later use within the lifetime of the current Julia session, or when serializing and reloading the system image. Technically, `invoke_in_world` will prevent any function called by `f` from being extended by the user during their Julia session. That is, generic function method tables seen by `f` (and any functions it calls) will be frozen as they existed at the given `world` age. In a sense, this is like the opposite of [`invokelatest`](@ref). !!! note It is not valid to store world ages obtained in precompilation for later use. This is because precompilation generates a "parallel universe" where the world age refers to system state unrelated to the main Julia session. """ function invoke_in_world(world::UInt, @nospecialize(f), @nospecialize args...; kwargs...) kwargs = Base.merge(NamedTuple(), kwargs) if isempty(kwargs) return Core._call_in_world(world, f, args...) end return Core._call_in_world(world, Core.kwcall, kwargs, f, args...) end inferencebarrier(@nospecialize(x)) = compilerbarrier(:type, x) """ isempty(collection) -> Bool Determine whether a collection is empty (has no elements). !!! warning `isempty(itr)` may consume the next element of a stateful iterator `itr` unless an appropriate `Base.isdone(itr)` or `isempty` method is defined. Use of `isempty` should therefore be avoided when writing generic code which should support any iterator type. # Examples ```jldoctest julia> isempty([]) true julia> isempty([1 2 3]) false ``` """ function isempty(itr) d = isdone(itr) d !== missing && return d iterate(itr) === nothing end """ values(iterator) For an iterator or collection that has keys and values, return an iterator over the values. This function simply returns its argument by default, since the elements of a general iterator are normally considered its "values". # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2); julia> values(d) ValueIterator for a Dict{String, Int64} with 2 entries. Values: 2 1 julia> values([2]) 1-element Vector{Int64}: 2 ``` """ values(itr) = itr """ Missing A type with no fields whose singleton instance [`missing`](@ref) is used to represent missing values. See also: [`skipmissing`](@ref), [`nonmissingtype`](@ref), [`Nothing`](@ref). """ struct Missing end """ missing The singleton instance of type [`Missing`](@ref) representing a missing value. See also: [`NaN`](@ref), [`skipmissing`](@ref), [`nonmissingtype`](@ref). """ const missing = Missing() """ ismissing(x) Indicate whether `x` is [`missing`](@ref). See also: [`skipmissing`](@ref), [`isnothing`](@ref), [`isnan`](@ref). """ ismissing(x) = x === missing function popfirst! end """ peek(stream[, T=UInt8]) Read and return a value of type `T` from a stream without advancing the current position in the stream. See also [`startswith(stream, char_or_string)`](@ref). # Examples ```jldoctest julia> b = IOBuffer("julia"); julia> peek(b) 0x6a julia> position(b) 0 julia> peek(b, Char) 'j': ASCII/Unicode U+006A (category Ll: Letter, lowercase) ``` !!! compat "Julia 1.5" The method which accepts a type requires Julia 1.5 or later. """ function peek end """ @__LINE__ -> Int Expand to the line number of the location of the macrocall. Return `0` if the line number could not be determined. """ macro __LINE__() return __source__.line end # Iteration """ isdone(itr, state...) -> Union{Bool, Missing} This function provides a fast-path hint for iterator completion. This is useful for mutable iterators that want to avoid having elements consumed, if they are not going to be exposed to the user (e.g. to check for done-ness in `isempty` or `zip`). Mutable iterators that want to opt into this feature should define an isdone method that returns true/false depending on whether the iterator is done or not. Stateless iterators need not implement this function. If the result is `missing`, callers may go ahead and compute `iterate(x, state...) === nothing` to compute a definite answer. """ isdone(itr, state...) = missing """ iterate(iter [, state]) -> Union{Nothing, Tuple{Any, Any}} Advance the iterator to obtain the next element. If no elements remain, `nothing` should be returned. Otherwise, a 2-tuple of the next element and the new iteration state should be returned. """ function iterate end """ isiterable(T) -> Bool Test if type `T` is an iterable collection type or not, that is whether it has an `iterate` method or not. """ function isiterable(T)::Bool return hasmethod(iterate, Tuple{T}) end M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ctypes.jlฌ# This file is a part of Julia. License is MIT: https://julialang.org/license # essential type definitions for interacting with C code # (platform- or OS-dependent types are defined in c.jl) """ Cuchar Equivalent to the native `unsigned char` c-type ([`UInt8`](@ref)). """ const Cuchar = UInt8 """ Cshort Equivalent to the native `signed short` c-type ([`Int16`](@ref)). """ const Cshort = Int16 """ Cushort Equivalent to the native `unsigned short` c-type ([`UInt16`](@ref)). """ const Cushort = UInt16 """ Cint Equivalent to the native `signed int` c-type ([`Int32`](@ref)). """ const Cint = Int32 """ Cuint Equivalent to the native `unsigned int` c-type ([`UInt32`](@ref)). """ const Cuint = UInt32 """ Cptrdiff_t Equivalent to the native `ptrdiff_t` c-type (`Int`). """ const Cptrdiff_t = Int """ Csize_t Equivalent to the native `size_t` c-type (`UInt`). """ const Csize_t = UInt """ Cssize_t Equivalent to the native `ssize_t` c-type. """ const Cssize_t = Int """ Cintmax_t Equivalent to the native `intmax_t` c-type ([`Int64`](@ref)). """ const Cintmax_t = Int64 """ Cuintmax_t Equivalent to the native `uintmax_t` c-type ([`UInt64`](@ref)). """ const Cuintmax_t = UInt64 """ Clonglong Equivalent to the native `signed long long` c-type ([`Int64`](@ref)). """ const Clonglong = Int64 """ Culonglong Equivalent to the native `unsigned long long` c-type ([`UInt64`](@ref)). """ const Culonglong = UInt64 """ Cfloat Equivalent to the native `float` c-type ([`Float32`](@ref)). """ const Cfloat = Float32 """ Cdouble Equivalent to the native `double` c-type ([`Float64`](@ref)). """ const Cdouble = Float64 N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/gcutils.jlv# This file is a part of Julia. License is MIT: https://julialang.org/license """ WeakRef(x) `w = WeakRef(x)` constructs a [weak reference](https://en.wikipedia.org/wiki/Weak_reference) to the Julia value `x`: although `w` contains a reference to `x`, it does not prevent `x` from being garbage collected. `w.value` is either `x` (if `x` has not been garbage-collected yet) or `nothing` (if `x` has been garbage-collected). ```jldoctest julia> x = "a string" "a string" julia> w = WeakRef(x) WeakRef("a string") julia> GC.gc() julia> w # a reference is maintained via `x` WeakRef("a string") julia> x = nothing # clear reference julia> GC.gc() julia> w WeakRef(nothing) ``` """ WeakRef ==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) ==(w::WeakRef, v) = isequal(w.value, v) ==(w, v::WeakRef) = isequal(w, v.value) # Used by `Base.finalizer` to validate mutability of an object being finalized. function _check_mutable(@nospecialize(o)) @noinline if !ismutable(o) error("objects of type ", typeof(o), " cannot be finalized") end end """ finalizer(f, x) Register a function `f(x)` to be called when there are no program-accessible references to `x`, and return `x`. The type of `x` must be a `mutable struct`, otherwise the function will throw. `f` must not cause a task switch, which excludes most I/O operations such as `println`. Using the `@async` macro (to defer context switching to outside of the finalizer) or `ccall` to directly invoke IO functions in C may be helpful for debugging purposes. Note that there is no guaranteed world age for the execution of `f`. It may be called in the world age in which the finalizer was registered or any later world age. # Examples ```julia finalizer(my_mutable_struct) do x @async println("Finalizing \$x.") end finalizer(my_mutable_struct) do x ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring), "Finalizing %s.", repr(x)) end ``` A finalizer may be registered at object construction. In the following example note that we implicitly rely on the finalizer returning the newly created mutable struct `x`. # Example ```julia mutable struct MyMutableStruct bar function MyMutableStruct(bar) x = new(bar) f(t) = @async println("Finalizing \$t.") finalizer(f, x) end end ``` """ function finalizer(@nospecialize(f), @nospecialize(o)) _check_mutable(o) Core.finalizer(f, o) return o end function finalizer(f::Ptr{Cvoid}, o::T) where T @inline _check_mutable(o) ccall(:jl_gc_add_ptr_finalizer, Cvoid, (Ptr{Cvoid}, Any, Ptr{Cvoid}), Core.getptls(), o, f) return o end """ finalize(x) Immediately run finalizers registered for object `x`. """ finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Cvoid, (Any, Any,), current_task(), o) """ Base.GC Module with garbage collection utilities. """ module GC # mirrored from julia.h const GC_AUTO = 0 const GC_FULL = 1 const GC_INCREMENTAL = 2 """ GC.gc([full=true]) Perform garbage collection. The argument `full` determines the kind of collection: A full collection (default) sweeps all objects, which makes the next GC scan much slower, while an incremental collection may only sweep so-called young objects. !!! warning Excessive use will likely lead to poor performance. """ gc(full::Bool=true) = ccall(:jl_gc_collect, Cvoid, (Cint,), full ? GC_FULL : GC_INCREMENTAL) """ GC.enable(on::Bool) Control whether garbage collection is enabled using a boolean argument (`true` for enabled, `false` for disabled). Return previous GC state. !!! warning Disabling garbage collection should be used only with caution, as it can cause memory use to grow without bound. """ enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 """ GC.enable_finalizers(on::Bool) Increment or decrement the counter that controls the running of finalizers on the current Task. Finalizers will only run when the counter is at zero. (Set `true` for enabling, `false` for disabling). They may still run concurrently on another Task or thread. """ enable_finalizers(on::Bool) = on ? enable_finalizers() : disable_finalizers() function enable_finalizers() @inline ccall(:jl_gc_enable_finalizers_internal, Cvoid, ()) if Core.Intrinsics.atomic_pointerref(cglobal(:jl_gc_have_pending_finalizers, Cint), :monotonic) != 0 ccall(:jl_gc_run_pending_finalizers, Cvoid, (Ptr{Cvoid},), C_NULL) end end function disable_finalizers() @inline ccall(:jl_gc_disable_finalizers_internal, Cvoid, ()) end """ GC.in_finalizer()::Bool Returns `true` if the current task is running a finalizer, returns `false` otherwise. Will also return `false` within a finalizer which was inlined by the compiler's eager finalization optimization, or if `finalize` is called on the finalizer directly. The result of this function may be useful, for example, when a finalizer must wait on a resource to become available; instead of polling the resource in a `yield` loop (which is not legal to execute within a task running finalizers), busy polling or an `@async` continuation could be used instead. """ function in_finalizer() @inline ccall(:jl_gc_is_in_finalizer, Int8, ()) > 0 end """ GC.@preserve x1 x2 ... xn expr Mark the objects `x1, x2, ...` as being *in use* during the evaluation of the expression `expr`. This is only required in unsafe code where `expr` *implicitly uses* memory or other resources owned by one of the `x`s. *Implicit use* of `x` covers any indirect use of resources logically owned by `x` which the compiler cannot see. Some examples: * Accessing memory of an object directly via a `Ptr` * Passing a pointer to `x` to `ccall` * Using resources of `x` which would be cleaned up in the finalizer. `@preserve` should generally not have any performance impact in typical use cases where it briefly extends object lifetime. In implementation, `@preserve` has effects such as protecting dynamically allocated objects from garbage collection. # Examples When loading from a pointer with `unsafe_load`, the underlying object is implicitly used, for example `x` is implicitly used by `unsafe_load(p)` in the following: ```jldoctest julia> let x = Ref{Int}(101) p = Base.unsafe_convert(Ptr{Int}, x) GC.@preserve x unsafe_load(p) end 101 ``` When passing pointers to `ccall`, the pointed-to object is implicitly used and should be preserved. (Note however that you should normally just pass `x` directly to `ccall` which counts as an explicit use.) ```jldoctest julia> let x = "Hello" p = pointer(x) Int(GC.@preserve x @ccall strlen(p::Cstring)::Csize_t) # Preferred alternative Int(@ccall strlen(x::Cstring)::Csize_t) end 5 ``` """ macro preserve(args...) syms = args[1:end-1] for x in syms isa(x, Symbol) || error("Preserved variable must be a symbol") end esc(Expr(:gc_preserve, args[end], syms...)) end """ GC.safepoint() Inserts a point in the program where garbage collection may run. This can be useful in rare cases in multi-threaded programs where some threads are allocating memory (and hence may need to run GC) but other threads are doing only simple operations (no allocation, task switches, or I/O). Calling this function periodically in non-allocating threads allows garbage collection to run. !!! compat "Julia 1.4" This function is available as of Julia 1.4. """ safepoint() = ccall(:jl_gc_safepoint, Cvoid, ()) """ GC.enable_logging(on::Bool) When turned on, print statistics about each GC to stderr. """ function enable_logging(on::Bool=true) ccall(:jl_enable_gc_logging, Cvoid, (Cint,), on) end end # module GC P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/generator.jlึ# This file is a part of Julia. License is MIT: https://julialang.org/license """ Generator(f, iter) Given a function `f` and an iterator `iter`, construct an iterator that yields the values of `f` applied to the elements of `iter`. The syntax for constructing an instance of this type is `f(x) for x in iter [if cond(x)::Bool] `. The `[if cond(x)::Bool]` expression is optional and acts as a "guard", effectively filtering out values where the condition is false. ```jldoctest julia> g = (abs2(x) for x in 1:5 if x != 3); julia> for x in g println(x) end 1 4 16 25 julia> collect(g) 4-element Vector{Int64}: 1 4 16 25 ``` """ struct Generator{I,F} f::F iter::I end Generator(f, I1, I2, Is...) = Generator(a->f(a...), zip(I1, I2, Is...)) Generator(::Type{T}, iter::I) where {T,I} = Generator{I,Type{T}}(T, iter) Generator(::Type{T}, I1, I2, Is...) where {T} = Generator(a->T(a...), zip(I1, I2, Is...)) function iterate(g::Generator, s...) @inline y = iterate(g.iter, s...) y === nothing && return nothing y = y::Tuple{Any, Any} # try to give inference some idea of what to expect about the behavior of the next line return (g.f(y[1]), y[2]) end length(g::Generator) = length(g.iter) size(g::Generator) = size(g.iter) axes(g::Generator) = axes(g.iter) ndims(g::Generator) = ndims(g.iter) keys(g::Generator) = keys(g.iter) last(g::Generator) = g.f(last(g.iter)) isempty(g::Generator) = isempty(g.iter) isdone(g::Generator, state...) = isdone(g.iter, state...) ## iterator traits abstract type IteratorSize end struct SizeUnknown <: IteratorSize end struct HasLength <: IteratorSize end struct HasShape{N} <: IteratorSize end struct IsInfinite <: IteratorSize end """ IteratorSize(itertype::Type) -> IteratorSize Given the type of an iterator, return one of the following values: * `SizeUnknown()` if the length (number of elements) cannot be determined in advance. * `HasLength()` if there is a fixed, finite length. * `HasShape{N}()` if there is a known length plus a notion of multidimensional shape (as for an array). In this case `N` should give the number of dimensions, and the [`axes`](@ref) function is valid for the iterator. * `IsInfinite()` if the iterator yields values forever. The default value (for iterators that do not define this function) is `HasLength()`. This means that most iterators are assumed to implement [`length`](@ref). This trait is generally used to select between algorithms that pre-allocate space for their result, and algorithms that resize their result incrementally. ```jldoctest julia> Base.IteratorSize(1:5) Base.HasShape{1}() julia> Base.IteratorSize((2,3)) Base.HasLength() ``` """ IteratorSize(x) = IteratorSize(typeof(x)) IteratorSize(::Type) = HasLength() # HasLength is the default IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} abstract type IteratorEltype end struct EltypeUnknown <: IteratorEltype end struct HasEltype <: IteratorEltype end """ IteratorEltype(itertype::Type) -> IteratorEltype Given the type of an iterator, return one of the following values: * `EltypeUnknown()` if the type of elements yielded by the iterator is not known in advance. * `HasEltype()` if the element type is known, and [`eltype`](@ref) would return a meaningful value. `HasEltype()` is the default, since iterators are assumed to implement [`eltype`](@ref). This trait is generally used to select between algorithms that pre-allocate a specific type of result, and algorithms that pick a result type based on the types of yielded values. ```jldoctest julia> Base.IteratorEltype(1:5) Base.HasEltype() ``` """ IteratorEltype(x) = IteratorEltype(typeof(x)) IteratorEltype(::Type) = HasEltype() # HasEltype is the default IteratorEltype(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) IteratorEltype(::Type{Any}) = EltypeUnknown() IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/reflection.jlฎ"# This file is a part of Julia. License is MIT: https://julialang.org/license # name and module reflection """ parentmodule(m::Module) -> Module Get a module's enclosing `Module`. `Main` is its own parent. See also: [`names`](@ref), [`nameof`](@ref), [`fullname`](@ref), [`@__MODULE__`](@ref). # Examples ```jldoctest julia> parentmodule(Main) Main julia> parentmodule(Base.Broadcast) Base ``` """ parentmodule(m::Module) = ccall(:jl_module_parent, Ref{Module}, (Any,), m) """ moduleroot(m::Module) -> Module Find the root module of a given module. This is the first module in the chain of parent modules of `m` which is either a registered root module or which is its own parent module. """ function moduleroot(m::Module) while true is_root_module(m) && return m p = parentmodule(m) p === m && return m m = p end end """ @__MODULE__ -> Module Get the `Module` of the toplevel eval, which is the `Module` code is currently being read from. """ macro __MODULE__() return __module__ end """ fullname(m::Module) Get the fully-qualified name of a module as a tuple of symbols. For example, # Examples ```jldoctest julia> fullname(Base.Iterators) (:Base, :Iterators) julia> fullname(Main) (:Main,) ``` """ function fullname(m::Module) mn = nameof(m) if m === Main || m === Base || m === Core return (mn,) end mp = parentmodule(m) if mp === m return (mn,) end return (fullname(mp)..., mn) end """ names(x::Module; all::Bool = false, imported::Bool = false) Get an array of the names exported by a `Module`, excluding deprecated names. If `all` is true, then the list also includes non-exported names defined in the module, deprecated names, and compiler-generated names. If `imported` is true, then names explicitly imported from other modules are also included. As a special case, all names defined in `Main` are considered \"exported\", since it is not idiomatic to explicitly export names from `Main`. See also: [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref). """ names(m::Module; all::Bool = false, imported::Bool = false) = sort!(unsorted_names(m; all, imported)) unsorted_names(m::Module; all::Bool = false, imported::Bool = false) = ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint), m, all, imported) isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0 isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0 function binding_module(m::Module, s::Symbol) p = ccall(:jl_get_module_of_binding, Ptr{Cvoid}, (Any, Any), m, s) p == C_NULL && return m return unsafe_pointer_to_objref(p)::Module end const _NAMEDTUPLE_NAME = NamedTuple.body.body.name function _fieldnames(@nospecialize t) if t.name === _NAMEDTUPLE_NAME if t.parameters[1] isa Tuple return t.parameters[1] else throw(ArgumentError("type does not have definite field names")) end end return t.name.names end """ fieldname(x::DataType, i::Integer) Get the name of field `i` of a `DataType`. # Examples ```jldoctest julia> fieldname(Rational, 1) :num julia> fieldname(Rational, 2) :den ``` """ function fieldname(t::DataType, i::Integer) throw_not_def_field() = throw(ArgumentError("type does not have definite field names")) function throw_field_access(t, i, n_fields) field_label = n_fields == 1 ? "field" : "fields" throw(ArgumentError("Cannot access field $i since type $t only has $n_fields $field_label.")) end throw_need_pos_int(i) = throw(ArgumentError("Field numbers must be positive integers. $i is invalid.")) isabstracttype(t) && throw_not_def_field() names = _fieldnames(t) n_fields = length(names)::Int i > n_fields && throw_field_access(t, i, n_fields) i < 1 && throw_need_pos_int(i) return @inbounds names[i]::Symbol end fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) fieldname(t::Type{<:Tuple}, i::Integer) = i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i) """ fieldnames(x::DataType) Get a tuple with the names of the fields of a `DataType`. See also [`propertynames`](@ref), [`hasfield`](@ref). # Examples ```jldoctest julia> fieldnames(Rational) (:num, :den) julia> fieldnames(typeof(1+im)) (:re, :im) ``` """ fieldnames(t::DataType) = (fieldcount(t); # error check to make sure type is specific enough (_fieldnames(t)...,))::Tuple{Vararg{Symbol}} fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t)) fieldnames(::Core.TypeofBottom) = throw(ArgumentError("The empty type does not have field names since it does not have instances.")) fieldnames(t::Type{<:Tuple}) = ntuple(identity, fieldcount(t)) """ hasfield(T::Type, name::Symbol) Return a boolean indicating whether `T` has `name` as one of its own fields. See also [`fieldnames`](@ref), [`fieldcount`](@ref), [`hasproperty`](@ref). !!! compat "Julia 1.2" This function requires at least Julia 1.2. # Examples ```jldoctest julia> struct Foo bar::Int end julia> hasfield(Foo, :bar) true julia> hasfield(Foo, :x) false ``` """ hasfield(T::Type, name::Symbol) = fieldindex(T, name, false) > 0 """ nameof(t::DataType) -> Symbol Get the name of a (potentially `UnionAll`-wrapped) `DataType` (without its parent module) as a symbol. # Examples ```jldoctest julia> module Foo struct S{T} end end Foo julia> nameof(Foo.S{T} where T) :S ``` """ nameof(t::DataType) = t.name.name nameof(t::UnionAll) = nameof(unwrap_unionall(t))::Symbol """ parentmodule(t::DataType) -> Module Determine the module containing the definition of a (potentially `UnionAll`-wrapped) `DataType`. # Examples ```jldoctest julia> module Foo struct Int end end Foo julia> parentmodule(Int) Core julia> parentmodule(Foo.Int) Foo ``` """ parentmodule(t::DataType) = t.name.module parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) """ isconst(m::Module, s::Symbol) -> Bool Determine whether a global is declared `const` in a given module `m`. """ isconst(m::Module, s::Symbol) = ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 function isconst(g::GlobalRef) return ccall(:jl_globalref_is_const, Cint, (Any,), g) != 0 end """ isconst(t::DataType, s::Union{Int,Symbol}) -> Bool Determine whether a field `s` is declared `const` in a given type `t`. """ function isconst(@nospecialize(t::Type), s::Symbol) t = unwrap_unionall(t) isa(t, DataType) || return false return isconst(t, fieldindex(t, s, false)) end function isconst(@nospecialize(t::Type), s::Int) t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false # uncertain ismutabletype(t) || return true # immutable structs are always const 1 <= s <= length(t.name.names) || return true # OOB reads are "const" since they always throw constfields = t.name.constfields constfields === C_NULL && return false s -= 1 return unsafe_load(Ptr{UInt32}(constfields), 1 + sรท32) & (1 << (s%32)) != 0 end """ isfieldatomic(t::DataType, s::Union{Int,Symbol}) -> Bool Determine whether a field `s` is declared `@atomic` in a given type `t`. """ function isfieldatomic(@nospecialize(t::Type), s::Symbol) t = unwrap_unionall(t) isa(t, DataType) || return false return isfieldatomic(t, fieldindex(t, s, false)) end function isfieldatomic(@nospecialize(t::Type), s::Int) t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false # uncertain ismutabletype(t) || return false # immutable structs are never atomic 1 <= s <= length(t.name.names) || return false # OOB reads are not atomic (they always throw) atomicfields = t.name.atomicfields atomicfields === C_NULL && return false s -= 1 return unsafe_load(Ptr{UInt32}(atomicfields), 1 + sรท32) & (1 << (s%32)) != 0 end """ @locals() Construct a dictionary of the names (as symbols) and values of all local variables defined as of the call site. !!! compat "Julia 1.1" This macro requires at least Julia 1.1. # Examples ```jldoctest julia> let x = 1, y = 2 Base.@locals end Dict{Symbol, Any} with 2 entries: :y => 2 :x => 1 julia> function f(x) local y show(Base.@locals); println() for i = 1:1 show(Base.@locals); println() end y = 2 show(Base.@locals); println() nothing end; julia> f(42) Dict{Symbol, Any}(:x => 42) Dict{Symbol, Any}(:i => 1, :x => 42) Dict{Symbol, Any}(:y => 2, :x => 42) ``` """ macro locals() return Expr(:locals) end # concrete datatype predicates datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Core.SimpleVector, (Any,), x) struct DataTypeLayout size::UInt32 nfields::UInt32 npointers::UInt32 firstptr::Int32 alignment::UInt16 flags::UInt16 # haspadding : 1; # fielddesc_type : 2; end """ Base.datatype_alignment(dt::DataType) -> Int Memory allocation minimum alignment for instances of this type. Can be called on any `isconcretetype`. """ function datatype_alignment(dt::DataType) @_foldable_meta dt.layout == C_NULL && throw(UndefRefError()) alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment return Int(alignment) end function uniontype_layout(@nospecialize T::Type) sz = RefValue{Csize_t}(0) algn = RefValue{Csize_t}(0) isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0 (isinline, Int(sz[]), Int(algn[])) end LLT_ALIGN(x, sz) = (x + sz - 1) & -sz # amount of total space taken by T when stored in a container function aligned_sizeof(@nospecialize T::Type) @_foldable_meta if isa(T, Union) if allocatedinline(T) # NOTE this check is equivalent to `isbitsunion(T)`, we can improve type # inference in the second branch with the outer `isa(T, Union)` check _, sz, al = uniontype_layout(T) return LLT_ALIGN(sz, al) end elseif allocatedinline(T) al = datatype_alignment(T) return LLT_ALIGN(Core.sizeof(T), al) end return Core.sizeof(Ptr{Cvoid}) end gc_alignment(sz::Integer) = Int(ccall(:jl_alignment, Cint, (Csize_t,), sz)) gc_alignment(T::Type) = gc_alignment(Core.sizeof(T)) """ Base.datatype_haspadding(dt::DataType) -> Bool Return whether the fields of instances of this type are packed in memory, with no intervening padding bytes. Can be called on any `isconcretetype`. """ function datatype_haspadding(dt::DataType) @_foldable_meta dt.layout == C_NULL && throw(UndefRefError()) flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags return flags & 1 == 1 end """ Base.datatype_nfields(dt::DataType) -> Bool Return the number of fields known to this datatype's layout. Can be called on any `isconcretetype`. """ function datatype_nfields(dt::DataType) @_foldable_meta dt.layout == C_NULL && throw(UndefRefError()) return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields end """ Base.datatype_pointerfree(dt::DataType) -> Bool Return whether instances of this type can contain references to gc-managed memory. Can be called on any `isconcretetype`. """ function datatype_pointerfree(dt::DataType) @_foldable_meta dt.layout == C_NULL && throw(UndefRefError()) npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers return npointers == 0 end """ Base.datatype_fielddesc_type(dt::DataType) -> Int Return the size in bytes of each field-description entry in the layout array, located at `(dt.layout + sizeof(DataTypeLayout))`. Can be called on any `isconcretetype`. See also [`fieldoffset`](@ref). """ function datatype_fielddesc_type(dt::DataType) @_foldable_meta dt.layout == C_NULL && throw(UndefRefError()) flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags return (flags >> 1) & 3 end # For type stability, we only expose a single struct that describes everything struct FieldDesc isforeign::Bool isptr::Bool size::UInt32 offset::UInt32 end struct FieldDescStorage{T} ptrsize::T offset::T end FieldDesc(fd::FieldDescStorage{T}) where {T} = FieldDesc(false, fd.ptrsize & 1 != 0, fd.ptrsize >> 1, fd.offset) struct DataTypeFieldDesc dt::DataType function DataTypeFieldDesc(dt::DataType) dt.layout == C_NULL && throw(UndefRefError()) new(dt) end end function getindex(dtfd::DataTypeFieldDesc, i::Int) layout_ptr = convert(Ptr{DataTypeLayout}, dtfd.dt.layout) fd_ptr = layout_ptr + sizeof(DataTypeLayout) layout = unsafe_load(layout_ptr) fielddesc_type = (layout.flags >> 1) & 3 nfields = layout.nfields @boundscheck ((1 <= i <= nfields) || throw(BoundsError(dtfd, i))) if fielddesc_type == 0 return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt8}}(fd_ptr), i)) elseif fielddesc_type == 1 return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt16}}(fd_ptr), i)) elseif fielddesc_type == 2 return FieldDesc(unsafe_load(Ptr{FieldDescStorage{UInt32}}(fd_ptr), i)) else # fielddesc_type == 3 return FieldDesc(true, true, 0, 0) end end """ ismutable(v) -> Bool Return `true` if and only if value `v` is mutable. See [Mutable Composite Types](@ref) for a discussion of immutability. Note that this function works on values, so if you give it a `DataType`, it will tell you that a value of the type is mutable. !!! note For technical reasons, `ismutable` returns `true` for values of certain special types (for example `String` and `Symbol`) even though they cannot be mutated in a permissible way. See also [`isbits`](@ref), [`isstructtype`](@ref). # Examples ```jldoctest julia> ismutable(1) false julia> ismutable([1,2]) true ``` !!! compat "Julia 1.5" This function requires at least Julia 1.5. """ ismutable(@nospecialize(x)) = (@_total_meta; (typeof(x).name::Core.TypeName).flags & 0x2 == 0x2) # The type assertion above is required to fix some invalidations. # See also https://github.com/JuliaLang/julia/issues/52134 """ ismutabletype(T) -> Bool Determine whether type `T` was declared as a mutable type (i.e. using `mutable struct` keyword). !!! compat "Julia 1.7" This function requires at least Julia 1.7. """ function ismutabletype(@nospecialize t) @_total_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? return isa(t, DataType) && t.name.flags & 0x2 == 0x2 end """ isstructtype(T) -> Bool Determine whether type `T` was declared as a struct type (i.e. using the `struct` or `mutable struct` keyword). """ function isstructtype(@nospecialize t) @_total_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false return !isprimitivetype(t) && !isabstracttype(t) end """ isprimitivetype(T) -> Bool Determine whether type `T` was declared as a primitive type (i.e. using the `primitive type` syntax). """ function isprimitivetype(@nospecialize t) @_total_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false return (t.flags & 0x0080) == 0x0080 end """ isbitstype(T) Return `true` if type `T` is a "plain data" type, meaning it is immutable and contains no references to other values, only `primitive` types and other `isbitstype` types. Typical examples are numeric types such as [`UInt8`](@ref), [`Float64`](@ref), and [`Complex{Float64}`](@ref). This category of types is significant since they are valid as type parameters, may not track [`isdefined`](@ref) / [`isassigned`](@ref) status, and have a defined layout that is compatible with C. See also [`isbits`](@ref), [`isprimitivetype`](@ref), [`ismutable`](@ref). # Examples ```jldoctest julia> isbitstype(Complex{Float64}) true julia> isbitstype(Complex) false ``` """ isbitstype(@nospecialize t) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0008) == 0x0008) """ isbits(x) Return `true` if `x` is an instance of an [`isbitstype`](@ref) type. """ isbits(@nospecialize x) = isbitstype(typeof(x)) """ objectid(x) -> UInt Get a hash value for `x` based on object identity. If `x === y` then `objectid(x) == objectid(y)`, and usually when `x !== y`, `objectid(x) != objectid(y)`. See also [`hash`](@ref), [`IdDict`](@ref). """ function objectid(x) # objectid is foldable iff it isn't a pointer. if isidentityfree(typeof(x)) return _foldable_objectid(x) end return _objectid(x) end function _foldable_objectid(@nospecialize(x)) @_foldable_meta _objectid(x) end _objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) """ isdispatchtuple(T) Determine whether type `T` is a tuple "leaf type", meaning it could appear as a type signature in dispatch and has no subtypes (or supertypes) which could appear in a call. """ isdispatchtuple(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0004) == 0x0004) datatype_ismutationfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0100) == 0x0100) """ ismutationfree(T) Determine whether type `T` is mutation free in the sense that no mutable memory is reachable from this type (either in the type itself) or through any fields. Note that the type itself need not be immutable. For example, an empty mutable type is `ismutabletype`, but also `ismutationfree`. """ function ismutationfree(@nospecialize(t)) t = unwrap_unionall(t) if isa(t, DataType) return datatype_ismutationfree(t) elseif isa(t, Union) return ismutationfree(t.a) && ismutationfree(t.b) end # TypeVar, etc. return false end datatype_isidentityfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0200) == 0x0200) """ isidentityfree(T) Determine whether type `T` is identity free in the sense that this type or any reachable through its fields has non-content-based identity. """ function isidentityfree(@nospecialize(t)) t = unwrap_unionall(t) if isa(t, DataType) return datatype_isidentityfree(t) elseif isa(t, Union) return isidentityfree(t.a) && isidentityfree(t.b) end # TypeVar, etc. return false end iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0 # equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} # and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query function isdispatchelem(@nospecialize v) return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) || (isType(v) && !has_free_typevars(v)) end const _TYPE_NAME = Type.body.name isType(@nospecialize t) = isa(t, DataType) && t.name === _TYPE_NAME """ isconcretetype(T) Determine whether type `T` is a concrete type, meaning it could have direct instances (values `x` such that `typeof(x) === T`). See also: [`isbits`](@ref), [`isabstracttype`](@ref), [`issingletontype`](@ref). # Examples ```jldoctest julia> isconcretetype(Complex) false julia> isconcretetype(Complex{Float32}) true julia> isconcretetype(Vector{Complex}) true julia> isconcretetype(Vector{Complex{Float32}}) true julia> isconcretetype(Union{}) false julia> isconcretetype(Union{Int,String}) false ``` """ isconcretetype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0002) == 0x0002) """ isabstracttype(T) Determine whether type `T` was declared as an abstract type (i.e. using the `abstract type` syntax). # Examples ```jldoctest julia> isabstracttype(AbstractArray) true julia> isabstracttype(Vector) false ``` """ function isabstracttype(@nospecialize(t)) @_total_meta t = unwrap_unionall(t) # TODO: what to do for `Union`? return isa(t, DataType) && (t.name.flags & 0x1) == 0x1 end """ Base.issingletontype(T) Determine whether type `T` has exactly one possible instance; for example, a struct type with no fields. """ issingletontype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && isdefined(t, :instance)) """ typeintersect(T::Type, S::Type) Compute a type that contains the intersection of `T` and `S`. Usually this will be the smallest such type or one close to it. """ typeintersect(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_intersection, Any, (Any, Any), a::Type, b::Type)) morespecific(@nospecialize(a), @nospecialize(b)) = (@_total_meta; ccall(:jl_type_morespecific, Cint, (Any, Any), a::Type, b::Type) != 0) """ fieldoffset(type, i) The byte offset of field `i` of a type relative to the data start. For example, we could use it in the following manner to summarize information about a struct: ```jldoctest julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)]; julia> structinfo(Base.Filesystem.StatStruct) 13-element Vector{Tuple{UInt64, Symbol, Type}}: (0x0000000000000000, :desc, Union{RawFD, String}) (0x0000000000000008, :device, UInt64) (0x0000000000000010, :inode, UInt64) (0x0000000000000018, :mode, UInt64) (0x0000000000000020, :nlink, Int64) (0x0000000000000028, :uid, UInt64) (0x0000000000000030, :gid, UInt64) (0x0000000000000038, :rdev, UInt64) (0x0000000000000040, :size, Int64) (0x0000000000000048, :blksize, Int64) (0x0000000000000050, :blocks, Int64) (0x0000000000000058, :mtime, Float64) (0x0000000000000060, :ctime, Float64) ``` """ fieldoffset(x::DataType, idx::Integer) = (@_foldable_meta; ccall(:jl_get_field_offset, Csize_t, (Any, Cint), x, idx)) """ fieldtype(T, name::Symbol | index::Int) Determine the declared type of a field (specified by name or index) in a composite DataType `T`. # Examples ```jldoctest julia> struct Foo x::Int64 y::String end julia> fieldtype(Foo, :x) Int64 julia> fieldtype(Foo, 2) String ``` """ fieldtype """ Base.fieldindex(T, name::Symbol, err:Bool=true) Get the index of a named field, throwing an error if the field does not exist (when err==true) or returning 0 (when err==false). # Examples ```jldoctest julia> struct Foo x::Int64 y::String end julia> Base.fieldindex(Foo, :z) ERROR: type Foo has no field z Stacktrace: [...] julia> Base.fieldindex(Foo, :z, false) 0 ``` """ function fieldindex(T::DataType, name::Symbol, err::Bool=true) return err ? _fieldindex_maythrow(T, name) : _fieldindex_nothrow(T, name) end function _fieldindex_maythrow(T::DataType, name::Symbol) @_foldable_meta @noinline return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, true)+1) end function _fieldindex_nothrow(T::DataType, name::Symbol) @_total_meta @noinline return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, false)+1) end function fieldindex(t::UnionAll, name::Symbol, err::Bool=true) t = argument_datatype(t) if t === nothing err && throw(ArgumentError("type does not have definite fields")) return 0 end return fieldindex(t, name, err) end function argument_datatype(@nospecialize t) @_total_meta @noinline return ccall(:jl_argument_datatype, Any, (Any,), t)::Union{Nothing,DataType} end function datatype_fieldcount(t::DataType) if t.name === _NAMEDTUPLE_NAME names, types = t.parameters[1], t.parameters[2] if names isa Tuple return length(names) end if types isa DataType && types <: Tuple return fieldcount(types) end return nothing elseif isabstracttype(t) || (t.name === Tuple.name && isvatuple(t)) return nothing end if isdefined(t, :types) return length(t.types) end return length(t.name.names) end """ fieldcount(t::Type) Get the number of fields that an instance of the given type would have. An error is thrown if the type is too abstract to determine this. """ function fieldcount(@nospecialize t) @_foldable_meta if t isa UnionAll || t isa Union t = argument_datatype(t) if t === nothing throw(ArgumentError("type does not have a definite number of fields")) end elseif t === Union{} throw(ArgumentError("The empty type does not have a well-defined number of fields since it does not have instances.")) end if !(t isa DataType) throw(TypeError(:fieldcount, DataType, t)) end fcount = datatype_fieldcount(t) if fcount === nothing throw(ArgumentError("type does not have a definite number of fields")) end return fcount end """ fieldtypes(T::Type) The declared types of all fields in a composite DataType `T` as a tuple. !!! compat "Julia 1.1" This function requires at least Julia 1.1. # Examples ```jldoctest julia> struct Foo x::Int64 y::String end julia> fieldtypes(Foo) (Int64, String) ``` """ fieldtypes(T::Type) = (@_foldable_meta; ntupleany(i -> fieldtype(T, i), fieldcount(T))) # return all instances, for types that can be enumerated """ instances(T::Type) Return a collection of all instances of the given type, if applicable. Mostly used for enumerated types (see `@enum`). # Example ```jldoctest julia> @enum Color red blue green julia> instances(Color) (red, blue, green) ``` """ function instances end function to_tuple_type(@nospecialize(t)) if isa(t, Tuple) || isa(t, AbstractArray) || isa(t, SimpleVector) t = Tuple{t...} end if isa(t, Type) && t <: Tuple for p in (unwrap_unionall(t)::DataType).parameters if isa(p, Core.TypeofVararg) p = unwrapva(p) end if !(isa(p, Type) || isa(p, TypeVar)) error("argument tuple type must contain only types") end end else error("expected tuple type") end t end function signature_type(@nospecialize(f), @nospecialize(argtypes)) argtypes = to_tuple_type(argtypes) ft = Core.Typeof(f) u = unwrap_unionall(argtypes)::DataType return rewrap_unionall(Tuple{ft, u.parameters...}, argtypes) end """ code_lowered(f, types; generated=true, debuginfo=:default) Return an array of the lowered forms (IR) for the methods matching the given generic function and type signature. If `generated` is `false`, the returned `CodeInfo` instances will correspond to fallback implementations. An error is thrown if no fallback implementation exists. If `generated` is `true`, these `CodeInfo` instances will correspond to the method bodies yielded by expanding the generators. The keyword `debuginfo` controls the amount of code metadata present in the output. Note that an error will be thrown if `types` are not leaf types when `generated` is `true` and any of the corresponding methods are an `@generated` method. """ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool=true, debuginfo::Symbol=:default) if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default debuginfo = :source end if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end world = get_world_counter() return map(method_instances(f, t, world)) do m if generated && hasgenerator(m) if may_invoke_generator(m) return ccall(:jl_code_for_staged, Any, (Any, UInt), m, world)::CodeInfo else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", "not leaf types, but the `generated` argument is `true`.") end end code = uncompressed_ir(m.def::Method) debuginfo === :none && remove_linenums!(code) return code end end hasgenerator(m::Method) = isdefined(m, :generator) hasgenerator(m::Core.MethodInstance) = hasgenerator(m.def::Method) # low-level method lookup functions used by the compiler unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) unionlen(@nospecialize(x)) = 1 _uniontypes(x::Union, ts) = (_uniontypes(x.a,ts); _uniontypes(x.b,ts); ts) _uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) tt = signature_type(f, t) return _methods_by_ftype(tt, lim, world) end function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt) return _methods_by_ftype(t, nothing, lim, world) end function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt) return _methods_by_ftype(t, mt, lim, world, false, RefValue{UInt}(typemin(UInt)), RefValue{UInt}(typemax(UInt)), Ptr{Int32}(C_NULL)) end function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing}, lim::Int, world::UInt, ambig::Bool, min::Ref{UInt}, max::Ref{UInt}, has_ambig::Ref{Int32}) return ccall(:jl_matching_methods, Any, (Any, Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}, Ptr{Int32}), t, mt, lim, ambig, world, min, max, has_ambig)::Union{Vector{Any},Nothing} end # high-level, more convenient method lookup functions # type for reflecting and pretty-printing a subset of methods mutable struct MethodList <: AbstractArray{Method,1} ms::Array{Method,1} mt::Core.MethodTable end size(m::MethodList) = size(m.ms) getindex(m::MethodList, i::Integer) = m.ms[i] function MethodList(mt::Core.MethodTable) ms = Method[] visit(mt) do m push!(ms, m) end return MethodList(ms, mt) end """ methods(f, [types], [module]) Return the method table for `f`. If `types` is specified, return an array of methods whose types match. If `module` is specified, return an array of methods defined in that module. A list of modules can also be specified as an array. !!! compat "Julia 1.4" At least Julia 1.4 is required for specifying a module. See also: [`which`](@ref) and `@which`. """ function methods(@nospecialize(f), @nospecialize(t), mod::Union{Tuple{Module},AbstractArray{Module},Nothing}=nothing) world = get_world_counter() # Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually ms = Method[] for m in _methods(f, t, -1, world)::Vector m = m::Core.MethodMatch (mod === nothing || parentmodule(m.method) โˆˆ mod) && push!(ms, m.method) end MethodList(ms, typeof(f).name.mt) end methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,)) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) world = get_world_counter() (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") min = RefValue{UInt}(typemin(UInt)) max = RefValue{UInt}(typemax(UInt)) ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector return MethodList(Method[(m::Core.MethodMatch).method for m in ms], typeof(f).name.mt) end function methods(@nospecialize(f), mod::Union{Module,AbstractArray{Module},Nothing}=nothing) # return all matches return methods(f, Tuple{Vararg{Any}}, mod) end function visit(f, mt::Core.MethodTable) mt.defs !== nothing && visit(f, mt.defs) nothing end function visit(f, mc::Core.TypeMapLevel) function avisit(f, e::Array{Any,1}) for i in 2:2:length(e) isassigned(e, i) || continue ei = e[i] if ei isa Vector{Any} for j in 2:2:length(ei) isassigned(ei, j) || continue visit(f, ei[j]) end else visit(f, ei) end end end if mc.targ !== nothing avisit(f, mc.targ::Vector{Any}) end if mc.arg1 !== nothing avisit(f, mc.arg1::Vector{Any}) end if mc.tname !== nothing avisit(f, mc.tname::Vector{Any}) end if mc.name1 !== nothing avisit(f, mc.name1::Vector{Any}) end mc.list !== nothing && visit(f, mc.list) mc.any !== nothing && visit(f, mc.any) nothing end function visit(f, d::Core.TypeMapEntry) while d !== nothing f(d.func) d = d.next end nothing end struct MethodSpecializations specializations::Union{Nothing, Core.MethodInstance, Core.SimpleVector} end """ specializations(m::Method) โ†’ itr Return an iterator `itr` of all compiler-generated specializations of `m`. """ specializations(m::Method) = MethodSpecializations(isdefined(m, :specializations) ? m.specializations : nothing) function iterate(specs::MethodSpecializations) s = specs.specializations s === nothing && return nothing isa(s, Core.MethodInstance) && return (s, nothing) return iterate(specs, 0) end iterate(specs::MethodSpecializations, ::Nothing) = nothing function iterate(specs::MethodSpecializations, i::Int) s = specs.specializations::Core.SimpleVector n = length(s) i >= n && return nothing item = nothing while i < n && item === nothing item = s[i+=1] end item === nothing && return nothing return (item, i) end length(specs::MethodSpecializations) = count(Returns(true), specs) function length(mt::Core.MethodTable) n = 0 visit(mt) do m n += 1 end return n::Int end isempty(mt::Core.MethodTable) = (mt.defs === nothing) uncompressed_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m, m.source) : isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : error("Code for this Method is not available.") _uncompressed_ir(m::Method, s::CodeInfo) = copy(s) _uncompressed_ir(m::Method, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo _uncompressed_ir(ci::Core.CodeInstance, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo # for backwards compat const uncompressed_ast = uncompressed_ir const _uncompressed_ast = _uncompressed_ir function method_instances(@nospecialize(f), @nospecialize(t), world::UInt) tt = signature_type(f, t) results = Core.MethodInstance[] # this make a better error message than the typeassert that follows world == typemax(UInt) && error("code reflection cannot be used from generated functions") for match in _methods_by_ftype(tt, -1, world)::Vector instance = Core.Compiler.specialize_method(match) push!(results, instance) end return results end default_debug_info_kind() = unsafe_load(cglobal(:jl_default_debug_info_kind, Cint)) # this type mirrors jl_cgparams_t (documented in julia.h) struct CodegenParams track_allocations::Cint code_coverage::Cint prefer_specsig::Cint gnu_pubnames::Cint debug_info_kind::Cint safepoint_on_entry::Cint gcstack_arg::Cint lookup::Ptr{Cvoid} generic_context::Any function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true, prefer_specsig::Bool=false, gnu_pubnames=true, debug_info_kind::Cint = default_debug_info_kind(), safepoint_on_entry::Bool=true, gcstack_arg::Bool=true, lookup::Ptr{Cvoid}=unsafe_load(cglobal(:jl_rettype_inferred_addr, Ptr{Cvoid})), generic_context = nothing) return new( Cint(track_allocations), Cint(code_coverage), Cint(prefer_specsig), Cint(gnu_pubnames), debug_info_kind, Cint(safepoint_on_entry), Cint(gcstack_arg), lookup, generic_context) end end const SLOT_USED = 0x8 ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_slotflag, UInt8, (Any, Csize_t), code, i - 1) """ may_invoke_generator(method, atype, sparams) -> Bool Computes whether or not we may invoke the generator for the given `method` on the given `atype` and `sparams`. For correctness, all generated function are required to return monotonic answers. However, since we don't expect users to be able to successfully implement this criterion, we only call generated functions on concrete types. The one exception to this is that we allow calling generators with abstract types if the generator does not use said abstract type (and thus cannot incorrectly use it to break monotonicity). This function computes whether we are in either of these cases. Unlike normal functions, the compilation heuristics still can't generate good dispatch in some cases, but this may still allow inference not to fall over in some limited cases. """ function may_invoke_generator(mi::MethodInstance) return may_invoke_generator(mi.def::Method, mi.specTypes, mi.sparam_vals) end function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector) # If we have complete information, we may always call the generator isdispatchtuple(atype) && return true # We don't have complete information, but it is possible that the generator # syntactically doesn't make use of the information we don't have. Check # for that. # For now, only handle the (common, generated by the frontend case) that the # generator only has one method generator = method.generator isa(generator, Core.GeneratedFunctionStub) || return false tt = Tuple{typeof(generator.gen), Vararg{Any}} gen_mthds = _methods_by_ftype(tt, #=lim=#1, method.primary_world) gen_mthds isa Vector || return false length(gen_mthds) == 1 || return false generator_method = first(gen_mthds).method nsparams = length(sparams) isdefined(generator_method, :source) || return false code = generator_method.source nslots = ccall(:jl_ir_nslots, Int, (Any,), code) at = unwrap_unionall(atype) at isa DataType || return false (nslots >= 1 + length(sparams) + length(at.parameters)) || return false firstarg = 1 for i = 1:nsparams if isa(sparams[i], TypeVar) if (ast_slotflag(code, firstarg + i) & SLOT_USED) != 0 return false end end end nargs = Int(method.nargs) non_va_args = method.isva ? nargs - 1 : nargs for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) if (ast_slotflag(code, firstarg + i + nsparams) & SLOT_USED) != 0 return false end end end if method.isva # If the va argument is used, we need to ensure that all arguments that # contribute to the va tuple are dispatchelemes if (ast_slotflag(code, firstarg + nargs + nsparams) & SLOT_USED) != 0 for i = (non_va_args+1):length(at.parameters) if !isdispatchelem(at.parameters[i]) return false end end end end return true end # give a decent error message if we try to instantiate a staged function on non-leaf types function func_for_method_checked(m::Method, @nospecialize(types), sparams::SimpleVector) if isdefined(m, :generator) && !may_invoke_generator(m, types, sparams) error("cannot call @generated function `", m, "` ", "with abstract argument types: ", types) end return m end """ code_typed(f, types; kw...) Returns an array of type-inferred lowered form (IR) for the methods matching the given generic function and type signature. # Keyword Arguments - `optimize::Bool = true`: optional, controls whether additional optimizations, such as inlining, are also applied. - `debuginfo::Symbol = :default`: optional, controls the amount of code metadata present in the output, possible options are `:source` or `:none`. # Internal Keyword Arguments This section should be considered internal, and is only for who understands Julia compiler internals. - `world::UInt = Base.get_world_counter()`: optional, controls the world age to use when looking up methods, use current world age if not specified. - `interp::Core.Compiler.AbstractInterpreter = Core.Compiler.NativeInterpreter(world)`: optional, controls the abstract interpreter to use, use the native interpreter if not specified. # Example One can put the argument types in a tuple to get the corresponding `code_typed`. ```julia julia> code_typed(+, (Float64, Float64)) 1-element Vector{Any}: CodeInfo( 1 โ”€ %1 = Base.add_float(x, y)::Float64 โ””โ”€โ”€ return %1 ) => Float64 ``` """ function code_typed(@nospecialize(f), @nospecialize(types=default_tt(f)); kwargs...) if isa(f, Core.OpaqueClosure) return code_typed_opaque_closure(f; kwargs...) end tt = signature_type(f, types) return code_typed_by_type(tt; kwargs...) end # returns argument tuple type which is supposed to be used for `code_typed` and its family; # if there is a single method this functions returns the method argument signature, # otherwise returns `Tuple` that doesn't match with any signature function default_tt(@nospecialize(f)) ms = methods(f) if length(ms) == 1 return tuple_type_tail(only(ms).sig) else return Tuple end end """ code_typed_by_type(types::Type{<:Tuple}; ...) Similar to [`code_typed`](@ref), except the argument is a tuple type describing a full signature to query. """ function code_typed_by_type(@nospecialize(tt::Type); optimize::Bool=true, debuginfo::Symbol=:default, world::UInt=get_world_counter(), interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) elseif debuginfo === :default debuginfo = :source end if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector asts = [] for match in matches match = match::Core.MethodMatch meth = func_for_method_checked(match.method, tt, match.sparams) (code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, optimize) if code === nothing push!(asts, meth => Any) else debuginfo === :none && remove_linenums!(code) push!(asts, code => ty) end end return asts end function code_typed_opaque_closure(@nospecialize(oc::Core.OpaqueClosure); debuginfo::Symbol=:default, _...) ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") m = oc.source if isa(m, Method) code = _uncompressed_ir(m, m.source) debuginfo === :none && remove_linenums!(code) # intersect the declared return type and the inferred return type (if available) rt = typeintersect(code.rettype, typeof(oc).parameters[2]) return Any[code => rt] else error("encountered invalid Core.OpaqueClosure object") end end """ code_ircode(f, [types]) Return an array of pairs of `IRCode` and inferred return type if type inference succeeds. The `Method` is included instead of `IRCode` otherwise. See also: [`code_typed`](@ref) # Internal Keyword Arguments This section should be considered internal, and is only for who understands Julia compiler internals. - `world::UInt = Base.get_world_counter()`: optional, controls the world age to use when looking up methods, use current world age if not specified. - `interp::Core.Compiler.AbstractInterpreter = Core.Compiler.NativeInterpreter(world)`: optional, controls the abstract interpreter to use, use the native interpreter if not specified. - `optimize_until::Union{Integer,AbstractString,Nothing} = nothing`: optional, controls the optimization passes to run. If it is a string, it specifies the name of the pass up to which the optimizer is run. If it is an integer, it specifies the number of passes to run. If it is `nothing` (default), all passes are run. # Example One can put the argument types in a tuple to get the corresponding `code_ircode`. ```julia julia> Base.code_ircode(+, (Float64, Int64)) 1-element Vector{Any}: 388 1 โ”€ %1 = Base.sitofp(Float64, _3)::Float64 โ”‚ %2 = Base.add_float(_2, %1)::Float64 โ””โ”€โ”€ return %2 => Float64 julia> Base.code_ircode(+, (Float64, Int64); optimize_until = "compact 1") 1-element Vector{Any}: 388 1 โ”€ %1 = Base.promote(_2, _3)::Tuple{Float64, Float64} โ”‚ %2 = Core._apply_iterate(Base.iterate, Base.:+, %1)::Float64 โ””โ”€โ”€ return %2 => Float64 ``` """ function code_ircode(@nospecialize(f), @nospecialize(types = default_tt(f)); kwargs...) if isa(f, Core.OpaqueClosure) error("OpaqueClosure not supported") end tt = signature_type(f, types) return code_ircode_by_type(tt; kwargs...) end """ code_ircode_by_type(types::Type{<:Tuple}; ...) Similar to [`code_ircode`](@ref), except the argument is a tuple type describing a full signature to query. """ function code_ircode_by_type( @nospecialize(tt::Type); world::UInt=get_world_counter(), interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world), optimize_until::Union{Integer,AbstractString,Nothing}=nothing, ) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector asts = [] for match in matches match = match::Core.MethodMatch meth = func_for_method_checked(match.method, tt, match.sparams) (code, ty) = Core.Compiler.typeinf_ircode( interp, meth, match.spec_types, match.sparams, optimize_until, ) if code === nothing push!(asts, meth => Any) else push!(asts, code => ty) end end return asts end """ Base.return_types(f::Function, types::DataType=default_tt(f); world::UInt=get_world_counter(), interp::NativeInterpreter=Core.Compiler.NativeInterpreter(world)) Return a list of possible return types for a given function `f` and argument types `types`. The list corresponds to the results of type inference on all the possible method match candidates for `f` and `types` (see also [`methods(f, types)`](@ref methods). # Example ```julia julia> Base.return_types(sum, Tuple{Vector{Int}}) 1-element Vector{Any}: Int64 julia> methods(sum, (Union{Vector{Int},UnitRange{Int}},)) # 2 methods for generic function "sum" from Base: [1] sum(r::AbstractRange{<:Real}) @ range.jl:1396 [2] sum(a::AbstractArray; dims, kw...) @ reducedim.jl:996 julia> Base.return_types(sum, (Union{Vector{Int},UnitRange{Int}},)) 2-element Vector{Any}: Int64 # the result of inference on sum(r::AbstractRange{<:Real}) Int64 # the result of inference on sum(a::AbstractArray; dims, kw...) ``` !!! warning The `return_types` function should not be used from generated functions; doing so will result in an error. """ function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)); world::UInt=get_world_counter(), interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") if isa(f, Core.OpaqueClosure) _, rt = only(code_typed_opaque_closure(f)) return Any[rt] end if isa(f, Core.Builtin) argtypes = Any[to_tuple_type(types).parameters...] rt = Core.Compiler.builtin_tfunction(interp, f, argtypes, nothing) return Any[Core.Compiler.widenconst(rt)] end rts = [] tt = signature_type(f, types) matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector for match in matches match = match::Core.MethodMatch meth = func_for_method_checked(match.method, types, match.sparams) ty = Core.Compiler.typeinf_type(interp, meth, match.spec_types, match.sparams) push!(rts, something(ty, Any)) end return rts end """ infer_effects(f, types=default_tt(f); world=get_world_counter(), interp=Core.Compiler.NativeInterpreter(world)) Compute the `Effects` of a function `f` with argument types `types`. The `Effects` represents the computational effects of the function call, such as whether it is free of side effects, guaranteed not to throw an exception, guaranteed to terminate, etc. The `world` and `interp` arguments specify the world counter and the native interpreter to use for the analysis. # Arguments - `f`: The function to analyze. - `types` (optional): The argument types of the function. Defaults to the default tuple type of `f`. - `world` (optional): The world counter to use for the analysis. Defaults to the current world counter. - `interp` (optional): The native interpreter to use for the analysis. Defaults to a new `Core.Compiler.NativeInterpreter` with the specified `world`. # Returns - `effects::Effects`: The computed effects of the function call. # Example ```julia julia> function foo(x) y = x * 2 return y end; julia> effects = Base.infer_effects(foo, (Int,)) (+c,+e,+n,+t,+s,+m,+i) ``` This function will return an `Effects` object with information about the computational effects of the function `foo` when called with an `Int` argument. See the documentation for `Effects` for more information on the various effect properties. !!! warning The `infer_effects` function should not be used from generated functions; doing so will result in an error. # See Also - [`Core.Compiler.Effects`](@ref): A type representing the computational effects of a method call. - [`Base.@assume_effects`](@ref): A macro for making assumptions about the effects of a method. """ function infer_effects(@nospecialize(f), @nospecialize(types=default_tt(f)); world = get_world_counter(), interp = Core.Compiler.NativeInterpreter(world)) (ccall(:jl_is_in_pure_context, Bool, ()) || world == typemax(UInt)) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) types = to_tuple_type(types) argtypes = Any[Core.Compiler.Const(f), types.parameters...] rt = Core.Compiler.builtin_tfunction(interp, f, argtypes[2:end], nothing) return Core.Compiler.builtin_effects(Core.Compiler.typeinf_lattice(interp), f, Core.Compiler.ArgInfo(nothing, argtypes), rt) end tt = signature_type(f, types) result = Core.Compiler.findall(tt, Core.Compiler.method_table(interp)) if result === missing # unanalyzable call, return the unknown effects return Core.Compiler.Effects() end (; matches) = result effects = Core.Compiler.EFFECTS_TOTAL if matches.ambig || !any(match::Core.MethodMatch->match.fully_covers, matches.matches) # account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. effects = Core.Compiler.Effects(effects; nothrow=false) end for match in matches.matches match = match::Core.MethodMatch frame = Core.Compiler.typeinf_frame(interp, match.method, match.spec_types, match.sparams, #=run_optimizer=#false) frame === nothing && return Core.Compiler.Effects() effects = Core.Compiler.merge_effects(effects, frame.ipo_effects) end return effects end """ print_statement_costs(io::IO, f, types) Print type-inferred and optimized code for `f` given argument types `types`, prepending each line with its cost as estimated by the compiler's inlining engine. """ function print_statement_costs(io::IO, @nospecialize(f), @nospecialize(t); kwargs...) tt = signature_type(f, t) print_statement_costs(io, tt; kwargs...) end function print_statement_costs(io::IO, @nospecialize(tt::Type); world::UInt=get_world_counter(), interp::Core.Compiler.AbstractInterpreter=Core.Compiler.NativeInterpreter(world)) tt = to_tuple_type(tt) matches = _methods_by_ftype(tt, #=lim=#-1, world)::Vector params = Core.Compiler.OptimizationParams(interp) cst = Int[] for match in matches match = match::Core.MethodMatch meth = func_for_method_checked(match.method, tt, match.sparams) println(io, meth) (code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, true) if code === nothing println(io, " inference not successful") else empty!(cst) resize!(cst, length(code.code)) sptypes = Core.Compiler.VarState[Core.Compiler.VarState(sp, false) for sp in match.sparams] maxcost = Core.Compiler.statement_costs!(cst, code.code, code, sptypes, false, params) nd = ndigits(maxcost) irshow_config = IRShow.IRShowConfig() do io, linestart, idx print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ") return "" end IRShow.show_ir(io, code, irshow_config) end println(io) end end print_statement_costs(args...; kwargs...) = print_statement_costs(stdout, args...; kwargs...) function _which(@nospecialize(tt::Type); method_table::Union{Nothing,Core.MethodTable,Core.Compiler.MethodTableView}=nothing, world::UInt=get_world_counter(), raise::Bool=true) world == typemax(UInt) && error("code reflection cannot be used from generated functions") if method_table === nothing table = Core.Compiler.InternalMethodTable(world) elseif method_table isa Core.MethodTable table = Core.Compiler.OverlayMethodTable(world, method_table) else table = method_table end match, = Core.Compiler.findsup(tt, table) if match === nothing raise && error("no unique matching method found for the specified argument types") return nothing end return match end """ which(f, types) Returns the method of `f` (a `Method` object) that would be called for arguments of the given `types`. If `types` is an abstract type, then the method that would be called by `invoke` is returned. See also: [`parentmodule`](@ref), and `@which` and `@edit` in [`InteractiveUtils`](@ref man-interactive-utils). """ function which(@nospecialize(f), @nospecialize(t)) tt = signature_type(f, t) return which(tt) end """ which(types::Type{<:Tuple}) Returns the method that would be called by the given type signature (as a tuple type). """ function which(@nospecialize(tt#=::Type=#)) return _which(tt).method end """ which(module, symbol) Return the module in which the binding for the variable referenced by `symbol` in `module` was created. """ function which(m::Module, s::Symbol) if !isdefined(m, s) error("\"$s\" is not defined in module $m") end return binding_module(m, s) end # function reflection """ nameof(f::Function) -> Symbol Get the name of a generic `Function` as a symbol. For anonymous functions, this is a compiler-generated name. For explicitly-declared subtypes of `Function`, it is the name of the function's type. """ function nameof(f::Function) t = typeof(f) mt = t.name.mt if mt === Symbol.name.mt # uses shared method table, so name is not unique to this function type return nameof(t) end return mt.name end function nameof(f::Core.IntrinsicFunction) name = ccall(:jl_intrinsic_name, Ptr{UInt8}, (Core.IntrinsicFunction,), f) return ccall(:jl_symbol, Ref{Symbol}, (Ptr{UInt8},), name) end """ parentmodule(f::Function) -> Module Determine the module containing the (first) definition of a generic function. """ parentmodule(f::Function) = parentmodule(typeof(f)) """ parentmodule(f::Function, types) -> Module Determine the module containing the first method of a generic function `f` matching the specified `types`. """ function parentmodule(@nospecialize(f), @nospecialize(types)) m = methods(f, types) if isempty(m) error("no matching methods") end return parentmodule(first(m)) end """ parentmodule(m::Method) -> Module Return the module in which the given method `m` is defined. !!! compat "Julia 1.9" Passing a `Method` as an argument requires Julia 1.9 or later. """ parentmodule(m::Method) = m.module """ hasmethod(f, t::Type{<:Tuple}[, kwnames]; world=get_world_counter()) -> Bool Determine whether the given generic function has a method matching the given `Tuple` of argument types with the upper bound of world age given by `world`. If a tuple of keyword argument names `kwnames` is provided, this also checks whether the method of `f` matching `t` has the given keyword argument names. If the matching method accepts a variable number of keyword arguments, e.g. with `kwargs...`, any names given in `kwnames` are considered valid. Otherwise the provided names must be a subset of the method's keyword arguments. See also [`applicable`](@ref). !!! compat "Julia 1.2" Providing keyword argument names requires Julia 1.2 or later. # Examples ```jldoctest julia> hasmethod(length, Tuple{Array}) true julia> f(; oranges=0) = oranges; julia> hasmethod(f, Tuple{}, (:oranges,)) true julia> hasmethod(f, Tuple{}, (:apples, :bananas)) false julia> g(; xs...) = 4; julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs true ``` """ function hasmethod(@nospecialize(f), @nospecialize(t)) return Core._hasmethod(f, t isa Type ? t : to_tuple_type(t)) end function Core.kwcall(kwargs::NamedTuple, ::typeof(hasmethod), @nospecialize(f), @nospecialize(t)) world = kwargs.world::UInt # make sure this is the only local, to avoid confusing kwarg_decl() return ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), signature_type(f, t), nothing, world) !== nothing end function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_counter()) @nospecialize isempty(kwnames) && return hasmethod(f, t; world) t = to_tuple_type(t) ft = Core.Typeof(f) u = unwrap_unionall(t)::DataType tt = rewrap_unionall(Tuple{typeof(Core.kwcall), NamedTuple, ft, u.parameters...}, t) match = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) match === nothing && return false kws = ccall(:jl_uncompress_argnames, Array{Symbol,1}, (Any,), (match::Method).slot_syms) isempty(kws) && return true # some kwfuncs simply forward everything directly for kw in kws endswith(String(kw), "...") && return true end kwnames = Symbol[kwnames[i] for i in 1:length(kwnames)] return issubset(kwnames, kws) end """ fbody = bodyfunction(basemethod::Method) Find the keyword "body function" (the function that contains the body of the method as written, called after all missing keyword-arguments have been assigned default values). `basemethod` is the method you obtain via [`which`](@ref) or [`methods`](@ref). """ function bodyfunction(basemethod::Method) fmod = parentmodule(basemethod) # The lowered code for `basemethod` should look like # %1 = mkw(kwvalues..., #self#, args...) # return %1 # where `mkw` is the name of the "active" keyword body-function. ast = uncompressed_ast(basemethod) if isa(ast, Core.CodeInfo) && length(ast.code) >= 2 callexpr = ast.code[end-1] if isa(callexpr, Expr) && callexpr.head === :call fsym = callexpr.args[1] while true if isa(fsym, Symbol) return getfield(fmod, fsym) elseif isa(fsym, GlobalRef) if fsym.mod === Core && fsym.name === :_apply fsym = callexpr.args[2] elseif fsym.mod === Core && fsym.name === :_apply_iterate fsym = callexpr.args[3] end if isa(fsym, Symbol) return getfield(fmod, fsym)::Function elseif isa(fsym, GlobalRef) return getfield(fsym.mod, fsym.name)::Function elseif isa(fsym, Core.SSAValue) fsym = ast.code[fsym.id] else return nothing end else return nothing end end end end return nothing end """ Base.isambiguous(m1, m2; ambiguous_bottom=false) -> Bool Determine whether two methods `m1` and `m2` may be ambiguous for some call signature. This test is performed in the context of other methods of the same function; in isolation, `m1` and `m2` might be ambiguous, but if a third method resolving the ambiguity has been defined, this returns `false`. Alternatively, in isolation `m1` and `m2` might be ordered, but if a third method cannot be sorted with them, they may cause an ambiguity together. For parametric types, the `ambiguous_bottom` keyword argument controls whether `Union{}` counts as an ambiguous intersection of type parameters โ€“ when `true`, it is considered ambiguous, when `false` it is not. # Examples ```jldoctest julia> foo(x::Complex{<:Integer}) = 1 foo (generic function with 1 method) julia> foo(x::Complex{<:Rational}) = 2 foo (generic function with 2 methods) julia> m1, m2 = collect(methods(foo)); julia> typeintersect(m1.sig, m2.sig) Tuple{typeof(foo), Complex{Union{}}} julia> Base.isambiguous(m1, m2, ambiguous_bottom=true) true julia> Base.isambiguous(m1, m2, ambiguous_bottom=false) false ``` """ function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false) m1 === m2 && return false ti = typeintersect(m1.sig, m2.sig) ti === Bottom && return false function inner(ti) ti === Bottom && return false if !ambiguous_bottom has_bottom_parameter(ti) && return false end world = get_world_counter() min = Ref{UInt}(typemin(UInt)) max = Ref{UInt}(typemax(UInt)) has_ambig = Ref{Int32}(0) ms = _methods_by_ftype(ti, nothing, -1, world, true, min, max, has_ambig)::Vector has_ambig[] == 0 && return false if !ambiguous_bottom filter!(ms) do m::Core.MethodMatch return !has_bottom_parameter(m.spec_types) end end # if ml-matches reported the existence of an ambiguity over their # intersection, see if both m1 and m2 seem to be involved in it # (if one was fully dominated by a different method, we want to will # report the other ambiguous pair) have_m1 = have_m2 = false for match in ms match = match::Core.MethodMatch m = match.method m === m1 && (have_m1 = true) m === m2 && (have_m2 = true) end if !have_m1 || !have_m2 # ml-matches did not need both methods to expose the reported ambiguity return false end if !ambiguous_bottom # since we're intentionally ignoring certain ambiguities (via the # filter call above), see if we can now declare the intersection fully # covered even though it is partially ambiguous over Union{} as a type # parameter somewhere minmax = nothing for match in ms m = match.method match.fully_covers || continue if minmax === nothing || morespecific(m.sig, minmax.sig) minmax = m end end if minmax === nothing || minmax == m1 || minmax == m2 return true end for match in ms m = match.method m === minmax && continue if !morespecific(minmax.sig, m.sig) if match.fully_covers || !morespecific(m.sig, minmax.sig) return true end end end return false end return true end if !(ti <: m1.sig && ti <: m2.sig) # When type-intersection fails, it's often also not commutative. Thus # checking the reverse may allow detecting ambiguity solutions # correctly in more cases (and faster). ti2 = typeintersect(m2.sig, m1.sig) if ti2 <: m1.sig && ti2 <: m2.sig ti = ti2 elseif ti != ti2 # TODO: this would be the more correct way to handle this case, but # people complained so we don't do it #inner(ti2) || return false # report that the type system failed to decide if it was ambiguous by saying they definitely are return false # report that the type system failed to decide if it was ambiguous by saying they definitely are not else return false # report that the type system failed to decide if it was ambiguous by saying they definitely are not end end inner(ti) || return false # otherwise type-intersection reported an ambiguity we couldn't solve return true end """ delete_method(m::Method) Make method `m` uncallable and force recompilation of any methods that use(d) it. """ function delete_method(m::Method) ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) end function get_methodtable(m::Method) mt = ccall(:jl_method_get_table, Any, (Any,), m) if mt === nothing return nothing end return mt::Core.MethodTable end """ has_bottom_parameter(t) -> Bool Determine whether `t` is a Type for which one or more of its parameters is `Union{}`. """ function has_bottom_parameter(t::DataType) for p in t.parameters has_bottom_parameter(p) && return true end return false end has_bottom_parameter(t::typeof(Bottom)) = true has_bottom_parameter(t::UnionAll) = has_bottom_parameter(unwrap_unionall(t)) has_bottom_parameter(t::Union) = has_bottom_parameter(t.a) & has_bottom_parameter(t.b) has_bottom_parameter(t::TypeVar) = has_bottom_parameter(t.ub) has_bottom_parameter(::Any) = false min_world(m::Core.CodeInstance) = m.min_world max_world(m::Core.CodeInstance) = m.max_world min_world(m::Core.CodeInfo) = m.min_world max_world(m::Core.CodeInfo) = m.max_world get_world_counter() = ccall(:jl_get_world_counter, UInt, ()) """ propertynames(x, private=false) Get a tuple or a vector of the properties (`x.property`) of an object `x`. This is typically the same as [`fieldnames(typeof(x))`](@ref), but types that overload [`getproperty`](@ref) should generally overload `propertynames` as well to get the properties of an instance of the type. `propertynames(x)` may return only "public" property names that are part of the documented interface of `x`. If you want it to also return "private" property names intended for internal use, pass `true` for the optional second argument. REPL tab completion on `x.` shows only the `private=false` properties. See also: [`hasproperty`](@ref), [`hasfield`](@ref). """ propertynames(x) = fieldnames(typeof(x)) propertynames(m::Module) = names(m) propertynames(x, private::Bool) = propertynames(x) # ignore private flag by default """ hasproperty(x, s::Symbol) Return a boolean indicating whether the object `x` has `s` as one of its own properties. !!! compat "Julia 1.2" This function requires at least Julia 1.2. See also: [`propertynames`](@ref), [`hasfield`](@ref). """ hasproperty(x, s::Symbol) = s in propertynames(x) """ @invoke f(arg::T, ...; kwargs...) Provides a convenient way to call [`invoke`](@ref) by expanding `@invoke f(arg1::T1, arg2::T2; kwargs...)` to `invoke(f, Tuple{T1,T2}, arg1, arg2; kwargs...)`. When an argument's type annotation is omitted, it's replaced with `Core.Typeof` that argument. To invoke a method where an argument is untyped or explicitly typed as `Any`, annotate the argument with `::Any`. It also supports the following syntax: - `@invoke (x::X).f` expands to `invoke(getproperty, Tuple{X,Symbol}, x, :f)` - `@invoke (x::X).f = v::V` expands to `invoke(setproperty!, Tuple{X,Symbol,V}, x, :f, v)` - `@invoke (xs::Xs)[i::I]` expands to `invoke(getindex, Tuple{Xs,I}, xs, i)` - `@invoke (xs::Xs)[i::I] = v::V` expands to `invoke(setindex!, Tuple{Xs,V,I}, xs, v, i)` # Examples ```jldoctest julia> @macroexpand @invoke f(x::T, y) :(Core.invoke(f, Tuple{T, Core.Typeof(y)}, x, y)) julia> @invoke 420::Integer % Unsigned 0x00000000000001a4 julia> @macroexpand @invoke (x::X).f :(Core.invoke(Base.getproperty, Tuple{X, Core.Typeof(:f)}, x, :f)) julia> @macroexpand @invoke (x::X).f = v::V :(Core.invoke(Base.setproperty!, Tuple{X, Core.Typeof(:f), V}, x, :f, v)) julia> @macroexpand @invoke (xs::Xs)[i::I] :(Core.invoke(Base.getindex, Tuple{Xs, I}, xs, i)) julia> @macroexpand @invoke (xs::Xs)[i::I] = v::V :(Core.invoke(Base.setindex!, Tuple{Xs, V, I}, xs, v, i)) ``` !!! compat "Julia 1.7" This macro requires Julia 1.7 or later. !!! compat "Julia 1.9" This macro is exported as of Julia 1.9. !!! compat "Julia 1.10" The additional syntax is supported as of Julia 1.10. """ macro invoke(ex) topmod = Core.Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally f, args, kwargs = destructure_callex(topmod, ex) types = Expr(:curly, :Tuple) out = Expr(:call, GlobalRef(Core, :invoke)) isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) push!(out.args, f) push!(out.args, types) for arg in args if isexpr(arg, :(::)) push!(out.args, arg.args[1]) push!(types.args, arg.args[2]) else push!(out.args, arg) push!(types.args, Expr(:call, GlobalRef(Core, :Typeof), arg)) end end return esc(out) end """ @invokelatest f(args...; kwargs...) Provides a convenient way to call [`invokelatest`](@ref). `@invokelatest f(args...; kwargs...)` will simply be expanded into `Base.invokelatest(f, args...; kwargs...)`. It also supports the following syntax: - `@invokelatest x.f` expands to `Base.invokelatest(getproperty, x, :f)` - `@invokelatest x.f = v` expands to `Base.invokelatest(setproperty!, x, :f, v)` - `@invokelatest xs[i]` expands to `Base.invokelatest(getindex, xs, i)` - `@invokelatest xs[i] = v` expands to `Base.invokelatest(setindex!, xs, v, i)` ```jldoctest julia> @macroexpand @invokelatest f(x; kw=kwv) :(Base.invokelatest(f, x; kw = kwv)) julia> @macroexpand @invokelatest x.f :(Base.invokelatest(Base.getproperty, x, :f)) julia> @macroexpand @invokelatest x.f = v :(Base.invokelatest(Base.setproperty!, x, :f, v)) julia> @macroexpand @invokelatest xs[i] :(Base.invokelatest(Base.getindex, xs, i)) julia> @macroexpand @invokelatest xs[i] = v :(Base.invokelatest(Base.setindex!, xs, v, i)) ``` !!! compat "Julia 1.7" This macro requires Julia 1.7 or later. !!! compat "Julia 1.9" Prior to Julia 1.9, this macro was not exported, and was called as `Base.@invokelatest`. !!! compat "Julia 1.10" The additional `x.f` and `xs[i]` syntax requires Julia 1.10. """ macro invokelatest(ex) topmod = Core.Compiler._topmod(__module__) # well, except, do not get it via CC but define it locally f, args, kwargs = destructure_callex(topmod, ex) out = Expr(:call, GlobalRef(Base, :invokelatest)) isempty(kwargs) || push!(out.args, Expr(:parameters, kwargs...)) push!(out.args, f) append!(out.args, args) return esc(out) end function destructure_callex(topmod::Module, @nospecialize(ex)) function flatten(xs) out = Any[] for x in xs if isexpr(x, :tuple) append!(out, x.args) else push!(out, x) end end return out end kwargs = Any[] if isexpr(ex, :call) # `f(args...)` f = first(ex.args) args = Any[] for x in ex.args[2:end] if isexpr(x, :parameters) append!(kwargs, x.args) elseif isexpr(x, :kw) push!(kwargs, x) else push!(args, x) end end elseif isexpr(ex, :.) # `x.f` f = GlobalRef(topmod, :getproperty) args = flatten(ex.args) elseif isexpr(ex, :ref) # `x[i]` f = GlobalRef(topmod, :getindex) args = flatten(ex.args) elseif isexpr(ex, :(=)) # `x.f = v` or `x[i] = v` lhs, rhs = ex.args if isexpr(lhs, :.) f = GlobalRef(topmod, :setproperty!) args = flatten(Any[lhs.args..., rhs]) elseif isexpr(lhs, :ref) f = GlobalRef(topmod, :setindex!) args = flatten(Any[lhs.args[1], rhs, lhs.args[2]]) else throw(ArgumentError("expected a `setproperty!` expression `x.f = v` or `setindex!` expression `x[i] = v`")) end else throw(ArgumentError("expected a `:call` expression `f(args...; kwargs...)`")) end return f, args, kwargs end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/options.jl~ # This file is a part of Julia. License is MIT: https://julialang.org/license # NOTE: This type needs to be kept in sync with jl_options in src/jloptions.h struct JLOptions quiet::Int8 banner::Int8 julia_bindir::Ptr{UInt8} julia_bin::Ptr{UInt8} commands::Ptr{Ptr{UInt8}} # (e)eval, (E)print, (L)load image_file::Ptr{UInt8} cpu_target::Ptr{UInt8} nthreadpools::Int16 nthreads::Int16 nmarkthreads::Int16 nsweepthreads::Int8 nthreads_per_pool::Ptr{Int16} nprocs::Int32 machine_file::Ptr{UInt8} project::Ptr{UInt8} isinteractive::Int8 color::Int8 historyfile::Int8 startupfile::Int8 compile_enabled::Int8 code_coverage::Int8 malloc_log::Int8 tracked_path::Ptr{UInt8} opt_level::Int8 opt_level_min::Int8 debug_level::Int8 check_bounds::Int8 depwarn::Int8 warn_overwrite::Int8 can_inline::Int8 polly::Int8 trace_compile::Ptr{UInt8} fast_math::Int8 worker::Int8 cookie::Ptr{UInt8} handle_signals::Int8 use_sysimage_native_code::Int8 use_compiled_modules::Int8 use_pkgimages::Int8 bindto::Ptr{UInt8} outputbc::Ptr{UInt8} outputunoptbc::Ptr{UInt8} outputo::Ptr{UInt8} outputasm::Ptr{UInt8} outputji::Ptr{UInt8} output_code_coverage::Ptr{UInt8} incremental::Int8 image_file_specified::Int8 warn_scope::Int8 image_codegen::Int8 rr_detach::Int8 strip_metadata::Int8 strip_ir::Int8 permalloc_pkgimg::Int8 heap_size_hint::UInt64 end # This runs early in the sysimage != is not defined yet if sizeof(JLOptions) === ccall(:jl_sizeof_jl_options, Int, ()) else ccall(:jl_throw, Cvoid, (Any,), "Option structure mismatch") end JLOptions() = unsafe_load(cglobal(:jl_options, JLOptions)) function show(io::IO, opt::JLOptions) print(io, "JLOptions(") fields = fieldnames(JLOptions) nfields = length(fields) for (i, f) in enumerate(fields) v = getfield(opt, i) if isa(v, Ptr{UInt8}) v = (v != C_NULL) ? unsafe_string(v) : "" elseif isa(v, Ptr{Ptr{UInt8}}) v = unsafe_load_commands(v) end print(io, f, " = ", repr(v), i < nfields ? ", " : "") end print(io, ")") end function unsafe_load_commands(v::Ptr{Ptr{UInt8}}) cmds = Pair{Char, String}[] v == C_NULL && return cmds i = 1 while true s = unsafe_load(v, i) s == C_NULL && break e = Char(unsafe_load(s)) push!(cmds, e => unsafe_string(s + 1)) i += 1 end return cmds end function is_file_tracked(file::Symbol) return ccall(:jl_is_file_tracked, Cint, (Any,), file) == 1 end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/promotion.jl@# This file is a part of Julia. License is MIT: https://julialang.org/license ## type join (closest common ancestor, or least upper bound) ## """ typejoin(T, S, ...) Return the closest common ancestor of types `T` and `S`, i.e. the narrowest type from which they both inherit. Recurses on additional varargs. # Examples ```jldoctest julia> typejoin(Int, Float64) Real julia> typejoin(Int, Float64, ComplexF32) Number ``` """ typejoin() = Bottom typejoin(@nospecialize(t)) = t typejoin(@nospecialize(t), ts...) = (@_foldable_meta; typejoin(t, typejoin(ts...))) function typejoin(@nospecialize(a), @nospecialize(b)) @_foldable_meta if isa(a, TypeVar) return typejoin(a.ub, b) elseif isa(b, TypeVar) return typejoin(a, b.ub) elseif a <: b return b elseif b <: a return a elseif isa(a, UnionAll) return UnionAll(a.var, typejoin(a.body, b)) elseif isa(b, UnionAll) return UnionAll(b.var, typejoin(a, b.body)) elseif isa(a, Union) return typejoin(typejoin(a.a, a.b), b) elseif isa(b, Union) return typejoin(a, typejoin(b.a, b.b)) end # a and b are DataTypes # We have to hide Constant info from inference, see #44390 a, b = inferencebarrier(a)::DataType, inferencebarrier(b)::DataType if a <: Tuple if !(b <: Tuple) return Any end ap, bp = a.parameters, b.parameters lar = length(ap) lbr = length(bp) if lar == 0 return Tuple{Vararg{tailjoin(bp, 1)}} end if lbr == 0 return Tuple{Vararg{tailjoin(ap, 1)}} end laf, afixed = full_va_len(ap) lbf, bfixed = full_va_len(bp) if laf < lbf if isvarargtype(ap[lar]) && !afixed c = Vector{Any}(undef, laf) c[laf] = Vararg{typejoin(unwrapva(ap[lar]), tailjoin(bp, laf))} n = laf-1 else c = Vector{Any}(undef, laf+1) c[laf+1] = Vararg{tailjoin(bp, laf+1)} n = laf end elseif lbf < laf if isvarargtype(bp[lbr]) && !bfixed c = Vector{Any}(undef, lbf) c[lbf] = Vararg{typejoin(unwrapva(bp[lbr]), tailjoin(ap, lbf))} n = lbf-1 else c = Vector{Any}(undef, lbf+1) c[lbf+1] = Vararg{tailjoin(ap, lbf+1)} n = lbf end else c = Vector{Any}(undef, laf) n = laf end for i = 1:n ai = ap[min(i,lar)]; bi = bp[min(i,lbr)] ci = typejoin(unwrapva(ai), unwrapva(bi)) c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci end return Tuple{c...} elseif b <: Tuple return Any end while b !== Any if a <: b.name.wrapper while a.name !== b.name a = supertype(a)::DataType end if a.name === Type.body.name ap = a.parameters[1] bp = b.parameters[1] if ((isa(ap,TypeVar) && ap.lb === Bottom && ap.ub === Any) || (isa(bp,TypeVar) && bp.lb === Bottom && bp.ub === Any)) # handle special Type{T} supertype return Type end end aprimary = a.name.wrapper # join on parameters n = length(a.parameters) if n == 0 return aprimary end vars = [] for i = 1:n ai, bi = a.parameters[i], b.parameters[i] if ai === bi || (isa(ai,Type) && isa(bi,Type) && ai <: bi && bi <: ai) aprimary = aprimary{ai} else aprimary = aprimary::UnionAll # pushfirst!(vars, aprimary.var) _growbeg!(vars, 1) arrayset(false, vars, aprimary.var, 1) aprimary = aprimary.body end end for v in vars aprimary = UnionAll(v, aprimary) end return aprimary end b = supertype(b)::DataType end return Any end # return an upper-bound on type `a` with type `b` removed # such that `return <: a` && `Union{return, b} == Union{a, b}` # WARNING: this is wrong for some objects for which subtyping is broken # (Core.Compiler.isnotbrokensubtype), use only simple types for `b` function typesplit(@nospecialize(a), @nospecialize(b)) @_foldable_meta if a <: b return Bottom end if isa(a, Union) return Union{typesplit(a.a, b), typesplit(a.b, b)} end return a end """ promote_typejoin(T, S) Compute a type that contains both `T` and `S`, which could be either a parent of both types, or a `Union` if appropriate. Falls back to [`typejoin`](@ref). See instead [`promote`](@ref), [`promote_type`](@ref). # Examples ```jldoctest julia> Base.promote_typejoin(Int, Float64) Real julia> Base.promote_type(Int, Float64) Float64 ``` """ function promote_typejoin(@nospecialize(a), @nospecialize(b)) c = typejoin(_promote_typesubtract(a), _promote_typesubtract(b)) return Union{a, b, c}::Type end _promote_typesubtract(@nospecialize(a)) = a === Any ? a : a >: Union{Nothing, Missing} ? typesplit(a, Union{Nothing, Missing}) : a >: Nothing ? typesplit(a, Nothing) : a >: Missing ? typesplit(a, Missing) : a function promote_typejoin_union(::Type{T}) where T if T === Union{} return Union{} elseif T isa UnionAll return Any # TODO: compute more precise bounds elseif T isa Union return promote_typejoin(promote_typejoin_union(T.a), promote_typejoin_union(T.b)) elseif T isa DataType T <: Tuple && return typejoin_union_tuple(T) return T else error("unreachable") # not a type?? end end function typejoin_union_tuple(T::DataType) @_foldable_meta u = Base.unwrap_unionall(T) p = (u::DataType).parameters lr = length(p)::Int if lr == 0 return Tuple{} end c = Vector{Any}(undef, lr) for i = 1:lr pi = p[i] U = Core.Compiler.unwrapva(pi) if U === Union{} ci = Union{} elseif U isa Union ci = typejoin(U.a, U.b) elseif U isa UnionAll return Any # TODO: compute more precise bounds else ci = promote_typejoin_union(U) end if i == lr && Core.Compiler.isvarargtype(pi) c[i] = isdefined(pi, :N) ? Vararg{ci, pi.N} : Vararg{ci} else c[i] = ci end end return Base.rewrap_unionall(Tuple{c...}, T) end # Returns length, isfixed function full_va_len(p::Core.SimpleVector) isempty(p) && return 0, true last = p[end] if isvarargtype(last) if isdefined(last, :N) N = last.N isa(N, Int) && return length(p) + N - 1, true end return length(p), false end return length(p), true end # reduce typejoin over A[i:end] function tailjoin(A, i) if i > length(A) return unwrapva(A[end]) end t = Bottom for j = i:length(A) t = typejoin(t, unwrapva(A[j])) end return t end ## promotion mechanism ## """ promote_type(type1, type2, ...) Promotion refers to converting values of mixed types to a single common type. `promote_type` represents the default promotion behavior in Julia when operators (usually mathematical) are given arguments of differing types. `promote_type` generally tries to return a type which can at least approximate most values of either input type without excessively widening. Some loss is tolerated; for example, `promote_type(Int64, Float64)` returns [`Float64`](@ref) even though strictly, not all [`Int64`](@ref) values can be represented exactly as `Float64` values. See also: [`promote`](@ref), [`promote_typejoin`](@ref), [`promote_rule`](@ref). # Examples ```jldoctest julia> promote_type(Int64, Float64) Float64 julia> promote_type(Int32, Int64) Int64 julia> promote_type(Float32, BigInt) BigFloat julia> promote_type(Int16, Float16) Float16 julia> promote_type(Int64, Float16) Float16 julia> promote_type(Int8, UInt16) UInt16 ``` !!! warning "Don't overload this directly" To overload promotion for your own types you should overload [`promote_rule`](@ref). `promote_type` calls `promote_rule` internally to determine the type. Overloading `promote_type` directly can cause ambiguity errors. """ function promote_type end promote_type() = Bottom promote_type(T) = T promote_type(T, S, U, V...) = (@inline; promote_type(T, promote_type(S, U, V...))) promote_type(::Type{Bottom}, ::Type{Bottom}) = Bottom promote_type(::Type{T}, ::Type{T}) where {T} = T promote_type(::Type{T}, ::Type{Bottom}) where {T} = T promote_type(::Type{Bottom}, ::Type{T}) where {T} = T function promote_type(::Type{T}, ::Type{S}) where {T,S} @inline # Try promote_rule in both orders. Typically only one is defined, # and there is a fallback returning Bottom below, so the common case is # promote_type(T, S) => # promote_result(T, S, result, Bottom) => # typejoin(result, Bottom) => result promote_result(T, S, promote_rule(T,S), promote_rule(S,T)) end """ promote_rule(type1, type2) Specifies what type should be used by [`promote`](@ref) when given values of types `type1` and `type2`. This function should not be called directly, but should have definitions added to it for new types as appropriate. """ function promote_rule end promote_rule(::Type, ::Type) = Bottom # Define some methods to avoid needing to enumerate unrelated possibilities when presented # with Type{<:T}, and return a value in general accordance with the result given by promote_type promote_rule(::Type{Bottom}, slurp...) = Bottom promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly necessary, since the next method would match unambiguously anyways promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) # If no promote_rule is defined, both directions give Bottom. In that # case use typejoin on the original types instead. promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@inline; typejoin(T, S)) """ promote(xs...) Convert all arguments to a common type, and return them all (as a tuple). If no arguments can be converted, an error is raised. See also: [`promote_type`](@ref), [`promote_rule`](@ref). # Examples ```jldoctest julia> promote(Int8(1), Float16(4.5), Float32(4.1)) (1.0f0, 4.5f0, 4.1f0) julia> promote_type(Int8, Float16, Float32) Float32 julia> reduce(Base.promote_typejoin, (Int8, Float16, Float32)) Real julia> promote(1, "x") ERROR: promotion of types Int64 and String failed to change any arguments [...] julia> promote_type(Int, String) Any ``` """ function promote end function _promote(x::T, y::S) where {T,S} @inline R = promote_type(T, S) return (convert(R, x), convert(R, y)) end promote_typeof(x) = typeof(x) promote_typeof(x, xs...) = (@inline; promote_type(typeof(x), promote_typeof(xs...))) function _promote(x, y, z) @inline R = promote_typeof(x, y, z) return (convert(R, x), convert(R, y), convert(R, z)) end function _promote(x, y, zs...) @inline R = promote_typeof(x, y, zs...) return (convert(R, x), convert(R, y), convert(Tuple{Vararg{R}}, zs)...) end # TODO: promote(x::T, ys::T...) where {T} here to catch all circularities? ## promotions in arithmetic, etc. ## promote() = () promote(x) = (x,) function promote(x, y) @inline px, py = _promote(x, y) not_sametype((x,y), (px,py)) px, py end function promote(x, y, z) @inline px, py, pz = _promote(x, y, z) not_sametype((x,y,z), (px,py,pz)) px, py, pz end function promote(x, y, z, a...) p = _promote(x, y, z, a...) not_sametype((x, y, z, a...), p) p end promote(x::T, y::T, zs::T...) where {T} = (x, y, zs...) not_sametype(x::T, y::T) where {T} = sametype_error(x) not_sametype(x, y) = nothing function sametype_error(input) @noinline error("promotion of types ", join(map(x->string(typeof(x)), input), ", ", " and "), " failed to change any arguments") end +(x::Number, y::Number) = +(promote(x,y)...) *(x::Number, y::Number) = *(promote(x,y)...) -(x::Number, y::Number) = -(promote(x,y)...) /(x::Number, y::Number) = /(promote(x,y)...) """ ^(x, y) Exponentiation operator. If `x` is a matrix, computes matrix exponentiation. If `y` is an `Int` literal (e.g. `2` in `x^2` or `-3` in `x^-3`), the Julia code `x^y` is transformed by the compiler to `Base.literal_pow(^, x, Val(y))`, to enable compile-time specialization on the value of the exponent. (As a default fallback we have `Base.literal_pow(^, x, Val(y)) = ^(x,y)`, where usually `^ == Base.^` unless `^` has been defined in the calling namespace.) If `y` is a negative integer literal, then `Base.literal_pow` transforms the operation to `inv(x)^-y` by default, where `-y` is positive. # Examples ```jldoctest julia> 3^5 243 julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> A^3 2ร—2 Matrix{Int64}: 37 54 81 118 ``` """ ^(x::Number, y::Number) = ^(promote(x,y)...) fma(x::Number, y::Number, z::Number) = fma(promote(x,y,z)...) muladd(x::Number, y::Number, z::Number) = muladd(promote(x,y,z)...) ==(x::Number, y::Number) = (==)(promote(x,y)...) <( x::Real, y::Real) = (< )(promote(x,y)...) <=(x::Real, y::Real) = (<=)(promote(x,y)...) rem(x::Real, y::Real) = rem(promote(x,y)...) mod(x::Real, y::Real) = mod(promote(x,y)...) mod1(x::Real, y::Real) = mod1(promote(x,y)...) fld1(x::Real, y::Real) = fld1(promote(x,y)...) max(x::Real, y::Real) = max(promote(x,y)...) min(x::Real, y::Real) = min(promote(x,y)...) minmax(x::Real, y::Real) = minmax(promote(x, y)...) if isdefined(Core, :Compiler) const _return_type = Core.Compiler.return_type else _return_type(@nospecialize(f), @nospecialize(t)) = Any end function TupleOrBottom(tt...) any(p -> p === Union{}, tt) && return Union{} return Tuple{tt...} end """ promote_op(f, argtypes...) Guess what an appropriate container eltype would be for storing results of `f(::argtypes...)`. The guess is in part based on type inference, so can change any time. !!! warning Due to its fragility, use of `promote_op` should be avoided. It is preferable to base the container eltype on the type of the actual elements. Only in the absence of any elements (for an empty result container), it may be unavoidable to call `promote_op`. """ function promote_op(f, S::Type...) argT = TupleOrBottom(S...) argT === Union{} && return Union{} return _return_type(f, argT) end ## catch-alls to prevent infinite recursion when definitions are missing ## no_op_err(name, T) = error(name," not defined for ",T) (+)(x::T, y::T) where {T<:Number} = no_op_err("+", T) (*)(x::T, y::T) where {T<:Number} = no_op_err("*", T) (-)(x::T, y::T) where {T<:Number} = no_op_err("-", T) (/)(x::T, y::T) where {T<:Number} = no_op_err("/", T) (^)(x::T, y::T) where {T<:Number} = no_op_err("^", T) fma(x::T, y::T, z::T) where {T<:Number} = no_op_err("fma", T) fma(x::Integer, y::Integer, z::Integer) = x*y+z muladd(x::T, y::T, z::T) where {T<:Number} = x*y+z (&)(x::T, y::T) where {T<:Integer} = no_op_err("&", T) (|)(x::T, y::T) where {T<:Integer} = no_op_err("|", T) xor(x::T, y::T) where {T<:Integer} = no_op_err("xor", T) (==)(x::T, y::T) where {T<:Number} = x === y (< )(x::T, y::T) where {T<:Real} = no_op_err("<" , T) (<=)(x::T, y::T) where {T<:Real} = (x == y) | (x < y) rem(x::T, y::T) where {T<:Real} = no_op_err("rem", T) mod(x::T, y::T) where {T<:Real} = no_op_err("mod", T) min(x::Real) = x max(x::Real) = x minmax(x::Real) = (x, x) max(x::T, y::T) where {T<:Real} = ifelse(y < x, x, y) min(x::T, y::T) where {T<:Real} = ifelse(y < x, y, x) minmax(x::T, y::T) where {T<:Real} = y < x ? (y, x) : (x, y) flipsign(x::T, y::T) where {T<:Signed} = no_op_err("flipsign", T) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/tuple.jl2K# This file is a part of Julia. License is MIT: https://julialang.org/license # Document NTuple here where we have everything needed for the doc system """ NTuple{N, T} A compact way of representing the type for a tuple of length `N` where all elements are of type `T`. # Examples ```jldoctest julia> isa((1, 2, 3, 4, 5, 6), NTuple{6, Int}) true ``` See also [`ntuple`](@ref). """ NTuple # convenience function for extracting N from a Tuple (if defined) # else return `nothing` for anything else given (such as Vararg or other non-sized Union) _counttuple(::Type{<:NTuple{N,Any}}) where {N} = N _counttuple(::Type) = nothing ## indexing ## length(@nospecialize t::Tuple) = nfields(t) firstindex(@nospecialize t::Tuple) = 1 lastindex(@nospecialize t::Tuple) = length(t) size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) axes(@nospecialize t::Tuple) = (OneTo(length(t)),) @eval getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, $(Expr(:boundscheck))) @eval getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), $(Expr(:boundscheck))) __inbounds_getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, false) __inbounds_getindex(@nospecialize(t::Tuple), i::Integer) = getfield(t, convert(Int, i), false) getindex(t::Tuple, r::AbstractArray{<:Any,1}) = (eltype(t)[t[ri] for ri in r]...,) getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, findall(b)) : throw(BoundsError(t, b)) getindex(t::Tuple, c::Colon) = t get(t::Tuple, i::Integer, default) = i in 1:length(t) ? getindex(t, i) : default get(f::Callable, t::Tuple, i::Integer) = i in 1:length(t) ? getindex(t, i) : f() # returns new tuple; N.B.: becomes no-op if `i` is out-of-bounds """ setindex(c::Tuple, v, i::Integer) Creates a new tuple similar to `x` with the value at index `i` set to `v`. Throws a `BoundsError` when out of bounds. # Examples ```jldoctest julia> Base.setindex((1, 2, 6), 2, 3) == (1, 2, 2) true ``` """ function setindex(x::Tuple, v, i::Integer) @boundscheck 1 <= i <= length(x) || throw(BoundsError(x, i)) @inline _setindex(v, i, x...) end function _setindex(v, i::Integer, args::Vararg{Any,N}) where {N} @inline return ntuple(j -> ifelse(j == i, v, args[j]), Val{N}()) end ## iterating ## function iterate(@nospecialize(t::Tuple), i::Int=1) @inline return (1 <= i <= length(t)) ? (t[i], i + 1) : nothing end keys(@nospecialize t::Tuple) = OneTo(length(t)) prevind(@nospecialize(t::Tuple), i::Integer) = Int(i)-1 nextind(@nospecialize(t::Tuple), i::Integer) = Int(i)+1 function keys(t::Tuple, t2::Tuple...) @inline OneTo(_maxlength(t, t2...)) end _maxlength(t::Tuple) = length(t) function _maxlength(t::Tuple, t2::Tuple, t3::Tuple...) @inline max(length(t), _maxlength(t2, t3...)) end # this allows partial evaluation of bounded sequences of next() calls on tuples, # while reducing to plain next() for arbitrary iterables. indexed_iterate(t::Tuple, i::Int, state=1) = (@inline; (getfield(t, i), i+1)) indexed_iterate(a::Array, i::Int, state=1) = (@inline; (a[i], i+1)) function indexed_iterate(I, i) x = iterate(I) x === nothing && throw(BoundsError(I, i)) x end function indexed_iterate(I, i, state) x = iterate(I, state) x === nothing && throw(BoundsError(I, i)) x end """ Base.rest(collection[, itr_state]) Generic function for taking the tail of `collection`, starting from a specific iteration state `itr_state`. Return a `Tuple`, if `collection` itself is a `Tuple`, a subtype of `AbstractVector`, if `collection` is an `AbstractArray`, a subtype of `AbstractString` if `collection` is an `AbstractString`, and an arbitrary iterator, falling back to `Iterators.rest(collection[, itr_state])`, otherwise. Can be overloaded for user-defined collection types to customize the behavior of [slurping in assignments](@ref destructuring-assignment) in final position, like `a, b... = collection`. !!! compat "Julia 1.6" `Base.rest` requires at least Julia 1.6. See also: [`first`](@ref first), [`Iterators.rest`](@ref), [`Base.split_rest`](@ref). # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> first, state = iterate(a) (1, 2) julia> first, Base.rest(a, state) (1, [3, 2, 4]) ``` """ function rest end rest(t::Tuple) = t rest(t::Tuple, i::Int) = ntuple(x -> getfield(t, x+i-1), length(t)-i+1) rest(a::Array, i::Int=1) = a[i:end] rest(a::Core.SimpleVector, i::Int=1) = a[i:end] rest(itr, state...) = Iterators.rest(itr, state...) """ Base.split_rest(collection, n::Int[, itr_state]) -> (rest_but_n, last_n) Generic function for splitting the tail of `collection`, starting from a specific iteration state `itr_state`. Returns a tuple of two new collections. The first one contains all elements of the tail but the `n` last ones, which make up the second collection. The type of the first collection generally follows that of [`Base.rest`](@ref), except that the fallback case is not lazy, but is collected eagerly into a vector. Can be overloaded for user-defined collection types to customize the behavior of [slurping in assignments](@ref destructuring-assignment) in non-final position, like `a, b..., c = collection`. !!! compat "Julia 1.9" `Base.split_rest` requires at least Julia 1.9. See also: [`Base.rest`](@ref). # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> first, state = iterate(a) (1, 2) julia> first, Base.split_rest(a, 1, state) (1, ([3, 2], [4])) ``` """ function split_rest end function split_rest(itr, n::Int, state...) if IteratorSize(itr) == IsInfinite() throw(ArgumentError("Cannot split an infinite iterator in the middle.")) end return _split_rest(rest(itr, state...), n) end _split_rest(itr, n::Int) = _split_rest(collect(itr), n) function _check_length_split_rest(len, n) len < n && throw(ArgumentError( "The iterator only contains $len elements, but at least $n were requested." )) end function _split_rest(a::Union{AbstractArray, Core.SimpleVector}, n::Int) _check_length_split_rest(length(a), n) return a[begin:end-n], a[end-n+1:end] end @eval split_rest(t::Tuple, n::Int, i=1) = ($(Expr(:meta, :aggressive_constprop)); (t[i:end-n], t[end-n+1:end])) # Use dispatch to avoid a branch in first first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty")) first(t::Tuple) = t[1] # eltype eltype(::Type{Tuple{}}) = Bottom function eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} if @isdefined(E) return E else # TODO: need to guard against E being miscomputed by subtyping (ref #23017) # and compute the result manually in this case return _compute_eltype(t) end end eltype(t::Type{<:Tuple}) = _compute_eltype(t) function _tuple_unique_fieldtypes(@nospecialize t) @_total_meta types = IdSet() tยด = unwrap_unionall(t) # Given t = Tuple{Vararg{S}} where S<:Real, the various # unwrapping/wrapping/va-handling here will return Real if tยด isa Union union!(types, _tuple_unique_fieldtypes(rewrap_unionall(tยด.a, t))) union!(types, _tuple_unique_fieldtypes(rewrap_unionall(tยด.b, t))) else for ti in (tยด::DataType).parameters push!(types, rewrap_unionall(unwrapva(ti), t)) end end return Core.svec(types...) end function _compute_eltype(@nospecialize t) @_total_meta # TODO: the compiler shouldn't need this types = _tuple_unique_fieldtypes(t) return afoldl(types...) do a, b # if we've already reached Any, it can't widen any more a === Any && return Any b === Any && return Any return promote_typejoin(a, b) end end # We'd like to be able to infer eltype(::Tuple), which needs to be able to # look at these four methods: # # julia> methods(Base.eltype, Tuple{Type{<:Tuple}}) # 4 methods for generic function "eltype" from Base: # [1] eltype(::Type{Union{}}) # @ abstractarray.jl:234 # [2] eltype(::Type{Tuple{}}) # @ tuple.jl:199 # [3] eltype(t::Type{<:Tuple{Vararg{E}}}) where E # @ tuple.jl:200 # [4] eltype(t::Type{<:Tuple}) # @ tuple.jl:209 typeof(function eltype end).name.max_methods = UInt8(4) # version of tail that doesn't throw on empty tuples (used in array indexing) safe_tail(t::Tuple) = tail(t) safe_tail(t::Tuple{}) = () # front (the converse of tail: it skips the last entry) """ front(x::Tuple)::Tuple Return a `Tuple` consisting of all but the last component of `x`. See also: [`first`](@ref), [`tail`](@ref Base.tail). # Examples ```jldoctest julia> Base.front((1,2,3)) (1, 2) julia> Base.front(()) ERROR: ArgumentError: Cannot call front on an empty tuple. ``` """ function front(t::Tuple) @inline _front(t...) end _front() = throw(ArgumentError("Cannot call front on an empty tuple.")) _front(v) = () function _front(v, t...) @inline (v, _front(t...)...) end ## mapping ## # 1 argument function map(f, t::Tuple{}) = () map(f, t::Tuple{Any,}) = (@inline; (f(t[1]),)) map(f, t::Tuple{Any, Any}) = (@inline; (f(t[1]), f(t[2]))) map(f, t::Tuple{Any, Any, Any}) = (@inline; (f(t[1]), f(t[2]), f(t[3]))) map(f, t::Tuple) = (@inline; (f(t[1]), map(f,tail(t))...)) # stop inlining after some number of arguments to avoid code blowup const Any32{N} = Tuple{Any,Any,Any,Any,Any,Any,Any,Any, Any,Any,Any,Any,Any,Any,Any,Any, Any,Any,Any,Any,Any,Any,Any,Any, Any,Any,Any,Any,Any,Any,Any,Any, Vararg{Any,N}} const All32{T,N} = Tuple{T,T,T,T,T,T,T,T, T,T,T,T,T,T,T,T, T,T,T,T,T,T,T,T, T,T,T,T,T,T,T,T, Vararg{T,N}} function map(f, t::Any32) n = length(t) A = Vector{Any}(undef, n) for i=1:n A[i] = f(t[i]) end (A...,) end # 2 argument function map(f, t::Tuple{}, s::Tuple{}) = () map(f, t::Tuple, s::Tuple{}) = () map(f, t::Tuple{}, s::Tuple) = () map(f, t::Tuple{Any,}, s::Tuple{Any,}) = (@inline; (f(t[1],s[1]),)) map(f, t::Tuple{Any,Any}, s::Tuple{Any,Any}) = (@inline; (f(t[1],s[1]), f(t[2],s[2]))) function map(f, t::Tuple, s::Tuple) @inline (f(t[1],s[1]), map(f, tail(t), tail(s))...) end function map(f, t::Any32, s::Any32) n = min(length(t), length(s)) A = Vector{Any}(undef, n) for i = 1:n A[i] = f(t[i], s[i]) end (A...,) end # n argument function heads(ts::Tuple...) = map(t -> t[1], ts) tails(ts::Tuple...) = map(tail, ts) map(f, ::Tuple{}...) = () anyempty(x::Tuple{}, xs...) = true anyempty(x::Tuple, xs...) = anyempty(xs...) anyempty() = false function map(f, t1::Tuple, t2::Tuple, ts::Tuple...) @inline anyempty(t1, t2, ts...) && return () (f(heads(t1, t2, ts...)...), map(f, tails(t1, t2, ts...)...)...) end function map(f, t1::Any32, t2::Any32, ts::Any32...) n = min(length(t1), length(t2), minimum(length, ts)) A = Vector{Any}(undef, n) for i = 1:n A[i] = f(t1[i], t2[i], map(t -> t[i], ts)...) end (A...,) end # type-stable padding fill_to_length(t::NTuple{N,Any}, val, ::Val{N}) where {N} = t fill_to_length(t::Tuple{}, val, ::Val{1}) = (val,) fill_to_length(t::Tuple{Any}, val, ::Val{2}) = (t..., val) fill_to_length(t::Tuple{}, val, ::Val{2}) = (val, val) #function fill_to_length(t::Tuple, val, ::Val{N}) where {N} # @inline # return (t..., ntuple(i -> val, N - length(t))...) #end # constructing from an iterator # only define these in Base, to avoid overwriting the constructors # NOTE: this means this constructor must be avoided in Core.Compiler! if nameof(@__MODULE__) === :Base function tuple_type_tail(T::Type) @_foldable_meta # TODO: this method is wrong (and not :foldable) if isa(T, UnionAll) return UnionAll(T.var, tuple_type_tail(T.body)) elseif isa(T, Union) return Union{tuple_type_tail(T.a), tuple_type_tail(T.b)} else T.name === Tuple.name || throw(MethodError(tuple_type_tail, (T,))) if isvatuple(T) && length(T.parameters) == 1 va = unwrap_unionall(T.parameters[1])::Core.TypeofVararg (isdefined(va, :N) && isa(va.N, Int)) || return T return Tuple{Vararg{va.T, va.N-1}} end return Tuple{argtail(T.parameters...)...} end end (::Type{T})(x::Tuple) where {T<:Tuple} = x isa T ? x : convert(T, x) # still use `convert` for tuples Tuple(x::Ref) = tuple(getindex(x)) # faster than iterator for one element Tuple(x::Array{T,0}) where {T} = tuple(getindex(x)) (::Type{T})(itr) where {T<:Tuple} = _totuple(T, itr) _totuple(::Type{Tuple{}}, itr, s...) = () function _totuple_err(@nospecialize T) @noinline throw(ArgumentError("too few elements for tuple type $T")) end function _totuple(::Type{T}, itr, s::Vararg{Any,N}) where {T,N} @inline y = iterate(itr, s...) y === nothing && _totuple_err(T) T1 = fieldtype(T, 1) y1 = y[1] t1 = y1 isa T1 ? y1 : convert(T1, y1)::T1 # inference may give up in recursive calls, so annotate here to force accurate return type to be propagated rT = tuple_type_tail(T) ts = _totuple(rT, itr, y[2])::rT return (t1, ts...)::T end # use iterative algorithm for long tuples function _totuple(T::Type{All32{E,N}}, itr) where {E,N} len = N+32 elts = collect(E, Iterators.take(itr,len)) if length(elts) != len _totuple_err(T) end (elts...,) end _totuple(::Type{Tuple{Vararg{E}}}, itr, s...) where {E} = (collect(E, Iterators.rest(itr,s...))...,) _totuple(::Type{Tuple}, itr, s...) = (collect(Iterators.rest(itr,s...))...,) # for types that `apply` knows about, just splatting is faster than collecting first _totuple(::Type{Tuple}, itr::Array) = (itr...,) _totuple(::Type{Tuple}, itr::SimpleVector) = (itr...,) _totuple(::Type{Tuple}, itr::NamedTuple) = (itr...,) _totuple(::Type{Tuple}, x::Number) = (x,) # to make Tuple(x) inferable end ## find ## _findfirst_rec(f, i::Int, ::Tuple{}) = nothing _findfirst_rec(f, i::Int, t::Tuple) = (@inline; f(first(t)) ? i : _findfirst_rec(f, i+1, tail(t))) function _findfirst_loop(f::Function, t) for i in 1:length(t) f(t[i]) && return i end return nothing end findfirst(f::Function, t::Tuple) = length(t) < 32 ? _findfirst_rec(f, 1, t) : _findfirst_loop(f, t) findlast(f::Function, t::Tuple) = length(t) < 32 ? _findlast_rec(f, t) : _findlast_loop(f, t) function _findlast_rec(f::Function, x::Tuple) r = findfirst(f, reverse(x)) return isnothing(r) ? r : length(x) - r + 1 end function _findlast_loop(f::Function, t) for i in reverse(1:length(t)) f(t[i]) && return i end return nothing end ## filter ## filter_rec(f, xs::Tuple) = afoldl((ys, x) -> f(x) ? (ys..., x) : ys, (), xs...) # use Array for long tuples filter(f, t::Tuple) = length(t) < 32 ? filter_rec(f, t) : Tuple(filter(f, collect(t))) ## comparison ## isequal(t1::Tuple, t2::Tuple) = length(t1) == length(t2) && _isequal(t1, t2) _isequal(::Tuple{}, ::Tuple{}) = true function _isequal(t1::Tuple{Any,Vararg{Any}}, t2::Tuple{Any,Vararg{Any}}) return isequal(t1[1], t2[1]) && _isequal(tail(t1), tail(t2)) end function _isequal(t1::Any32, t2::Any32) for i = 1:length(t1) if !isequal(t1[i], t2[i]) return false end end return true end ==(t1::Tuple, t2::Tuple) = (length(t1) == length(t2)) && _eq(t1, t2) _eq(t1::Tuple{}, t2::Tuple{}) = true _eq_missing(t1::Tuple{}, t2::Tuple{}) = missing function _eq(t1::Tuple, t2::Tuple) eq = t1[1] == t2[1] if eq === false return false elseif ismissing(eq) return _eq_missing(tail(t1), tail(t2)) else return _eq(tail(t1), tail(t2)) end end function _eq_missing(t1::Tuple, t2::Tuple) eq = t1[1] == t2[1] if eq === false return false else return _eq_missing(tail(t1), tail(t2)) end end function _eq(t1::Any32, t2::Any32) anymissing = false for i = 1:length(t1) eq = (t1[i] == t2[i]) if ismissing(eq) anymissing = true elseif !eq return false end end return anymissing ? missing : true end const tuplehash_seed = UInt === UInt64 ? 0x77cfa1eef01bca90 : 0xf01bca90 hash(::Tuple{}, h::UInt) = h + tuplehash_seed hash(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h)) function hash(t::Any32, h::UInt) out = h + tuplehash_seed for i = length(t):-1:1 out = hash(t[i], out) end return out end <(::Tuple{}, ::Tuple{}) = false <(::Tuple{}, ::Tuple) = true <(::Tuple, ::Tuple{}) = false function <(t1::Tuple, t2::Tuple) a, b = t1[1], t2[1] eq = (a == b) if ismissing(eq) return missing elseif !eq return a < b end return tail(t1) < tail(t2) end function <(t1::Any32, t2::Any32) n1, n2 = length(t1), length(t2) for i = 1:min(n1, n2) a, b = t1[i], t2[i] eq = (a == b) if ismissing(eq) return missing elseif !eq return a < b end end return n1 < n2 end isless(::Tuple{}, ::Tuple{}) = false isless(::Tuple{}, ::Tuple) = true isless(::Tuple, ::Tuple{}) = false """ isless(t1::Tuple, t2::Tuple) Return `true` when `t1` is less than `t2` in lexicographic order. """ function isless(t1::Tuple, t2::Tuple) a, b = t1[1], t2[1] isless(a, b) || (isequal(a, b) && isless(tail(t1), tail(t2))) end function isless(t1::Any32, t2::Any32) n1, n2 = length(t1), length(t2) for i = 1:min(n1, n2) a, b = t1[i], t2[i] if !isequal(a, b) return isless(a, b) end end return n1 < n2 end ## functions ## isempty(x::Tuple{}) = true isempty(@nospecialize x::Tuple) = false revargs() = () revargs(x, r...) = (revargs(r...)..., x) reverse(t::Tuple) = revargs(t...) ## specialized reduction ## prod(x::Tuple{}) = 1 # This is consistent with the regular prod because there is no need for size promotion # if all elements in the tuple are of system size. # It is defined here separately in order to support bootstrap, because it's needed earlier # than the general prod definition is available. prod(x::Tuple{Int, Vararg{Int}}) = *(x...) all(x::Tuple{}) = true all(x::Tuple{Bool}) = x[1] all(x::Tuple{Bool, Bool}) = x[1]&x[2] all(x::Tuple{Bool, Bool, Bool}) = x[1]&x[2]&x[3] # use generic reductions for the rest any(x::Tuple{}) = false any(x::Tuple{Bool}) = x[1] any(x::Tuple{Bool, Bool}) = x[1]|x[2] any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3] # a version of `in` esp. for NamedTuple, to make it pure, and not compiled for each tuple length function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}}) @noinline @_total_meta for y in itr y === x && return true end return false end in(x::Symbol, itr::Tuple{Vararg{Symbol}}) = sym_in(x, itr) """ empty(x::Tuple) Return an empty tuple, `()`. """ empty(@nospecialize x::Tuple) = () foreach(f, itr::Tuple) = foldl((_, x) -> (f(x); nothing), itr, init=nothing) foreach(f, itrs::Tuple...) = foldl((_, xs) -> (f(xs...); nothing), zip(itrs...), init=nothing) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/expr.jl๔Ÿ# This file is a part of Julia. License is MIT: https://julialang.org/license isexpr(@nospecialize(ex), heads) = isa(ex, Expr) && in(ex.head, heads) isexpr(@nospecialize(ex), heads, n::Int) = isa(ex, Expr) && in(ex.head, heads) && length(ex.args) == n const is_expr = isexpr ## symbols ## """ gensym([tag]) Generates a symbol which will not conflict with other variable names (in the same module). """ gensym() = ccall(:jl_gensym, Ref{Symbol}, ()) gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, sizeof(s)) gensym(ss::String...) = map(gensym, ss) gensym(s::Symbol) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Csize_t), s, -1 % Csize_t) """ @gensym Generates a gensym symbol for a variable. For example, `@gensym x y` is transformed into `x = gensym("x"); y = gensym("y")`. """ macro gensym(names...) blk = Expr(:block) for name in names push!(blk.args, :($(esc(name)) = gensym($(string(name))))) end push!(blk.args, :nothing) return blk end ## expressions ## isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head isexpr(@nospecialize(ex), head::Symbol, n::Int) = isa(ex, Expr) && ex.head === head && length(ex.args) == n copy(e::Expr) = exprarray(e.head, copy_exprargs(e.args)) # copy parts of an AST that the compiler mutates function copy_exprs(@nospecialize(x)) if isa(x, Expr) return copy(x) elseif isa(x, PhiNode) values = x.values nvalues = length(values) new_values = Vector{Any}(undef, nvalues) @inbounds for i = 1:nvalues isassigned(values, i) || continue new_values[i] = copy_exprs(values[i]) end return PhiNode(copy(x.edges), new_values) elseif isa(x, PhiCNode) values = x.values nvalues = length(values) new_values = Vector{Any}(undef, nvalues) @inbounds for i = 1:nvalues isassigned(values, i) || continue new_values[i] = copy_exprs(values[i]) end return PhiCNode(new_values) end return x end copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(@inbounds x[i]) for i in 1:length(x)] @eval exprarray(head::Symbol, arg::Array{Any,1}) = $(Expr(:new, :Expr, :head, :arg)) # create copies of the CodeInfo definition, and any mutable fields function copy(c::CodeInfo) cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c) cnew.code = copy_exprargs(cnew.code) cnew.slotnames = copy(cnew.slotnames) cnew.slotflags = copy(cnew.slotflags) if cnew.slottypes !== nothing cnew.slottypes = copy(cnew.slottypes) end cnew.codelocs = copy(cnew.codelocs) cnew.linetable = copy(cnew.linetable::Union{Vector{Any},Vector{Core.LineInfoNode}}) cnew.ssaflags = copy(cnew.ssaflags) cnew.edges = cnew.edges === nothing ? nothing : copy(cnew.edges::Vector) ssavaluetypes = cnew.ssavaluetypes ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes)) return cnew end ==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args) ==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value) ==(stmt1::Core.PhiNode, stmt2::Core.PhiNode) = stmt1.edges == stmt2.edges && stmt1.values == stmt2.values """ macroexpand(m::Module, x; recursive=true) Take the expression `x` and return an equivalent expression with all macros removed (expanded) for executing in module `m`. The `recursive` keyword controls whether deeper levels of nested macros are also expanded. This is demonstrated in the example below: ```julia-repl julia> module M macro m1() 42 end macro m2() :(@m1()) end end M julia> macroexpand(M, :(@m2()), recursive=true) 42 julia> macroexpand(M, :(@m2()), recursive=false) :(#= REPL[16]:6 =# M.@m1) ``` """ function macroexpand(m::Module, @nospecialize(x); recursive=true) if recursive ccall(:jl_macroexpand, Any, (Any, Any), x, m) else ccall(:jl_macroexpand1, Any, (Any, Any), x, m) end end """ @macroexpand Return equivalent expression with all macros removed (expanded). There are differences between `@macroexpand` and [`macroexpand`](@ref). * While [`macroexpand`](@ref) takes a keyword argument `recursive`, `@macroexpand` is always recursive. For a non recursive macro version, see [`@macroexpand1`](@ref). * While [`macroexpand`](@ref) has an explicit `module` argument, `@macroexpand` always expands with respect to the module in which it is called. This is best seen in the following example: ```julia-repl julia> module M macro m() 1 end function f() (@macroexpand(@m), macroexpand(M, :(@m)), macroexpand(Main, :(@m)) ) end end M julia> macro m() 2 end @m (macro with 1 method) julia> M.f() (1, 1, 2) ``` With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M` in the example). With `macroexpand` the expression expands in the module given as the first argument. """ macro macroexpand(code) return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true)) end """ @macroexpand1 Non recursive version of [`@macroexpand`](@ref). """ macro macroexpand1(code) return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false)) end ## misc syntax ## """ Core.eval(m::Module, expr) Evaluate an expression in the given module and return the result. """ Core.eval """ @inline Give a hint to the compiler that this function is worth inlining. Small functions typically do not need the `@inline` annotation, as the compiler does it automatically. By using `@inline` on bigger functions, an extra nudge can be given to the compiler to inline it. `@inline` can be applied immediately before a function definition or within a function body. ```julia # annotate long-form definition @inline function longdef(x) ... end # annotate short-form definition @inline shortdef(x) = ... # annotate anonymous function that a `do` block creates f() do @inline ... end ``` !!! compat "Julia 1.8" The usage within a function body requires at least Julia 1.8. --- @inline block Give a hint to the compiler that calls within `block` are worth inlining. ```julia # The compiler will try to inline `f` @inline f(...) # The compiler will try to inline `f`, `g` and `+` @inline f(...) + g(...) ``` !!! note A callsite annotation always has the precedence over the annotation applied to the definition of the called function: ```julia @noinline function explicit_noinline(args...) # body end let @inline explicit_noinline(args...) # will be inlined end ``` !!! note When there are nested callsite annotations, the innermost annotation has the precedence: ```julia @noinline let a0, b0 = ... a = @inline f(a0) # the compiler will try to inline this call b = f(b0) # the compiler will NOT try to inline this call return a, b end ``` !!! warning Although a callsite annotation will try to force inlining in regardless of the cost model, there are still chances it can't succeed in it. Especially, recursive calls can not be inlined even if they are annotated as `@inline`d. !!! compat "Julia 1.8" The callsite annotation requires at least Julia 1.8. """ macro inline(x) return annotate_meta_def_or_block(x, :inline) end """ @noinline Give a hint to the compiler that it should not inline a function. Small functions are typically inlined automatically. By using `@noinline` on small functions, auto-inlining can be prevented. `@noinline` can be applied immediately before a function definition or within a function body. ```julia # annotate long-form definition @noinline function longdef(x) ... end # annotate short-form definition @noinline shortdef(x) = ... # annotate anonymous function that a `do` block creates f() do @noinline ... end ``` !!! compat "Julia 1.8" The usage within a function body requires at least Julia 1.8. --- @noinline block Give a hint to the compiler that it should not inline the calls within `block`. ```julia # The compiler will try to not inline `f` @noinline f(...) # The compiler will try to not inline `f`, `g` and `+` @noinline f(...) + g(...) ``` !!! note A callsite annotation always has the precedence over the annotation applied to the definition of the called function: ```julia @inline function explicit_inline(args...) # body end let @noinline explicit_inline(args...) # will not be inlined end ``` !!! note When there are nested callsite annotations, the innermost annotation has the precedence: ```julia @inline let a0, b0 = ... a = @noinline f(a0) # the compiler will NOT try to inline this call b = f(b0) # the compiler will try to inline this call return a, b end ``` !!! compat "Julia 1.8" The callsite annotation requires at least Julia 1.8. --- !!! note If the function is trivial (for example returning a constant) it might get inlined anyway. """ macro noinline(x) return annotate_meta_def_or_block(x, :noinline) end """ @constprop setting [ex] Control the mode of interprocedural constant propagation for the annotated function. Two `setting`s are supported: - `@constprop :aggressive [ex]`: apply constant propagation aggressively. For a method where the return type depends on the value of the arguments, this can yield improved inference results at the cost of additional compile time. - `@constprop :none [ex]`: disable constant propagation. This can reduce compile times for functions that Julia might otherwise deem worthy of constant-propagation. Common cases are for functions with `Bool`- or `Symbol`-valued arguments or keyword arguments. `@constprop` can be applied immediately before a function definition or within a function body. ```julia # annotate long-form definition @constprop :aggressive function longdef(x) ... end # annotate short-form definition @constprop :aggressive shortdef(x) = ... # annotate anonymous function that a `do` block creates f() do @constprop :aggressive ... end ``` !!! compat "Julia 1.10" The usage within a function body requires at least Julia 1.10. """ macro constprop(setting, ex) sym = constprop_setting(setting) isa(ex, Expr) && return esc(pushmeta!(ex, sym)) throw(ArgumentError(LazyString("Bad expression `", ex, "` in `@constprop settings ex`"))) end macro constprop(setting) sym = constprop_setting(setting) return Expr(:meta, sym) end function constprop_setting(@nospecialize setting) isa(setting, QuoteNode) && (setting = setting.value) if setting === :aggressive return :aggressive_constprop elseif setting === :none return :no_constprop end throw(ArgumentError(LazyString("@constprop "), setting, "not supported")) end """ @assume_effects setting... [ex] Override the compiler's effect modeling for the given method or foreign call. `@assume_effects` can be applied immediately before a function definition or within a function body. It can also be applied immediately before a `@ccall` expression. !!! compat "Julia 1.8" Using `Base.@assume_effects` requires Julia version 1.8. # Examples ```jldoctest julia> Base.@assume_effects :terminates_locally function pow(x) # this :terminates_locally allows `pow` to be constant-folded res = 1 1 < x < 20 || error("bad pow") while x > 1 res *= x x -= 1 end return res end pow (generic function with 1 method) julia> code_typed() do pow(12) end 1-element Vector{Any}: CodeInfo( 1 โ”€ return 479001600 ) => Int64 julia> code_typed() do map((2,3,4)) do x # this :terminates_locally allows this anonymous function to be constant-folded Base.@assume_effects :terminates_locally res = 1 1 < x < 20 || error("bad pow") while x > 1 res *= x x -= 1 end return res end end 1-element Vector{Any}: CodeInfo( 1 โ”€ return (2, 6, 24) ) => Tuple{Int64, Int64, Int64} julia> Base.@assume_effects :total !:nothrow @ccall jl_type_intersection(Vector{Int}::Any, Vector{<:Integer}::Any)::Any Vector{Int64} (alias for Array{Int64, 1}) ``` !!! compat "Julia 1.10" The usage within a function body requires at least Julia 1.10. !!! warning Improper use of this macro causes undefined behavior (including crashes, incorrect answers, or other hard to track bugs). Use with care and only as a last resort if absolutely required. Even in such a case, you SHOULD take all possible steps to minimize the strength of the effect assertion (e.g., do not use `:total` if `:nothrow` would have been sufficient). In general, each `setting` value makes an assertion about the behavior of the function, without requiring the compiler to prove that this behavior is indeed true. These assertions are made for all world ages. It is thus advisable to limit the use of generic functions that may later be extended to invalidate the assumption (which would cause undefined behavior). The following `setting`s are supported. - `:consistent` - `:effect_free` - `:nothrow` - `:terminates_globally` - `:terminates_locally` - `:notaskstate` - `:inaccessiblememonly` - `:foldable` - `:removable` - `:total` # Extended help --- ## `:consistent` The `:consistent` setting asserts that for egal (`===`) inputs: - The manner of termination (return value, exception, non-termination) will always be the same. - If the method returns, the results will always be egal. !!! note This in particular implies that the method must not return a freshly allocated mutable object. Multiple allocations of mutable objects (even with identical contents) are not egal. !!! note The `:consistent`-cy assertion is made world-age wise. More formally, write ``fแตข`` for the evaluation of ``f`` in world-age ``i``, then we require: ```math โˆ€ i, x, y: x โ‰ก y โ†’ fแตข(x) โ‰ก fแตข(y) ``` However, for two world ages ``i``, ``j`` s.t. ``i โ‰  j``, we may have ``fแตข(x) โ‰ข fโฑผ(y)``. A further implication is that `:consistent` functions may not make their return value dependent on the state of the heap or any other global state that is not constant for a given world age. !!! note The `:consistent`-cy includes all legal rewrites performed by the optimizer. For example, floating-point fastmath operations are not considered `:consistent`, because the optimizer may rewrite them causing the output to not be `:consistent`, even for the same world age (e.g. because one ran in the interpreter, while the other was optimized). !!! note The `:consistent`-cy assertion currrently includes the assertion that the function will not execute any undefined behavior (for any input). Note that undefined behavior may technically cause the function to violate other effect assertions (such as `:nothrow` or `:effect_free`) as well, but we do not model this, and all effects except `:consistent` assume the absence of undefined behavior. !!! note If `:consistent` functions terminate by throwing an exception, that exception itself is not required to meet the egality requirement specified above. --- ## `:effect_free` The `:effect_free` setting asserts that the method is free of externally semantically visible side effects. The following is an incomplete list of externally semantically visible side effects: - Changing the value of a global variable. - Mutating the heap (e.g. an array or mutable value), except as noted below - Changing the method table (e.g. through calls to eval) - File/Network/etc. I/O - Task switching However, the following are explicitly not semantically visible, even if they may be observable: - Memory allocations (both mutable and immutable) - Elapsed time - Garbage collection - Heap mutations of objects whose lifetime does not exceed the method (i.e. were allocated in the method and do not escape). - The returned value (which is externally visible, but not a side effect) The rule of thumb here is that an externally visible side effect is anything that would affect the execution of the remainder of the program if the function were not executed. !!! note The `:effect_free` assertion is made both for the method itself and any code that is executed by the method. Keep in mind that the assertion must be valid for all world ages and limit use of this assertion accordingly. --- ## `:nothrow` The `:nothrow` settings asserts that this method does not terminate abnormally (i.e. will either always return a value or never return). !!! note It is permissible for `:nothrow` annotated methods to make use of exception handling internally as long as the exception is not rethrown out of the method itself. !!! note `MethodErrors` and similar exceptions count as abnormal termination. --- ## `:terminates_globally` The `:terminates_globally` settings asserts that this method will eventually terminate (either normally or abnormally), i.e. does not loop indefinitely. !!! note This `:terminates_globally` assertion covers any other methods called by the annotated method. !!! note The compiler will consider this a strong indication that the method will terminate relatively *quickly* and may (if otherwise legal), call this method at compile time. I.e. it is a bad idea to annotate this setting on a method that *technically*, but not *practically*, terminates. --- ## `:terminates_locally` The `:terminates_locally` setting is like `:terminates_globally`, except that it only applies to syntactic control flow *within* the annotated method. It is thus a much weaker (and thus safer) assertion that allows for the possibility of non-termination if the method calls some other method that does not terminate. !!! note `:terminates_globally` implies `:terminates_locally`. --- ## `:notaskstate` The `:notaskstate` setting asserts that the method does not use or modify the local task state (task local storage, RNG state, etc.) and may thus be safely moved between tasks without observable results. !!! note The implementation of exception handling makes use of state stored in the task object. However, this state is currently not considered to be within the scope of `:notaskstate` and is tracked separately using the `:nothrow` effect. !!! note The `:notaskstate` assertion concerns the state of the *currently running task*. If a reference to a `Task` object is obtained by some other means that does not consider which task is *currently* running, the `:notaskstate` effect need not be tainted. This is true, even if said task object happens to be `===` to the currently running task. !!! note Access to task state usually also results in the tainting of other effects, such as `:effect_free` (if task state is modified) or `:consistent` (if task state is used in the computation of the result). In particular, code that is not `:notaskstate`, but is `:effect_free` and `:consistent` may still be dead-code-eliminated and thus promoted to `:total`. --- ## `:inaccessiblememonly` The `:inaccessiblememonly` setting asserts that the method does not access or modify externally accessible mutable memory. This means the method can access or modify mutable memory for newly allocated objects that is not accessible by other methods or top-level execution before return from the method, but it can not access or modify any mutable global state or mutable memory pointed to by its arguments. !!! note Below is an incomplete list of examples that invalidate this assumption: - a global reference or `getglobal` call to access a mutable global variable - a global assignment or `setglobal!` call to perform assignment to a non-constant global variable - `setfield!` call that changes a field of a global mutable variable !!! note This `:inaccessiblememonly` assertion covers any other methods called by the annotated method. --- ## `:foldable` This setting is a convenient shortcut for the set of effects that the compiler requires to be guaranteed to constant fold a call at compile time. It is currently equivalent to the following `setting`s: - `:consistent` - `:effect_free` - `:terminates_globally` !!! note This list in particular does not include `:nothrow`. The compiler will still attempt constant propagation and note any thrown error at compile time. Note however, that by the `:consistent`-cy requirements, any such annotated call must consistently throw given the same argument values. !!! note An explicit `@inbounds` annotation inside the function will also disable constant folding and not be overriden by `:foldable`. --- ## `:removable` This setting is a convenient shortcut for the set of effects that the compiler requires to be guaranteed to delete a call whose result is unused at compile time. It is currently equivalent to the following `setting`s: - `:effect_free` - `:nothrow` - `:terminates_globally` --- ## `:total` This `setting` is the maximum possible set of effects. It currently implies the following other `setting`s: - `:consistent` - `:effect_free` - `:nothrow` - `:terminates_globally` - `:notaskstate` - `:inaccessiblememonly` !!! warning `:total` is a very strong assertion and will likely gain additional semantics in future versions of Julia (e.g. if additional effects are added and included in the definition of `:total`). As a result, it should be used with care. Whenever possible, prefer to use the minimum possible set of specific effect assertions required for a particular application. In cases where a large number of effect overrides apply to a set of functions, a custom macro is recommended over the use of `:total`. --- ## Negated effects Effect names may be prefixed by `!` to indicate that the effect should be removed from an earlier meta effect. For example, `:total !:nothrow` indicates that while the call is generally total, it may however throw. """ macro assume_effects(args...) lastex = args[end] inner = unwrap_macrocalls(lastex) if is_function_def(inner) ex = lastex idx = length(args)-1 elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall") ex = lastex idx = length(args)-1 else # anonymous function case ex = nothing idx = length(args) end (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) = (false, false, false, false, false, false, false, false) for org_setting in args[1:idx] (setting, val) = compute_assumed_setting(org_setting) if setting === :consistent consistent = val elseif setting === :effect_free effect_free = val elseif setting === :nothrow nothrow = val elseif setting === :terminates_globally terminates_globally = val elseif setting === :terminates_locally terminates_locally = val elseif setting === :notaskstate notaskstate = val elseif setting === :inaccessiblememonly inaccessiblememonly = val elseif setting === :foldable consistent = effect_free = terminates_globally = val elseif setting === :removable effect_free = nothrow = terminates_globally = val elseif setting === :total consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = val else throw(ArgumentError("@assume_effects $org_setting not supported")) end end if is_function_def(inner) return esc(pushmeta!(ex, :purity, consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall") ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride( consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, ))) return esc(ex) else # anonymous function case return Expr(:meta, Expr(:purity, consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) end end function compute_assumed_setting(@nospecialize(setting), val::Bool=true) if isexpr(setting, :call) && setting.args[1] === :(!) return compute_assumed_setting(setting.args[2], !val) elseif isa(setting, QuoteNode) return compute_assumed_setting(setting.value, val) else return (setting, val) end end """ Base.@nospecializeinfer function f(args...) @nospecialize ... ... end Base.@nospecializeinfer f(@nospecialize args...) = ... Tells the compiler to infer `f` using the declared types of `@nospecialize`d arguments. This can be used to limit the number of compiler-generated specializations during inference. # Example ```julia julia> f(A::AbstractArray) = g(A) f (generic function with 1 method) julia> @noinline Base.@nospecializeinfer g(@nospecialize(A::AbstractArray)) = A[1] g (generic function with 1 method) julia> @code_typed f([1.0]) CodeInfo( 1 โ”€ %1 = invoke Main.g(_2::AbstractArray)::Any โ””โ”€โ”€ return %1 ) => Any ``` In this example, `f` will be inferred for each specific type of `A`, but `g` will only be inferred once with the declared argument type `A::AbstractArray`, meaning that the compiler will not likely see the excessive inference time on it while it can not infer the concrete return type of it. Without the `@nospecializeinfer`, `f([1.0])` would infer the return type of `g` as `Float64`, indicating that inference ran for `g(::Vector{Float64})` despite the prohibition on specialized code generation. """ macro nospecializeinfer(ex) esc(isa(ex, Expr) ? pushmeta!(ex, :nospecializeinfer) : ex) end """ @propagate_inbounds Tells the compiler to inline a function while retaining the caller's inbounds context. """ macro propagate_inbounds(ex) if isa(ex, Expr) pushmeta!(ex, :inline) pushmeta!(ex, :propagate_inbounds) end esc(ex) end """ @polly Tells the compiler to apply the polyhedral optimizer Polly to a function. """ macro polly(ex) esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex) end ## some macro utilities ## unwrap_macrocalls(@nospecialize(x)) = x function unwrap_macrocalls(ex::Expr) inner = ex while inner.head === :macrocall inner = inner.args[end]::Expr end return inner end function pushmeta!(ex::Expr, sym::Symbol, args::Any...) if isempty(args) tag = sym else tag = Expr(sym, args...)::Expr end inner = unwrap_macrocalls(ex) idx, exargs = findmeta(inner) if idx != 0 push!(exargs[idx].args, tag) else body = inner.args[2]::Expr pushfirst!(body.args, Expr(:meta, tag)) end ex end popmeta!(body, sym) = _getmeta(body, sym, true) peekmeta(body, sym) = _getmeta(body, sym, false) function _getmeta(body::Expr, sym::Symbol, delete::Bool) body.head === :block || return false, [] _getmeta(body.args, sym, delete) end _getmeta(arg, sym, delete::Bool) = (false, []) function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool) idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0) if idx == 0 return false, [] end metaargs = blockargs[idx].args i = findmetaarg(blockargs[idx].args, sym) if i == 0 return false, [] end ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : [] if delete deleteat!(metaargs, i) isempty(metaargs) && deleteat!(blockargs, idx) end true, ret end # Find index of `sym` in a meta expression argument list, or 0. function findmetaarg(metaargs, sym) for i = 1:length(metaargs) arg = metaargs[i] if (isa(arg, Symbol) && (arg::Symbol) == sym) || (isa(arg, Expr) && (arg::Expr).head == sym) return i end end return 0 end function annotate_meta_def_or_block(@nospecialize(ex), meta::Symbol) inner = unwrap_macrocalls(ex) if is_function_def(inner) # annotation on a definition return esc(pushmeta!(ex, meta)) else # annotation on a block return Expr(:block, Expr(meta, true), Expr(:local, Expr(:(=), :val, esc(ex))), Expr(meta, false), :val) end end function is_short_function_def(@nospecialize(ex)) isexpr(ex, :(=)) || return false while length(ex.args) >= 1 && isa(ex.args[1], Expr) (ex.args[1].head === :call) && return true (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false ex = ex.args[1] end return false end is_function_def(@nospecialize(ex)) = return isexpr(ex, :function) || is_short_function_def(ex) || isexpr(ex, :->) function findmeta(ex::Expr) if is_function_def(ex) body = ex.args[2]::Expr body.head === :block || error(body, " is not a block expression") return findmeta_block(ex.args) end error(ex, " is not a function expression") end findmeta(ex::Array{Any,1}) = findmeta_block(ex) function findmeta_block(exargs, argsmatch=args->true) for i = 1:length(exargs) a = exargs[i] if isa(a, Expr) if a.head === :meta && argsmatch(a.args) return i, exargs elseif a.head === :block idx, exa = findmeta_block(a.args, argsmatch) if idx != 0 return idx, exa end end end end return 0, [] end remove_linenums!(ex) = ex function remove_linenums!(ex::Expr) if ex.head === :block || ex.head === :quote # remove line number expressions from metadata (not argument literal or inert) position filter!(ex.args) do x isa(x, Expr) && x.head === :line && return false isa(x, LineNumberNode) && return false return true end end for subex in ex.args subex isa Expr && remove_linenums!(subex) end return ex end function remove_linenums!(src::CodeInfo) src.codelocs .= 0 length(src.linetable) > 1 && resize!(src.linetable, 1) return src end replace_linenums!(ex, ln::LineNumberNode) = ex function replace_linenums!(ex::Expr, ln::LineNumberNode) if ex.head === :block || ex.head === :quote # replace line number expressions from metadata (not argument literal or inert) position map!(ex.args, ex.args) do @nospecialize(x) isa(x, Expr) && x.head === :line && length(x.args) == 1 && return Expr(:line, ln.line) isa(x, Expr) && x.head === :line && length(x.args) == 2 && return Expr(:line, ln.line, ln.file) isa(x, LineNumberNode) && return ln return x end end # preserve any linenums inside `esc(...)` guards if ex.head !== :escape for subex in ex.args subex isa Expr && replace_linenums!(subex, ln) end end return ex end macro generated() return Expr(:generated) end """ @generated f `@generated` is used to annotate a function which will be generated. In the body of the generated function, only types of arguments can be read (not the values). The function returns a quoted expression evaluated when the function is called. The `@generated` macro should not be used on functions mutating the global scope or depending on mutable elements. See [Metaprogramming](@ref) for further details. # Examples ```jldoctest julia> @generated function bar(x) if x <: Integer return :(x ^ 2) else return :(x) end end bar (generic function with 1 method) julia> bar(4) 16 julia> bar("baz") "baz" ``` """ macro generated(f) if isa(f, Expr) && (f.head === :function || is_short_function_def(f)) body = f.args[2] lno = body.args[1] tmp = gensym("tmp") return Expr(:escape, Expr(f.head, f.args[1], Expr(:block, lno, Expr(:if, Expr(:generated), body, Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) else error("invalid syntax; @generated must be used with a function definition") end end """ @atomic var @atomic order ex Mark `var` or `ex` as being performed atomically, if `ex` is a supported expression. If no `order` is specified it defaults to :sequentially_consistent. @atomic a.b.x = new @atomic a.b.x += addend @atomic :release a.b.x = new @atomic :acquire_release a.b.x += addend Perform the store operation expressed on the right atomically and return the new value. With `=`, this operation translates to a `setproperty!(a.b, :x, new)` call. With any operator also, this operation translates to a `modifyproperty!(a.b, :x, +, addend)[2]` call. @atomic a.b.x max arg2 @atomic a.b.x + arg2 @atomic max(a.b.x, arg2) @atomic :acquire_release max(a.b.x, arg2) @atomic :acquire_release a.b.x + arg2 @atomic :acquire_release a.b.x max arg2 Perform the binary operation expressed on the right atomically. Store the result into the field in the first argument and return the values `(old, new)`. This operation translates to a `modifyproperty!(a.b, :x, func, arg2)` call. See [Per-field atomics](@ref man-atomics) section in the manual for more details. # Examples ```jldoctest julia> mutable struct Atomic{T}; @atomic x::T; end julia> a = Atomic(1) Atomic{Int64}(1) julia> @atomic a.x # fetch field x of a, with sequential consistency 1 julia> @atomic :sequentially_consistent a.x = 2 # set field x of a, with sequential consistency 2 julia> @atomic a.x += 1 # increment field x of a, with sequential consistency 3 julia> @atomic a.x + 1 # increment field x of a, with sequential consistency 3 => 4 julia> @atomic a.x # fetch field x of a, with sequential consistency 4 julia> @atomic max(a.x, 10) # change field x of a to the max value, with sequential consistency 4 => 10 julia> @atomic a.x max 5 # again change field x of a to the max value, with sequential consistency 10 => 10 ``` !!! compat "Julia 1.7" This functionality requires at least Julia 1.7. """ macro atomic(ex) if !isa(ex, Symbol) && !is_expr(ex, :(::)) return make_atomic(QuoteNode(:sequentially_consistent), ex) end return esc(Expr(:atomic, ex)) end macro atomic(order, ex) order isa QuoteNode || (order = esc(order)) return make_atomic(order, ex) end macro atomic(a1, op, a2) return make_atomic(QuoteNode(:sequentially_consistent), a1, op, a2) end macro atomic(order, a1, op, a2) order isa QuoteNode || (order = esc(order)) return make_atomic(order, a1, op, a2) end function make_atomic(order, ex) @nospecialize if ex isa Expr if isexpr(ex, :., 2) l, r = esc(ex.args[1]), esc(ex.args[2]) return :(getproperty($l, $r, $order)) elseif isexpr(ex, :call, 3) return make_atomic(order, ex.args[2], ex.args[1], ex.args[3]) elseif ex.head === :(=) l, r = ex.args[1], esc(ex.args[2]) if is_expr(l, :., 2) ll, lr = esc(l.args[1]), esc(l.args[2]) return :(setproperty!($ll, $lr, $r, $order)) end end if length(ex.args) == 2 if ex.head === :(+=) op = :+ elseif ex.head === :(-=) op = :- elseif @isdefined string shead = string(ex.head) if endswith(shead, '=') op = Symbol(shead[1:prevind(shead, end)]) end end if @isdefined(op) return Expr(:ref, make_atomic(order, ex.args[1], op, ex.args[2]), 2) end end end error("could not parse @atomic expression $ex") end function make_atomic(order, a1, op, a2) @nospecialize is_expr(a1, :., 2) || error("@atomic modify expression missing field access") a1l, a1r, op, a2 = esc(a1.args[1]), esc(a1.args[2]), esc(op), esc(a2) return :(modifyproperty!($a1l, $a1r, $op, $a2, $order)) end """ @atomicswap a.b.x = new @atomicswap :sequentially_consistent a.b.x = new Stores `new` into `a.b.x` and returns the old value of `a.b.x`. This operation translates to a `swapproperty!(a.b, :x, new)` call. See [Per-field atomics](@ref man-atomics) section in the manual for more details. # Examples ```jldoctest julia> mutable struct Atomic{T}; @atomic x::T; end julia> a = Atomic(1) Atomic{Int64}(1) julia> @atomicswap a.x = 2+2 # replace field x of a with 4, with sequential consistency 1 julia> @atomic a.x # fetch field x of a, with sequential consistency 4 ``` !!! compat "Julia 1.7" This functionality requires at least Julia 1.7. """ macro atomicswap(order, ex) order isa QuoteNode || (order = esc(order)) return make_atomicswap(order, ex) end macro atomicswap(ex) return make_atomicswap(QuoteNode(:sequentially_consistent), ex) end function make_atomicswap(order, ex) @nospecialize is_expr(ex, :(=), 2) || error("@atomicswap expression missing assignment") l, val = ex.args[1], esc(ex.args[2]) is_expr(l, :., 2) || error("@atomicswap expression missing field access") ll, lr = esc(l.args[1]), esc(l.args[2]) return :(swapproperty!($ll, $lr, $val, $order)) end """ @atomicreplace a.b.x expected => desired @atomicreplace :sequentially_consistent a.b.x expected => desired @atomicreplace :sequentially_consistent :monotonic a.b.x expected => desired Perform the conditional replacement expressed by the pair atomically, returning the values `(old, success::Bool)`. Where `success` indicates whether the replacement was completed. This operation translates to a `replaceproperty!(a.b, :x, expected, desired)` call. See [Per-field atomics](@ref man-atomics) section in the manual for more details. # Examples ```jldoctest julia> mutable struct Atomic{T}; @atomic x::T; end julia> a = Atomic(1) Atomic{Int64}(1) julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency (old = 1, success = true) julia> @atomic a.x # fetch field x of a, with sequential consistency 2 julia> @atomicreplace a.x 1 => 2 # replace field x of a with 2 if it was 1, with sequential consistency (old = 2, success = false) julia> xchg = 2 => 0; # replace field x of a with 0 if it was 2, with sequential consistency julia> @atomicreplace a.x xchg (old = 2, success = true) julia> @atomic a.x # fetch field x of a, with sequential consistency 0 ``` !!! compat "Julia 1.7" This functionality requires at least Julia 1.7. """ macro atomicreplace(success_order, fail_order, ex, old_new) fail_order isa QuoteNode || (fail_order = esc(fail_order)) success_order isa QuoteNode || (success_order = esc(success_order)) return make_atomicreplace(success_order, fail_order, ex, old_new) end macro atomicreplace(order, ex, old_new) order isa QuoteNode || (order = esc(order)) return make_atomicreplace(order, order, ex, old_new) end macro atomicreplace(ex, old_new) return make_atomicreplace(QuoteNode(:sequentially_consistent), QuoteNode(:sequentially_consistent), ex, old_new) end function make_atomicreplace(success_order, fail_order, ex, old_new) @nospecialize is_expr(ex, :., 2) || error("@atomicreplace expression missing field access") ll, lr = esc(ex.args[1]), esc(ex.args[2]) if is_expr(old_new, :call, 3) && old_new.args[1] === :(=>) exp, rep = esc(old_new.args[2]), esc(old_new.args[3]) return :(replaceproperty!($ll, $lr, $exp, $rep, $success_order, $fail_order)) else old_new = esc(old_new) return :(replaceproperty!($ll, $lr, $old_new::Pair..., $success_order, $fail_order)) end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/pair.jlฅ# This file is a part of Julia. License is MIT: https://julialang.org/license const => = Pair """ Pair(x, y) x => y Construct a `Pair` object with type `Pair{typeof(x), typeof(y)}`. The elements are stored in the fields `first` and `second`. They can also be accessed via iteration (but a `Pair` is treated as a single "scalar" for broadcasting operations). See also [`Dict`](@ref). # Examples ```jldoctest julia> p = "foo" => 7 "foo" => 7 julia> typeof(p) Pair{String, Int64} julia> p.first "foo" julia> for x in p println(x) end foo 7 julia> replace.(["xops", "oxps"], "x" => "o") 2-element Vector{String}: "oops" "oops" ``` """ Pair, => eltype(p::Type{Pair{A, B}}) where {A, B} = Union{A, B} iterate(p::Pair, i=1) = i > 2 ? nothing : (getfield(p, i), i + 1) indexed_iterate(p::Pair, i::Int, state=1) = (getfield(p, i), i + 1) hash(p::Pair, h::UInt) = hash(p.second, hash(p.first, h)) ==(p::Pair, q::Pair) = (p.first==q.first) & (p.second==q.second) isequal(p::Pair, q::Pair) = isequal(p.first,q.first)::Bool & isequal(p.second,q.second)::Bool isless(p::Pair, q::Pair) = ifelse(!isequal(p.first,q.first), isless(p.first,q.first), isless(p.second,q.second)) getindex(p::Pair,i::Int) = getfield(p,i) getindex(p::Pair,i::Real) = getfield(p, convert(Int, i)) reverse(p::Pair{A,B}) where {A,B} = Pair{B,A}(p.second, p.first) firstindex(p::Pair) = 1 lastindex(p::Pair) = 2 length(p::Pair) = 2 first(p::Pair) = p.first last(p::Pair) = p.second convert(::Type{Pair{A,B}}, x::Pair{A,B}) where {A,B} = x function convert(::Type{Pair{A,B}}, x::Pair) where {A,B} a = getfield(x, :first) a isa A || (a = convert(A, a)) b = getfield(x, :second) b isa B || (b = convert(B, b)) return Pair{A,B}(a, b)::Pair{A,B} end promote_rule(::Type{Pair{A1,B1}}, ::Type{Pair{A2,B2}}) where {A1,B1,A2,B2} = Pair{promote_type(A1, A2), promote_type(B1, B2)} M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/traits.jlB # This file is a part of Julia. License is MIT: https://julialang.org/license ## numeric/object traits # trait for objects that have an ordering abstract type OrderStyle end struct Ordered <: OrderStyle end struct Unordered <: OrderStyle end OrderStyle(instance) = OrderStyle(typeof(instance)) OrderStyle(::Type{<:Real}) = Ordered() OrderStyle(::Type{<:AbstractString}) = Ordered() OrderStyle(::Type{Symbol}) = Ordered() OrderStyle(::Type{<:Any}) = Unordered() OrderStyle(::Type{Union{}}, slurp...) = Ordered() # trait for objects that support arithmetic abstract type ArithmeticStyle end struct ArithmeticRounds <: ArithmeticStyle end # least significant bits can be lost struct ArithmeticWraps <: ArithmeticStyle end # most significant bits can be lost struct ArithmeticUnknown <: ArithmeticStyle end ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance)) ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds() ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps() ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown() ArithmeticStyle(::Type{Union{}}, slurp...) = ArithmeticUnknown() # trait for objects that support ranges with regular step """ RangeStepStyle(instance) RangeStepStyle(T::Type) Indicate whether an instance or a type supports constructing a range with a perfectly regular step or not. A regular step means that [`step`](@ref) will always be exactly equal to the difference between two subsequent elements in a range, i.e. for a range `r::AbstractRange{T}`: ```julia all(diff(r) .== step(r)) ``` When a type `T` always leads to ranges with regular steps, it should define the following method: ```julia Base.RangeStepStyle(::Type{<:AbstractRange{<:T}}) = Base.RangeStepRegular() ``` This will allow [`hash`](@ref) to use an O(1) algorithm for `AbstractRange{T}` objects instead of the default O(N) algorithm (with N the length of the range). In some cases, whether the step will be regular depends not only on the element type `T`, but also on the type of the step `S`. In that case, more specific methods should be defined: ```julia Base.RangeStepStyle(::Type{<:OrdinalRange{<:T, <:S}}) = Base.RangeStepRegular() ``` By default, all range types are assumed to be `RangeStepIrregular`, except ranges with an element type which is a subtype of `Integer`. """ abstract type RangeStepStyle end struct RangeStepRegular <: RangeStepStyle end # range with regular step struct RangeStepIrregular <: RangeStepStyle end # range with rounding error RangeStepStyle(::Type{Union{}}, slurp...) = RangeStepIrregular() RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/range.jlึ# This file is a part of Julia. License is MIT: https://julialang.org/license (:)(a::Real, b::Real) = (:)(promote(a, b)...) (:)(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop) (:)(start::T, stop::T) where {T} = (:)(start, oftype(stop >= start ? stop - start : start - stop, 1), stop) # promote start and stop, leaving step alone (:)(start::A, step, stop::C) where {A<:Real, C<:Real} = (:)(convert(promote_type(A, C), start), step, convert(promote_type(A, C), stop)) # AbstractFloat specializations (:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, T(1), b) (:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a, b, c)...) (:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...) (:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...) (:)(start::T, step::T, stop::T) where {T<:AbstractFloat} = _colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop) (:)(start::T, step::T, stop::T) where {T<:Real} = _colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop) _colon(::Ordered, ::Any, start::T, step, stop::T) where {T} = StepRange(start, step, stop) # for T<:Union{Float16,Float32,Float64} see twiceprecision.jl _colon(::Ordered, ::ArithmeticRounds, start::T, step, stop::T) where {T} = StepRangeLen(start, step, convert(Integer, fld(stop - start, step)) + 1) _colon(::Any, ::Any, start::T, step, stop::T) where {T} = StepRangeLen(start, step, convert(Integer, fld(stop - start, step)) + 1) """ (:)(start, [step], stop) Range operator. `a:b` constructs a range from `a` to `b` with a step size equal to 1, which produces: * a [`UnitRange`](@ref) when `a` and `b` are integers, or * a [`StepRange`](@ref) when `a` and `b` are characters, or * a [`StepRangeLen`](@ref) when `a` and/or `b` are floating-point. `a:s:b` is similar but uses a step size of `s` (a [`StepRange`](@ref) or [`StepRangeLen`](@ref)). See also [`range`](@ref) for more control. The operator `:` is also used in indexing to select whole dimensions, e.g. in `A[:, 1]`. `:` is also used to [`quote`](@ref) code, e.g. `:(x + y) isa Expr` and `:x isa Symbol`. Since `:2 isa Int`, it does *not* create a range in indexing: `v[:2] == v[2] != v[begin:2]`. """ (:)(start::T, step, stop::T) where {T} = _colon(start, step, stop) (:)(start::T, step, stop::T) where {T<:Real} = _colon(start, step, stop) # without the second method above, the first method above is ambiguous with # (:)(start::A, step, stop::C) where {A<:Real,C<:Real} function _colon(start::T, step, stop::T) where T Tโ€ฒ = typeof(start+zero(step)) StepRange(convert(Tโ€ฒ,start), step, convert(Tโ€ฒ,stop)) end """ range(start, stop, length) range(start, stop; length, step) range(start; length, stop, step) range(;start, length, stop, step) Construct a specialized array with evenly spaced elements and optimized storage (an [`AbstractRange`](@ref)) from the arguments. Mathematically a range is uniquely determined by any three of `start`, `step`, `stop` and `length`. Valid invocations of range are: * Call `range` with any three of `start`, `step`, `stop`, `length`. * Call `range` with two of `start`, `stop`, `length`. In this case `step` will be assumed to be one. If both arguments are Integers, a [`UnitRange`](@ref) will be returned. * Call `range` with one of `stop` or `length`. `start` and `step` will be assumed to be one. See Extended Help for additional details on the returned type. # Examples ```jldoctest julia> range(1, length=100) 1:100 julia> range(1, stop=100) 1:100 julia> range(1, step=5, length=100) 1:5:496 julia> range(1, step=5, stop=100) 1:5:96 julia> range(1, 10, length=101) 1.0:0.09:10.0 julia> range(1, 100, step=5) 1:5:96 julia> range(stop=10, length=5) 6:10 julia> range(stop=10, step=1, length=5) 6:1:10 julia> range(start=1, step=1, stop=10) 1:1:10 julia> range(; length = 10) Base.OneTo(10) julia> range(; stop = 6) Base.OneTo(6) julia> range(; stop = 6.5) 1.0:1.0:6.0 ``` If `length` is not specified and `stop - start` is not an integer multiple of `step`, a range that ends before `stop` will be produced. ```jldoctest julia> range(1, 3.5, step=2) 1.0:2.0:3.0 ``` Special care is taken to ensure intermediate values are computed rationally. To avoid this induced overhead, see the [`LinRange`](@ref) constructor. !!! compat "Julia 1.1" `stop` as a positional argument requires at least Julia 1.1. !!! compat "Julia 1.7" The versions without keyword arguments and `start` as a keyword argument require at least Julia 1.7. !!! compat "Julia 1.8" The versions with `stop` as a sole keyword argument, or `length` as a sole keyword argument require at least Julia 1.8. # Extended Help `range` will produce a `Base.OneTo` when the arguments are Integers and * Only `length` is provided * Only `stop` is provided `range` will produce a `UnitRange` when the arguments are Integers and * Only `start` and `stop` are provided * Only `length` and `stop` are provided A `UnitRange` is not produced if `step` is provided even if specified as one. """ function range end range(start; stop=nothing, length::Union{Integer,Nothing}=nothing, step=nothing) = _range(start, step, stop, length) range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) = _range(start, step, stop, length) range(start, stop, length::Integer) = _range(start, nothing, stop, length) range(;start=nothing, stop=nothing, length::Union{Integer, Nothing}=nothing, step=nothing) = _range(start, step, stop, length) _range(start::Nothing, step::Nothing, stop::Nothing, len::Nothing) = range_error(start, step, stop, len) _range(start::Nothing, step::Nothing, stop::Nothing, len::Any ) = range_length(len) _range(start::Nothing, step::Nothing, stop::Any , len::Nothing) = range_stop(stop) _range(start::Nothing, step::Nothing, stop::Any , len::Any ) = range_stop_length(stop, len) _range(start::Nothing, step::Any , stop::Nothing, len::Nothing) = range_error(start, step, stop, len) _range(start::Nothing, step::Any , stop::Nothing, len::Any ) = range_error(start, step, stop, len) _range(start::Nothing, step::Any , stop::Any , len::Nothing) = range_error(start, step, stop, len) _range(start::Nothing, step::Any , stop::Any , len::Any ) = range_step_stop_length(step, stop, len) _range(start::Any , step::Nothing, stop::Nothing, len::Nothing) = range_error(start, step, stop, len) _range(start::Any , step::Nothing, stop::Nothing, len::Any ) = range_start_length(start, len) _range(start::Any , step::Nothing, stop::Any , len::Nothing) = range_start_stop(start, stop) _range(start::Any , step::Nothing, stop::Any , len::Any ) = range_start_stop_length(start, stop, len) _range(start::Any , step::Any , stop::Nothing, len::Nothing) = range_error(start, step, stop, len) _range(start::Any , step::Any , stop::Nothing, len::Any ) = range_start_step_length(start, step, len) _range(start::Any , step::Any , stop::Any , len::Nothing) = range_start_step_stop(start, step, stop) _range(start::Any , step::Any , stop::Any , len::Any ) = range_error(start, step, stop, len) # Length as the only argument range_length(len::Integer) = OneTo(len) # Stop as the only argument range_stop(stop) = range_start_stop(oftype(stop, 1), stop) range_stop(stop::Integer) = range_length(stop) function range_step_stop_length(step, a, len::Integer) start = a - step * (len - oneunit(len)) if start isa Signed # overflow in recomputing length from stop is okay return StepRange{typeof(start),typeof(step)}(start, step, convert(typeof(start), a)) end return StepRangeLen{typeof(start),typeof(start),typeof(step)}(start, step, len) end # Stop and length as the only argument function range_stop_length(a, len::Integer) step = oftype(a - a, 1) # assert that step is representable start = a - (len - oneunit(len)) if start isa Signed # overflow in recomputing length from stop is okay return UnitRange(start, oftype(start, a)) end return StepRangeLen{typeof(start),typeof(start),typeof(step)}(start, step, len) end # Start and length as the only argument function range_start_length(a, len::Integer) step = oftype(a - a, 1) # assert that step is representable stop = a + (len - oneunit(len)) if stop isa Signed # overflow in recomputing length from stop is okay return UnitRange(oftype(stop, a), stop) end return StepRangeLen{typeof(stop),typeof(a),typeof(step)}(a, step, len) end range_start_stop(start, stop) = start:stop function range_start_step_length(a, step, len::Integer) stop = a + step * (len - oneunit(len)) if stop isa Signed # overflow in recomputing length from stop is okay return StepRange{typeof(stop),typeof(step)}(convert(typeof(stop), a), step, stop) end return StepRangeLen{typeof(stop),typeof(a),typeof(step)}(a, step, len) end range_start_step_stop(start, step, stop) = start:step:stop function range_error(start, step, stop, length) hasstart = start !== nothing hasstep = step !== nothing hasstop = stop !== nothing haslength = start !== nothing hint = if hasstart && hasstep && hasstop && haslength "Try specifying only three arguments" elseif !hasstop && !haslength "At least one of `length` or `stop` must be specified." elseif !hasstep && !haslength "At least one of `length` or `step` must be specified." elseif !hasstart && !hasstop "At least one of `start` or `stop` must be specified." else "Try specifying more arguments." end msg = """ Cannot construct range from arguments: start = $start step = $step stop = $stop length = $length $hint """ throw(ArgumentError(msg)) end ## 1-dimensional ranges ## """ AbstractRange{T} Supertype for ranges with elements of type `T`. [`UnitRange`](@ref) and other types are subtypes of this. """ abstract type AbstractRange{T} <: AbstractArray{T,1} end RangeStepStyle(::Type{<:AbstractRange}) = RangeStepIrregular() RangeStepStyle(::Type{<:AbstractRange{<:Integer}}) = RangeStepRegular() convert(::Type{T}, r::AbstractRange) where {T<:AbstractRange} = r isa T ? r : T(r)::T ## ordinal ranges """ OrdinalRange{T, S} <: AbstractRange{T} Supertype for ordinal ranges with elements of type `T` with spacing(s) of type `S`. The steps should be always-exact multiples of [`oneunit`](@ref), and `T` should be a "discrete" type, which cannot have values smaller than `oneunit`. For example, `Integer` or `Date` types would qualify, whereas `Float64` would not (since this type can represent values smaller than `oneunit(Float64)`. [`UnitRange`](@ref), [`StepRange`](@ref), and other types are subtypes of this. """ abstract type OrdinalRange{T,S} <: AbstractRange{T} end """ AbstractUnitRange{T} <: OrdinalRange{T, T} Supertype for ranges with a step size of [`oneunit(T)`](@ref) with elements of type `T`. [`UnitRange`](@ref) and other types are subtypes of this. """ abstract type AbstractUnitRange{T} <: OrdinalRange{T,T} end """ StepRange{T, S} <: OrdinalRange{T, S} Ranges with elements of type `T` with spacing of type `S`. The step between each element is constant, and the range is defined in terms of a `start` and `stop` of type `T` and a `step` of type `S`. Neither `T` nor `S` should be floating point types. The syntax `a:b:c` with `b != 0` and `a`, `b`, and `c` all integers creates a `StepRange`. # Examples ```jldoctest julia> collect(StepRange(1, Int8(2), 10)) 5-element Vector{Int64}: 1 3 5 7 9 julia> typeof(StepRange(1, Int8(2), 10)) StepRange{Int64, Int8} julia> typeof(1:3:6) StepRange{Int64, Int64} ``` """ struct StepRange{T,S} <: OrdinalRange{T,S} start::T step::S stop::T function StepRange{T,S}(start, step, stop) where {T,S} start = convert(T, start) step = convert(S, step) stop = convert(T, stop) return new(start, step, steprange_last(start, step, stop)) end end # to make StepRange constructor inlineable, so optimizer can see `step` value function steprange_last(start, step, stop)::typeof(stop) if isa(start, AbstractFloat) || isa(step, AbstractFloat) throw(ArgumentError("StepRange should not be used with floating point")) end if isa(start, Integer) && !isinteger(start + step) throw(ArgumentError("StepRange{<:Integer} cannot have non-integer step")) end z = zero(step) step == z && throw(ArgumentError("step cannot be zero")) if stop == start last = stop else if (step > z) != (stop > start) last = steprange_last_empty(start, step, stop) else # Compute absolute value of difference between `start` and `stop` # (to simplify handling both signed and unsigned T and checking for signed overflow): absdiff, absstep = stop > start ? (stop - start, step) : (start - stop, -step) # Compute remainder as a nonnegative number: if absdiff isa Signed && absdiff < zero(absdiff) # unlikely, but handle the signed overflow case with unsigned rem overflow_case(absdiff, absstep) = (@noinline; convert(typeof(absdiff), unsigned(absdiff) % absstep)) remain = overflow_case(absdiff, absstep) else remain = convert(typeof(absdiff), absdiff % absstep) end # Move `stop` closer to `start` if there is a remainder: last = stop > start ? stop - remain : stop + remain end end return last end function steprange_last_empty(start::Integer, step, stop)::typeof(stop) # empty range has a special representation where stop = start-1, # which simplifies arithmetic for Signed numbers if step > zero(step) last = start - oneunit(step) else last = start + oneunit(step) end return last end # For types where x+oneunit(x) may not be well-defined use the user-given value for stop steprange_last_empty(start, step, stop) = stop StepRange{T}(start, step::S, stop) where {T,S} = StepRange{T,S}(start, step, stop) StepRange(start::T, step::S, stop::T) where {T,S} = StepRange{T,S}(start, step, stop) """ UnitRange{T<:Real} A range parameterized by a `start` and `stop` of type `T`, filled with elements spaced by `1` from `start` until `stop` is exceeded. The syntax `a:b` with `a` and `b` both `Integer`s creates a `UnitRange`. # Examples ```jldoctest julia> collect(UnitRange(2.3, 5.2)) 3-element Vector{Float64}: 2.3 3.3 4.3 julia> typeof(1:10) UnitRange{Int64} ``` """ struct UnitRange{T<:Real} <: AbstractUnitRange{T} start::T stop::T UnitRange{T}(start::T, stop::T) where {T<:Real} = new(start, unitrange_last(start, stop)) end UnitRange{T}(start, stop) where {T<:Real} = UnitRange{T}(convert(T, start), convert(T, stop)) UnitRange(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop) function UnitRange(start, stop) startstop_promoted = promote(start, stop) not_sametype((start, stop), startstop_promoted) UnitRange(startstop_promoted...) end # if stop and start are integral, we know that their difference is a multiple of 1 unitrange_last(start::Integer, stop::Integer) = stop >= start ? stop : convert(typeof(stop), start - oneunit(start - stop)) # otherwise, use `floor` as a more efficient way to compute modulus with step=1 unitrange_last(start, stop) = stop >= start ? convert(typeof(stop), start + floor(stop - start)) : convert(typeof(stop), start - oneunit(start - stop)) unitrange(x::AbstractUnitRange) = UnitRange(x) # convenience conversion for promoting the range type if isdefined(Main, :Base) # Constant-fold-able indexing into tuples to functionally expose Base.tail and Base.front function getindex(@nospecialize(t::Tuple), r::AbstractUnitRange) @inline require_one_based_indexing(r) if length(r) <= 10 return ntuple(i -> t[i + first(r) - 1], length(r)) elseif first(r) == 1 last(r) == length(t) && return t last(r) == length(t)-1 && return front(t) last(r) == length(t)-2 && return front(front(t)) elseif last(r) == length(t) first(r) == 2 && return tail(t) first(r) == 3 && return tail(tail(t)) end return (eltype(t)[t[ri] for ri in r]...,) end end """ Base.OneTo(n) Define an `AbstractUnitRange` that behaves like `1:n`, with the added distinction that the lower limit is guaranteed (by the type system) to be 1. """ struct OneTo{T<:Integer} <: AbstractUnitRange{T} stop::T function OneTo{T}(stop) where {T<:Integer} throwbool(r) = (@noinline; throw(ArgumentError("invalid index: $r of type Bool"))) T === Bool && throwbool(stop) return new(max(zero(T), stop)) end function OneTo{T}(r::AbstractRange) where {T<:Integer} throwstart(r) = (@noinline; throw(ArgumentError("first element must be 1, got $(first(r))"))) throwstep(r) = (@noinline; throw(ArgumentError("step must be 1, got $(step(r))"))) throwbool(r) = (@noinline; throw(ArgumentError("invalid index: $r of type Bool"))) first(r) == 1 || throwstart(r) step(r) == 1 || throwstep(r) T === Bool && throwbool(r) return new(max(zero(T), last(r))) end end OneTo(stop::T) where {T<:Integer} = OneTo{T}(stop) OneTo(r::AbstractRange{T}) where {T<:Integer} = OneTo{T}(r) oneto(r) = OneTo(r) ## Step ranges parameterized by length """ StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} StepRangeLen{T,R,S}( ref::R, step::S, len, [offset=1]) where {T,R,S} StepRangeLen{T,R,S,L}(ref::R, step::S, len, [offset=1]) where {T,R,S,L} A range `r` where `r[i]` produces values of type `T` (in the first form, `T` is deduced automatically), parameterized by a `ref`erence value, a `step`, and the `len`gth. By default `ref` is the starting value `r[1]`, but alternatively you can supply it as the value of `r[offset]` for some other index `1 <= offset <= len`. The syntax `a:b` or `a:b:c`, where any of `a`, `b`, or `c` are floating-point numbers, creates a `StepRangeLen`. !!! compat "Julia 1.7" The 4th type parameter `L` requires at least Julia 1.7. """ struct StepRangeLen{T,R,S,L<:Integer} <: AbstractRange{T} ref::R # reference value (might be smallest-magnitude value in the range) step::S # step value len::L # length of the range offset::L # the index of ref function StepRangeLen{T,R,S,L}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S,L} if T <: Integer && !isinteger(ref + step) throw(ArgumentError("StepRangeLen{<:Integer} cannot have non-integer step")) end len = convert(L, len) len >= zero(len) || throw(ArgumentError("length cannot be negative, got $len")) offset = convert(L, offset) L1 = oneunit(typeof(len)) L1 <= offset <= max(L1, len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) return new(ref, step, len, offset) end end StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = StepRangeLen{typeof(ref+zero(step)),R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = StepRangeLen{T,R,S,promote_type(Int,typeof(len))}(ref, step, len, offset) ## range with computed step """ LinRange{T,L} A range with `len` linearly spaced elements between its `start` and `stop`. The size of the spacing is controlled by `len`, which must be an `Integer`. # Examples ```jldoctest julia> LinRange(1.5, 5.5, 9) 9-element LinRange{Float64, Int64}: 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5 ``` Compared to using [`range`](@ref), directly constructing a `LinRange` should have less overhead but won't try to correct for floating point errors: ```jldoctest julia> collect(range(-0.1, 0.3, length=5)) 5-element Vector{Float64}: -0.1 0.0 0.1 0.2 0.3 julia> collect(LinRange(-0.1, 0.3, 5)) 5-element Vector{Float64}: -0.1 -1.3877787807814457e-17 0.09999999999999999 0.19999999999999998 0.3 ``` """ struct LinRange{T,L<:Integer} <: AbstractRange{T} start::T stop::T len::L lendiv::L function LinRange{T,L}(start::T, stop::T, len::L) where {T,L<:Integer} len >= 0 || throw(ArgumentError("range($start, stop=$stop, length=$len): negative length")) onelen = oneunit(typeof(len)) if len == onelen start == stop || throw(ArgumentError("range($start, stop=$stop, length=$len): endpoints differ")) return new(start, stop, len, len) end lendiv = max(len - onelen, onelen) if T <: Integer && !iszero(mod(stop-start, lendiv)) throw(ArgumentError("LinRange{<:Integer} cannot have non-integer step")) end return new(start, stop, len, lendiv) end end function LinRange{T,L}(start, stop, len::Integer) where {T,L} LinRange{T,L}(convert(T, start), convert(T, stop), convert(L, len)) end function LinRange{T}(start, stop, len::Integer) where T LinRange{T,promote_type(Int,typeof(len))}(start, stop, len) end function LinRange(start, stop, len::Integer) T = typeof((zero(stop) - zero(start)) / oneunit(len)) LinRange{T}(start, stop, len) end range_start_stop_length(start, stop, len::Integer) = range_start_stop_length(promote(start, stop)..., len) range_start_stop_length(start::T, stop::T, len::Integer) where {T} = LinRange(start, stop, len) range_start_stop_length(start::T, stop::T, len::Integer) where {T<:Integer} = _linspace(float(T), start, stop, len) ## for Float16, Float32, and Float64 we hit twiceprecision.jl to lift to higher precision StepRangeLen # for all other types we fall back to a plain old LinRange _linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where T = LinRange{T}(start, stop, len) function show(io::IO, r::LinRange{T}) where {T} print(io, "LinRange{") show(io, T) print(io, "}(") ioc = IOContext(io, :typeinfo=>T) show(ioc, first(r)) print(io, ", ") show(ioc, last(r)) print(io, ", ") show(io, length(r)) print(io, ')') end """ `print_range(io, r)` prints out a nice looking range r in terms of its elements as if it were `collect(r)`, dependent on the size of the terminal, and taking into account whether compact numbers should be shown. It figures out the width in characters of each element, and if they end up too wide, it shows the first and last elements separated by a horizontal ellipsis. Typical output will look like `1.0, 2.0, โ€ฆ, 5.0, 6.0`. `print_range(io, r, pre, sep, post, hdots)` uses optional parameters `pre` and `post` characters for each printed row, `sep` separator string between printed elements, `hdots` string for the horizontal ellipsis. """ function print_range(io::IO, r::AbstractRange, pre::AbstractString = " ", sep::AbstractString = ", ", post::AbstractString = "", hdots::AbstractString = ", \u2026, ") # horiz ellipsis # This function borrows from print_matrix() in show.jl # and should be called by show and display sz = displaysize(io) if !haskey(io, :compact) io = IOContext(io, :compact => true) end screenheight, screenwidth = sz[1] - 4, sz[2] screenwidth -= length(pre) + length(post) postsp = "" sepsize = length(sep) m = 1 # treat the range as a one-row matrix n = length(r) # Figure out spacing alignments for r, but only need to examine the # left and right edge columns, as many as could conceivably fit on the # screen, with the middle columns summarized by horz, vert, or diag ellipsis maxpossiblecols = div(screenwidth, 1+sepsize) # assume each element is at least 1 char + 1 separator colsr = n <= maxpossiblecols ? (1:n) : [1:div(maxpossiblecols,2)+1; (n-div(maxpossiblecols,2)):n] rowmatrix = reshape(r[colsr], 1, length(colsr)) # treat the range as a one-row matrix for print_matrix_row nrow, idxlast = size(rowmatrix, 2), last(axes(rowmatrix, 2)) A = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), screenwidth, screenwidth, sepsize, nrow) # how much space range takes if n <= length(A) # cols fit screen, so print out all elements print(io, pre) # put in pre chars print_matrix_row(io,rowmatrix,A,1,1:n,sep,idxlast) # the entire range print(io, post) # add the post characters else # cols don't fit so put horiz ellipsis in the middle # how many chars left after dividing width of screen in half # and accounting for the horiz ellipsis c = div(screenwidth-length(hdots)+1,2)+1 # chars remaining for each side of rowmatrix alignR = reverse(alignment(io, rowmatrix, 1:m, length(rowmatrix):-1:1, c, c, sepsize, nrow)) # which cols of rowmatrix to put on the right c = screenwidth - sum(map(sum,alignR)) - (length(alignR)-1)*sepsize - length(hdots) alignL = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), c, c, sepsize, nrow) # which cols of rowmatrix to put on the left print(io, pre) # put in pre chars print_matrix_row(io, rowmatrix,alignL,1,1:length(alignL),sep,idxlast) # left part of range print(io, hdots) # horizontal ellipsis print_matrix_row(io, rowmatrix,alignR,1,length(rowmatrix)-length(alignR)+1:length(rowmatrix),sep,idxlast) # right part of range print(io, post) # post chars end end ## interface implementations length(r::AbstractRange) = error("length implementation missing") # catch mistakes size(r::AbstractRange) = (length(r),) isempty(r::StepRange) = # steprange_last(r.start, r.step, r.stop) == r.stop (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) isempty(r::AbstractUnitRange) = first(r) > last(r) isempty(r::StepRangeLen) = length(r) == 0 isempty(r::LinRange) = length(r) == 0 """ step(r) Get the step size of an [`AbstractRange`](@ref) object. # Examples ```jldoctest julia> step(1:10) 1 julia> step(1:2:10) 2 julia> step(2.5:0.3:10.9) 0.3 julia> step(range(2.5, stop=10.9, length=85)) 0.1 ``` """ step(r::StepRange) = r.step step(r::AbstractUnitRange{T}) where {T} = oneunit(T) - zero(T) step(r::StepRangeLen) = r.step step(r::StepRangeLen{T}) where {T<:AbstractFloat} = T(r.step) step(r::LinRange) = (last(r)-first(r))/r.lendiv # high-precision step step_hp(r::StepRangeLen) = r.step step_hp(r::AbstractRange) = step(r) axes(r::AbstractRange) = (oneto(length(r)),) # Needed to ensure `has_offset_axes` can constant-fold. has_offset_axes(::StepRange) = false # n.b. checked_length for these is defined iff checked_add and checked_sub are # defined between the relevant types function checked_length(r::OrdinalRange{T}) where T s = step(r) start = first(r) if isempty(r) return Integer(div(start - start, oneunit(s))) end stop = last(r) if isless(s, zero(s)) diff = checked_sub(start, stop) s = -s else diff = checked_sub(stop, start) end a = div(diff, s) return Integer(checked_add(a, oneunit(a))) end function checked_length(r::AbstractUnitRange{T}) where T # compiler optimization: remove dead cases from above if isempty(r) return Integer(first(r) - first(r)) end a = checked_sub(last(r), first(r)) return Integer(checked_add(a, oneunit(a))) end function length(r::OrdinalRange{T}) where T s = step(r) start = first(r) if isempty(r) return Integer(div(start - start, oneunit(s))) end stop = last(r) if isless(s, zero(s)) diff = start - stop s = -s else diff = stop - start end a = div(diff, s) return Integer(a + oneunit(a)) end function length(r::AbstractUnitRange{T}) where T @inline start, stop = first(r), last(r) a = oneunit(zero(stop) - zero(start)) if a isa Signed || stop >= start a += stop - start # Signed are allowed to go negative else a = zero(a) # Unsigned don't necessarily underflow end return Integer(a) end length(r::OneTo) = Integer(r.stop - zero(r.stop)) length(r::StepRangeLen) = r.len length(r::LinRange) = r.len let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}, smallints = (Int === Int64 ? Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} : Union{Int8, UInt8, Int16, UInt16}), bitints = Union{bigints, smallints} global length, checked_length, firstindex # compile optimization for which promote_type(T, Int) == T length(r::OneTo{T}) where {T<:bigints} = r.stop # slightly more accurate length and checked_length in extreme cases # (near typemax) for types with known `unsigned` functions function length(r::OrdinalRange{T}) where T<:bigints s = step(r) diff = last(r) - first(r) isempty(r) && return zero(diff) # if |s| > 1, diff might have overflowed, but unsigned(diff)รทs should # therefore still be valid (if the result is representable at all) # n.b. !(s isa T) if s isa Unsigned || -1 <= s <= 1 || s == -s a = div(diff, s) % typeof(diff) elseif s < 0 a = div(unsigned(-diff), -s) % typeof(diff) else a = div(unsigned(diff), s) % typeof(diff) end return a + oneunit(a) end function checked_length(r::OrdinalRange{T}) where T<:bigints s = step(r) stop, start = last(r), first(r) ET = promote_type(typeof(stop), typeof(start)) isempty(r) && return zero(ET) # n.b. !(s isa T) if s > 1 diff = stop - start a = convert(ET, div(unsigned(diff), s)) elseif s < -1 diff = start - stop a = convert(ET, div(unsigned(diff), -s)) elseif s > 0 a = convert(ET, div(checked_sub(stop, start), s)) else a = convert(ET, div(checked_sub(start, stop), -s)) end return checked_add(a, oneunit(a)) end firstindex(r::StepRange{<:bigints,<:bitints}) = one(last(r)-first(r)) # some special cases to favor default Int type function length(r::OrdinalRange{<:smallints}) s = step(r) isempty(r) && return 0 # n.b. !(step isa T) return Int(div(Int(last(r)) - Int(first(r)), s)) + 1 end length(r::AbstractUnitRange{<:smallints}) = Int(last(r)) - Int(first(r)) + 1 length(r::OneTo{<:smallints}) = Int(r.stop) checked_length(r::OrdinalRange{<:smallints}) = length(r) checked_length(r::AbstractUnitRange{<:smallints}) = length(r) checked_length(r::OneTo{<:smallints}) = length(r) firstindex(::StepRange{<:smallints,<:bitints}) = 1 end first(r::OrdinalRange{T}) where {T} = convert(T, r.start) first(r::OneTo{T}) where {T} = oneunit(T) first(r::StepRangeLen) = unsafe_getindex(r, 1) first(r::LinRange) = r.start last(r::OrdinalRange{T}) where {T} = convert(T, r.stop) # via steprange_last last(r::StepRangeLen) = unsafe_getindex(r, length(r)) last(r::LinRange) = r.stop minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) minimum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) maximum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) """ argmin(r::AbstractRange) Ranges can have multiple minimal elements. In that case `argmin` will return a minimal index, but not necessarily the first one. """ function argmin(r::AbstractRange) if isempty(r) throw(ArgumentError("range must be non-empty")) elseif step(r) > 0 firstindex(r) else lastindex(r) end end """ argmax(r::AbstractRange) Ranges can have multiple maximal elements. In that case `argmax` will return a maximal index, but not necessarily the first one. """ function argmax(r::AbstractRange) if isempty(r) throw(ArgumentError("range must be non-empty")) elseif step(r) > 0 lastindex(r) else firstindex(r) end end extrema(r::AbstractRange) = (minimum(r), maximum(r)) # Ranges are immutable copy(r::AbstractRange) = r ## iteration function iterate(r::Union{StepRangeLen,LinRange}, i::Integer=zero(length(r))) @inline i += oneunit(i) length(r) < i && return nothing unsafe_getindex(r, i), i end iterate(r::OrdinalRange) = isempty(r) ? nothing : (first(r), first(r)) function iterate(r::OrdinalRange{T}, i) where {T} @inline i == last(r) && return nothing next = convert(T, i + step(r)) (next, next) end ## indexing function isassigned(r::AbstractRange, i::Integer) i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) firstindex(r) <= i <= lastindex(r) end _in_unit_range(v::UnitRange, val, i::Integer) = i > 0 && val <= v.stop && val >= v.start function getindex(v::UnitRange{T}, i::Integer) where T @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) val = convert(T, v.start + (i - oneunit(i))) @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) val end const OverflowSafe = Union{Bool,Int8,Int16,Int32,Int64,Int128, UInt8,UInt16,UInt32,UInt64,UInt128} function getindex(v::UnitRange{T}, i::Integer) where {T<:OverflowSafe} @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) val = v.start + (i - oneunit(i)) @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) val % T end function getindex(v::OneTo{T}, i::Integer) where T @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) @boundscheck ((i > 0) & (i <= v.stop)) || throw_boundserror(v, i) convert(T, i) end function getindex(v::AbstractRange{T}, i::Integer) where T @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) ret = convert(T, first(v) + (i - oneunit(i))*step_hp(v)) ok = ifelse(step(v) > zero(step(v)), (ret <= last(v)) & (ret >= first(v)), (ret <= first(v)) & (ret >= last(v))) @boundscheck ((i > 0) & ok) || throw_boundserror(v, i) ret end function getindex(r::Union{StepRangeLen,LinRange}, i::Integer) @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) @boundscheck checkbounds(r, i) unsafe_getindex(r, i) end # This is separate to make it useful even when running with --check-bounds=yes function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) u = oftype(r.offset, i) - r.offset T(r.ref + u*r.step) end function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) u = oftype(r.offset, i) - r.offset r.ref + u*r.step end function unsafe_getindex(r::LinRange, i::Integer) i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) lerpi(i-oneunit(i), r.lendiv, r.start, r.stop) end function lerpi(j::Integer, d::Integer, a::T, b::T) where T @inline t = j/d # โˆˆ [0,1] # compute approximately fma(t, b, -fma(t, a, a)) return T((1-t)*a + t*b) end getindex(r::AbstractRange, ::Colon) = copy(r) function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integer} @inline @boundscheck checkbounds(r, s) if T === Bool return range(first(s) ? first(r) : last(r), length = last(s)) else f = first(r) start = oftype(f, f + first(s) - firstindex(r)) len = length(s) stop = oftype(f, start + (len - oneunit(len))) return range(start, stop) end end function getindex(r::OneTo{T}, s::OneTo) where T @inline @boundscheck checkbounds(r, s) return OneTo(T(s.stop)) end function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer} @inline @boundscheck checkbounds(r, s) if T === Bool return range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length=last(s)) else f = first(r) start = oftype(f, f + s.start - firstindex(r)) st = step(s) len = length(s) stop = oftype(f, start + (len - oneunit(len)) * st) return range(start, stop; step=st) end end function getindex(r::StepRange, s::AbstractRange{T}) where {T<:Integer} @inline @boundscheck checkbounds(r, s) if T === Bool if length(s) == 0 start, len = first(r), 0 elseif length(s) == 1 if first(s) start, len = first(r), 1 else start, len = first(r), 0 end else # length(s) == 2 start, len = last(r), 1 end return range(start, step=step(r); length=len) else f = r.start fs = first(s) st = r.step start = oftype(f, f + (fs - oneunit(fs)) * st) st = st * step(s) len = length(s) stop = oftype(f, start + (len - oneunit(len)) * st) return range(start, stop; step=st) end end function getindex(r::StepRangeLen{T}, s::OrdinalRange{S}) where {T, S<:Integer} @inline @boundscheck checkbounds(r, s) len = length(s) sstep = step_hp(s) rstep = step_hp(r) L = typeof(len) if S === Bool rstep *= one(sstep) if len == 0 return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) elseif len == 1 if first(s) return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end else # len == 2 return StepRangeLen{T}(last(r), rstep, oneunit(L), oneunit(L)) end else # Find closest approach to offset by s ind = LinearIndices(s) offset = L(max(min(1 + round(L, (r.offset - first(s))/sstep), last(ind)), first(ind))) ref = _getindex_hiprec(r, first(s) + (offset - oneunit(offset)) * sstep) return StepRangeLen{T}(ref, rstep*sstep, len, offset) end end function getindex(r::LinRange{T}, s::OrdinalRange{S}) where {T, S<:Integer} @inline @boundscheck checkbounds(r, s) len = length(s) L = typeof(len) if S === Bool if len == 0 return LinRange{T}(first(r), first(r), len) elseif len == 1 if first(s) return LinRange{T}(first(r), first(r), len) else return LinRange{T}(first(r), first(r), zero(L)) end else # length(s) == 2 return LinRange{T}(last(r), last(r), oneunit(L)) end else vfirst = unsafe_getindex(r, first(s)) vlast = unsafe_getindex(r, last(s)) return LinRange{T}(vfirst, vlast, len) end end show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") function show(io::IO, r::StepRangeLen) if !iszero(step(r)) print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) else # ugly temporary printing, to avoid 0:0:0 etc. print(io, "StepRangeLen(", repr(first(r)), ", ", repr(step(r)), ", ", repr(length(r)), ")") end end function ==(r::T, s::T) where {T<:AbstractRange} isempty(r) && return isempty(s) _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) end function ==(r::OrdinalRange, s::OrdinalRange) isempty(r) && return isempty(s) _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) end ==(r::AbstractUnitRange, s::AbstractUnitRange) = (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (last(r) == last(s))) ==(r::OneTo, s::OneTo) = last(r) == last(s) ==(r::T, s::T) where {T<:Union{StepRangeLen,LinRange}} = (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (length(r) == length(s)) & (last(r) == last(s))) function ==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} isempty(r) && return isempty(s) _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) end _has_length_one(r::OrdinalRange) = first(r) == last(r) _has_length_one(r::AbstractRange) = isone(length(r)) function ==(r::AbstractRange, s::AbstractRange) lr = length(r) if lr != length(s) return false elseif iszero(lr) return true end yr, ys = iterate(r), iterate(s) while yr !== nothing yr[1] == ys[1] || return false yr, ys = iterate(r, yr[2]), iterate(s, ys[2]) end return true end intersect(r::OneTo, s::OneTo) = OneTo(min(r.stop,s.stop)) union(r::OneTo, s::OneTo) = OneTo(max(r.stop,s.stop)) intersect(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = max(first(r),first(s)):min(last(r),last(s)) intersect(i::Integer, r::AbstractUnitRange{<:Integer}) = range(max(i, first(r)), length=in(i, r)) intersect(r::AbstractUnitRange{<:Integer}, i::Integer) = intersect(i, r) function intersect(r::AbstractUnitRange{<:Integer}, s::StepRange{<:Integer}) T = promote_type(eltype(r), eltype(s)) if isempty(s) StepRange{T}(first(r), +step(s), first(r)-step(s)) else sta, ste, sto = first_step_last_ascending(s) lo = first(r) hi = last(r) i0 = max(sta, lo + mod(sta - lo, ste)) i1 = min(sto, hi - mod(hi - sta, ste)) StepRange{T}(i0, ste, i1) end end function first_step_last_ascending(r::StepRange) if step(r) < zero(step(r)) last(r), -step(r), first(r) else first(r), +step(r), last(r) end end function intersect(r::StepRange{<:Integer}, s::AbstractUnitRange{<:Integer}) if step(r) < 0 reverse(intersect(s, reverse(r))) else intersect(s, r) end end function intersect(r::StepRange, s::StepRange) T = promote_type(eltype(r), eltype(s)) S = promote_type(typeof(step(r)), typeof(step(s))) if isempty(r) || isempty(s) return StepRange{T,S}(first(r), step(r), first(r)-step(r)) end start1, step1, stop1 = first_step_last_ascending(r) start2, step2, stop2 = first_step_last_ascending(s) a = lcm(step1, step2) g, x, y = gcdx(step1, step2) if !iszero(rem(start1 - start2, g)) # Unaligned, no overlap possible. if step(r) < zero(step(r)) return StepRange{T,S}(stop1, -a, stop1+a) else return StepRange{T,S}(start1, a, start1-a) end end z = div(start1 - start2, g) b = start1 - x * z * step1 # Possible points of the intersection of r and s are # ..., b-2a, b-a, b, b+a, b+2a, ... # Determine where in the sequence to start and stop. m = max(start1 + mod(b - start1, a), start2 + mod(b - start2, a)) n = min(stop1 - mod(stop1 - b, a), stop2 - mod(stop2 - b, a)) step(r) < zero(step(r)) ? StepRange{T,S}(n, -a, m) : StepRange{T,S}(m, a, n) end function intersect(r1::AbstractRange, r2::AbstractRange) # To iterate over the shorter range length(r1) > length(r2) && return intersect(r2, r1) r1 = unique(r1) T = promote_eltype(r1, r2) return T[x for x in r1 if x โˆˆ r2] end function intersect(r1::AbstractRange, r2::AbstractRange, r3::AbstractRange, r::AbstractRange...) i = intersect(intersect(r1, r2), r3) for t in r i = intersect(i, t) end i end # _findin (the index of intersection) function _findin(r::AbstractRange{<:Integer}, span::AbstractUnitRange{<:Integer}) fspan = first(span) lspan = last(span) fr = first(r) lr = last(r) sr = step(r) if sr > 0 ifirst = fr >= fspan ? 1 : cld(fspan-fr, sr)+1 ilast = lr <= lspan ? length(r) : length(r) - cld(lr-lspan, sr) elseif sr < 0 ifirst = fr <= lspan ? 1 : cld(lspan-fr, sr)+1 ilast = lr >= fspan ? length(r) : length(r) - cld(lr-fspan, sr) else ifirst = fr >= fspan ? 1 : length(r)+1 ilast = fr <= lspan ? length(r) : 0 end r isa AbstractUnitRange ? (ifirst:ilast) : (ifirst:1:ilast) end issubset(r::OneTo, s::OneTo) = r.stop <= s.stop issubset(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = isempty(r) || (first(r) >= first(s) && last(r) <= last(s)) ## linear operations on ranges ## -(r::OrdinalRange) = range(-first(r), step=negate(step(r)), length=length(r)) -(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = StepRangeLen{T,R,S,L}(-r.ref, -r.step, r.len, r.offset) function -(r::LinRange) start = -r.start LinRange{typeof(start)}(start, -r.stop, length(r)) end # promote eltype if at least one container wouldn't change, otherwise join container types. el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n} = a # we assume a === b el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{S,n}}) where {T,S,n} = a el_same(::Type{T}, a::Type{<:AbstractArray{S,n}}, b::Type{<:AbstractArray{T,n}}) where {T,S,n} = b el_same(::Type, a, b) = promote_typejoin(a, b) promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = el_same(promote_type(T1, T2), a, b) UnitRange{T}(r::UnitRange{T}) where {T<:Real} = r UnitRange{T}(r::UnitRange) where {T<:Real} = UnitRange{T}(r.start, r.stop) promote_rule(a::Type{OneTo{T1}}, b::Type{OneTo{T2}}) where {T1,T2} = el_same(promote_type(T1, T2), a, b) OneTo{T}(r::OneTo{T}) where {T<:Integer} = r OneTo{T}(r::OneTo) where {T<:Integer} = OneTo{T}(r.stop) promote_rule(a::Type{UnitRange{T1}}, ::Type{UR}) where {T1,UR<:AbstractUnitRange} = promote_rule(a, UnitRange{eltype(UR)}) UnitRange{T}(r::AbstractUnitRange) where {T<:Real} = UnitRange{T}(first(r), last(r)) UnitRange(r::AbstractUnitRange) = UnitRange(first(r), last(r)) AbstractUnitRange{T}(r::AbstractUnitRange{T}) where {T} = r AbstractUnitRange{T}(r::UnitRange) where {T} = UnitRange{T}(r) AbstractUnitRange{T}(r::OneTo) where {T} = OneTo{T}(r) OrdinalRange{T, S}(r::OrdinalRange) where {T, S} = StepRange{T, S}(r) OrdinalRange{T, T}(r::AbstractUnitRange) where {T} = AbstractUnitRange{T}(r) function promote_rule(::Type{StepRange{T1a,T1b}}, ::Type{StepRange{T2a,T2b}}) where {T1a,T1b,T2a,T2b} Tb = promote_type(T1b, T2b) # el_same only operates on array element type, so just promote second type parameter el_same(promote_type(T1a, T2a), StepRange{T1a,Tb}, StepRange{T2a,Tb}) end StepRange{T1,T2}(r::StepRange{T1,T2}) where {T1,T2} = r promote_rule(a::Type{StepRange{T1a,T1b}}, ::Type{UR}) where {T1a,T1b,UR<:AbstractUnitRange} = promote_rule(a, StepRange{eltype(UR), eltype(UR)}) StepRange{T1,T2}(r::AbstractRange) where {T1,T2} = StepRange{T1,T2}(convert(T1, first(r)), convert(T2, step(r)), convert(T1, last(r))) StepRange(r::AbstractUnitRange{T}) where {T} = StepRange{T,T}(first(r), step(r), last(r)) (StepRange{T1,T2} where T1)(r::AbstractRange) where {T2} = StepRange{eltype(r),T2}(r) function promote_rule(::Type{StepRangeLen{T1,R1,S1,L1}},::Type{StepRangeLen{T2,R2,S2,L2}}) where {T1,T2,R1,R2,S1,S2,L1,L2} R, S, L = promote_type(R1, R2), promote_type(S1, S2), promote_type(L1, L2) el_same(promote_type(T1, T2), StepRangeLen{T1,R,S,L}, StepRangeLen{T2,R,S,L}) end StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T,R,S,L} = r StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T,R,S,L} = StepRangeLen{T,R,S,L}(convert(R, r.ref), convert(S, r.step), convert(L, r.len), convert(L, r.offset)) StepRangeLen{T}(r::StepRangeLen) where {T} = StepRangeLen(convert(T, r.ref), convert(T, r.step), r.len, r.offset) promote_rule(a::Type{StepRangeLen{T,R,S,L}}, ::Type{OR}) where {T,R,S,L,OR<:AbstractRange} = promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR), Int}) StepRangeLen{T,R,S,L}(r::AbstractRange) where {T,R,S,L} = StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), length(r)) StepRangeLen{T}(r::AbstractRange) where {T} = StepRangeLen(T(first(r)), T(step(r)), length(r)) StepRangeLen(r::AbstractRange) = StepRangeLen{eltype(r)}(r) function promote_rule(a::Type{LinRange{T1,L1}}, b::Type{LinRange{T2,L2}}) where {T1,T2,L1,L2} L = promote_type(L1, L2) el_same(promote_type(T1, T2), LinRange{T1,L}, LinRange{T2,L}) end LinRange{T,L}(r::LinRange{T,L}) where {T,L} = r LinRange{T,L}(r::AbstractRange) where {T,L} = LinRange{T,L}(first(r), last(r), length(r)) LinRange{T}(r::AbstractRange) where {T} = LinRange{T,typeof(length(r))}(first(r), last(r), length(r)) LinRange(r::AbstractRange{T}) where {T} = LinRange{T}(r) promote_rule(a::Type{LinRange{T,L}}, ::Type{OR}) where {T,L,OR<:OrdinalRange} = promote_rule(a, LinRange{eltype(OR),L}) promote_rule(::Type{LinRange{A,L}}, b::Type{StepRangeLen{T2,R2,S2,L2}}) where {A,L,T2,R2,S2,L2} = promote_rule(StepRangeLen{A,A,A,L}, b) ## concatenation ## function vcat(rs::AbstractRange{T}...) where T n::Int = 0 for ra in rs n += length(ra) end a = Vector{T}(undef, n) i = 1 for ra in rs, x in ra @inbounds a[i] = x i += 1 end return a end # This method differs from that for AbstractArrays as it # use iteration instead of indexing. This works even if certain # non-standard ranges don't support indexing. # See https://github.com/JuliaLang/julia/pull/27302 # Similarly, collect(r::AbstractRange) uses iteration function Array{T,1}(r::AbstractRange{T}) where {T} a = Vector{T}(undef, length(r)) i = 1 for x in r @inbounds a[i] = x i += 1 end return a end collect(r::AbstractRange) = Array(r) _reverse(r::OrdinalRange, ::Colon) = (:)(last(r), negate(step(r)), first(r)) function _reverse(r::StepRangeLen, ::Colon) # If `r` is empty, `length(r) - r.offset + 1 will be nonpositive hence # invalid. As `reverse(r)` is also empty, any offset would work so we keep # `r.offset` offset = isempty(r) ? r.offset : length(r)-r.offset+1 return typeof(r)(r.ref, negate(r.step), length(r), offset) end _reverse(r::LinRange{T}, ::Colon) where {T} = typeof(r)(r.stop, r.start, length(r)) ## sorting ## issorted(r::AbstractUnitRange) = true issorted(r::AbstractRange) = length(r) <= 1 || step(r) >= zero(step(r)) sort(r::AbstractUnitRange) = r sort!(r::AbstractUnitRange) = r sort(r::AbstractRange) = issorted(r) ? r : reverse(r) sortperm(r::AbstractUnitRange) = 1:length(r) sortperm(r::AbstractRange) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) function sum(r::AbstractRange{<:Real}) l = length(r) # note that a little care is required to avoid overflow in l*(l-1)/2 return l * first(r) + (iseven(l) ? (step(r) * (l-1)) * (l>>1) : (step(r) * l) * ((l-1)>>1)) end function _in_range(x, r::AbstractRange) isempty(r) && return false f, l = first(r), last(r) # check for NaN, Inf, and large x that may overflow in the next calculation f <= x <= l || l <= x <= f || return false iszero(step(r)) && return true n = round(Integer, (x - f) / step(r)) + 1 n >= 1 && n <= length(r) && r[n] == x end in(x::Real, r::AbstractRange{<:Real}) = _in_range(x, r) # This method needs to be defined separately since -(::T, ::T) can be implemented # even if -(::T, ::Real) is not in(x::T, r::AbstractRange{T}) where {T} = _in_range(x, r) in(x::Integer, r::AbstractUnitRange{<:Integer}) = (first(r) <= x) & (x <= last(r)) in(x::Real, r::AbstractRange{T}) where {T<:Integer} = isinteger(x) && !isempty(r) && (iszero(step(r)) ? x == first(r) : (x >= minimum(r) && x <= maximum(r) && (mod(convert(T,x),step(r))-mod(first(r),step(r)) == 0))) in(x::AbstractChar, r::AbstractRange{<:AbstractChar}) = !isempty(r) && (iszero(step(r)) ? x == first(r) : (x >= minimum(r) && x <= maximum(r) && (mod(Int(x) - Int(first(r)), step(r)) == 0))) # Addition/subtraction of ranges function _define_range_op(@nospecialize f) @eval begin function $f(r1::OrdinalRange, r2::OrdinalRange) r1l = length(r1) (r1l == length(r2) || throw(DimensionMismatch("argument dimensions must match: length of r1 is $r1l, length of r2 is $(length(r2))"))) StepRangeLen($f(first(r1), first(r2)), $f(step(r1), step(r2)), r1l) end function $f(r1::LinRange{T}, r2::LinRange{T}) where T len = r1.len (len == r2.len || throw(DimensionMismatch("argument dimensions must match: length of r1 is $len, length of r2 is $(r2.len)"))) LinRange{T}(convert(T, $f(first(r1), first(r2))), convert(T, $f(last(r1), last(r2))), len) end $f(r1::Union{StepRangeLen, OrdinalRange, LinRange}, r2::Union{StepRangeLen, OrdinalRange, LinRange}) = $f(promote(r1, r2)...) end end _define_range_op(:+) _define_range_op(:-) function +(r1::StepRangeLen{T,S}, r2::StepRangeLen{T,S}) where {T,S} len = length(r1) (len == length(r2) || throw(DimensionMismatch("argument dimensions must match: length of r1 is $len, length of r2 is $(length(r2))"))) StepRangeLen(first(r1)+first(r2), step(r1)+step(r2), len) end -(r1::StepRangeLen, r2::StepRangeLen) = +(r1, -r2) # Modular arithmetic on ranges """ mod(x::Integer, r::AbstractUnitRange) Find `y` in the range `r` such that ``x โ‰ก y (mod n)``, where `n = length(r)`, i.e. `y = mod(x - first(r), n) + first(r)`. See also [`mod1`](@ref). # Examples ```jldoctest julia> mod(0, Base.OneTo(3)) # mod1(0, 3) 3 julia> mod(3, 0:2) # mod(3, 3) 0 ``` !!! compat "Julia 1.3" This method requires at least Julia 1.3. """ mod(i::Integer, r::OneTo) = mod1(i, last(r)) mod(i::Integer, r::AbstractUnitRange{<:Integer}) = mod(i-first(r), length(r)) + first(r) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/error.jl9)# This file is a part of Julia. License is MIT: https://julialang.org/license # pseudo-definitions to show how everything behaves # # throw(label, val) = # throw a value to a dynamically enclosing block # # function rethrow(val) # global current_exception = val # throw(current_handler(), current_exception) # end # # rethrow() = rethrow(current_exception) # # function throw(val) # global catch_backtrace = backtrace() # rethrow(val) # end """ throw(e) Throw an object as an exception. See also: [`rethrow`](@ref), [`error`](@ref). """ throw ## native julia error handling ## """ error(message::AbstractString) Raise an `ErrorException` with the given message. """ error(s::AbstractString) = throw(ErrorException(s)) """ error(msg...) Raise an `ErrorException` with the given message. """ function error(s::Vararg{Any,N}) where {N} @noinline throw(ErrorException(Main.Base.string(s...))) end """ rethrow() Rethrow the current exception from within a `catch` block. The rethrown exception will continue propagation as if it had not been caught. !!! note The alternative form `rethrow(e)` allows you to associate an alternative exception object `e` with the current backtrace. However this misrepresents the program state at the time of the error so you're encouraged to instead throw a new exception using `throw(e)`. In Julia 1.1 and above, using `throw(e)` will preserve the root cause exception on the stack, as described in [`current_exceptions`](@ref). """ rethrow() = ccall(:jl_rethrow, Bottom, ()) rethrow(@nospecialize(e)) = ccall(:jl_rethrow_other, Bottom, (Any,), e) struct InterpreterIP code::Union{CodeInfo,Core.MethodInstance,Nothing} stmt::Csize_t mod::Union{Module,Nothing} end # convert dual arrays (raw bt buffer, array of GC managed values) to a single # array of locations function _reformat_bt(bt::Array{Ptr{Cvoid},1}, bt2::Array{Any,1}) ret = Vector{Union{InterpreterIP,Ptr{Cvoid}}}() i, j = 1, 1 while i <= length(bt) ip = bt[i]::Ptr{Cvoid} if UInt(ip) != (-1 % UInt) # See also jl_bt_is_native # native frame push!(ret, ip) i += 1 continue end # Extended backtrace entry entry_metadata = reinterpret(UInt, bt[i+1])::UInt njlvalues = entry_metadata & 0x7 nuintvals = (entry_metadata >> 3) & 0x7 tag = (entry_metadata >> 6) & 0xf header = entry_metadata >> 10 if tag == 1 # JL_BT_INTERP_FRAME_TAG code = bt2[j]::Union{CodeInfo,Core.MethodInstance,Nothing} mod = njlvalues == 2 ? bt2[j+1]::Union{Module,Nothing} : nothing push!(ret, InterpreterIP(code, header, mod)) else # Tags we don't know about are an error throw(ArgumentError("Unexpected extended backtrace entry tag $tag at bt[$i]")) end # See jl_bt_entry_size j += Int(njlvalues) i += 2 + Int(njlvalues + nuintvals) end ret end """ backtrace() Get a backtrace object for the current program point. """ function backtrace() @noinline # skip frame for backtrace(). Note that for this to work properly, # backtrace() itself must not be interpreted nor inlined. skip = 1 bt1, bt2 = ccall(:jl_backtrace_from_here, Ref{SimpleVector}, (Cint, Cint), false, skip) return _reformat_bt(bt1::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) end """ catch_backtrace() Get the backtrace of the current exception, for use within `catch` blocks. """ function catch_backtrace() bt, bt2 = ccall(:jl_get_backtrace, Ref{SimpleVector}, ()) return _reformat_bt(bt::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) end struct ExceptionStack <: AbstractArray{Any,1} stack::Array{Any,1} end """ current_exceptions(task::Task=current_task(); [backtrace::Bool=true]) Get the stack of exceptions currently being handled. For nested catch blocks there may be more than one current exception in which case the most recently thrown exception is last in the stack. The stack is returned as an `ExceptionStack` which is an AbstractVector of named tuples `(exception,backtrace)`. If `backtrace` is false, the backtrace in each pair will be set to `nothing`. Explicitly passing `task` will return the current exception stack on an arbitrary task. This is useful for inspecting tasks which have failed due to uncaught exceptions. !!! compat "Julia 1.7" This function went by the experimental name `catch_stack()` in Julia 1.1โ€“1.6, and had a plain Vector-of-tuples as a return type. """ function current_exceptions(task::Task=current_task(); backtrace::Bool=true) raw = ccall(:jl_get_excstack, Any, (Any,Cint,Cint), task, backtrace, typemax(Cint))::Vector{Any} formatted = Any[] stride = backtrace ? 3 : 1 for i = reverse(1:stride:length(raw)) exc = raw[i] bt = backtrace ? Base._reformat_bt(raw[i+1],raw[i+2]) : nothing push!(formatted, (exception=exc,backtrace=bt)) end ExceptionStack(formatted) end ## keyword arg lowering generates calls to this ## function kwerr(kw, args::Vararg{Any,N}) where {N} @noinline throw(MethodError(Core.kwcall, (kw, args...))) end ## system error handling ## """ systemerror(sysfunc[, errno::Cint=Libc.errno()]) systemerror(sysfunc, iftrue::Bool) Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true` """ systemerror(p, b::Bool; extrainfo=nothing) = b ? systemerror(p, extrainfo=extrainfo) : nothing systemerror(p, errno::Cint=Libc.errno(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), errno, extrainfo)) ## system errors from Windows API functions struct WindowsErrorInfo errnum::UInt32 extrainfo end """ windowserror(sysfunc[, code::UInt32=Libc.GetLastError()]) windowserror(sysfunc, iftrue::Bool) Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref Base.Libc.GetLastError) to return an error code instead of setting [`errno`](@ref Base.Libc.errno). """ windowserror(p, b::Bool; extrainfo=nothing) = b ? windowserror(p, extrainfo=extrainfo) : nothing windowserror(p, code::UInt32=Libc.GetLastError(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), 0, WindowsErrorInfo(code, extrainfo))) ## assertion macro ## """ @assert cond [text] Throw an [`AssertionError`](@ref) if `cond` is `false`. Preferred syntax for writing assertions. Message `text` is optionally displayed upon assertion failure. !!! warning An assert might be disabled at various optimization levels. Assert should therefore only be used as a debugging tool and not used for authentication verification (e.g., verifying passwords), nor should side effects needed for the function to work correctly be used inside of asserts. # Examples ```jldoctest julia> @assert iseven(3) "3 is an odd number!" ERROR: AssertionError: 3 is an odd number! julia> @assert isodd(3) "What even are numbers?" ``` """ macro assert(ex, msgs...) msg = isempty(msgs) ? ex : msgs[1] if isa(msg, AbstractString) msg = msg # pass-through elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol)) # message is an expression needing evaluating msg = :(Main.Base.string($(esc(msg)))) elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg) msg = Main.Base.string(msg) else # string() might not be defined during bootstrap msg = quote msg = $(Expr(:quote,msg)) isdefined(Main, :Base) ? Main.Base.string(msg) : (Core.println(msg); "Error during bootstrap. See stdout.") end end return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end struct ExponentialBackOff n::Int first_delay::Float64 max_delay::Float64 factor::Float64 jitter::Float64 function ExponentialBackOff(n, first_delay, max_delay, factor, jitter) all(x->x>=0, (n, first_delay, max_delay, factor, jitter)) || error("all inputs must be non-negative") new(n, first_delay, max_delay, factor, jitter) end end """ ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) A [`Float64`](@ref) iterator of length `n` whose elements exponentially increase at a rate in the interval `factor` * (1 ยฑ `jitter`). The first element is `first_delay` and all elements are clamped to `max_delay`. """ ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) = ExponentialBackOff(n, first_delay, max_delay, factor, jitter) function iterate(ebo::ExponentialBackOff, state= (ebo.n, min(ebo.first_delay, ebo.max_delay))) state[1] < 1 && return nothing next_n = state[1]-1 curr_delay = state[2] next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (Libc.rand(Float64) * 2.0 * ebo.jitter))) (curr_delay, (next_n, next_delay)) end length(ebo::ExponentialBackOff) = ebo.n eltype(::Type{ExponentialBackOff}) = Float64 """ retry(f; delays=ExponentialBackOff(), check=nothing) -> Function Return an anonymous function that calls function `f`. If an exception arises, `f` is repeatedly called again, each time `check` returns `true`, after waiting the number of seconds specified in `delays`. `check` should input `delays`'s current state and the `Exception`. !!! compat "Julia 1.2" Before Julia 1.2 this signature was restricted to `f::Function`. # Examples ```julia retry(f, delays=fill(5.0, 3)) retry(f, delays=rand(5:10, 2)) retry(f, delays=Base.ExponentialBackOff(n=3, first_delay=5, max_delay=1000)) retry(http_get, check=(s,e)->e.status == "503")(url) retry(read, check=(s,e)->isa(e, IOError))(io, 128; all=false) ``` """ function retry(f; delays=ExponentialBackOff(), check=nothing) (args...; kwargs...) -> begin y = iterate(delays) while y !== nothing (delay, state) = y try return f(args...; kwargs...) catch e if check !== nothing result = check(state, e) state, retry_or_not = length(result) == 2 ? result : (state, result) retry_or_not || rethrow() end end sleep(delay) y = iterate(delays, state) end # When delays is out, just run the function without try/catch return f(args...; kwargs...) end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/bool.jlด# This file is a part of Julia. License is MIT: https://julialang.org/license # promote Bool to any other numeric type promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T typemin(::Type{Bool}) = false typemax(::Type{Bool}) = true ## boolean operations ## """ !(x) Boolean not. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if `x` is `missing`. See also [`~`](@ref) for bitwise not. # Examples ```jldoctest julia> !true false julia> !false true julia> !missing missing julia> .![true false true] 1ร—3 BitMatrix: 0 1 0 ``` """ !(x::Bool) = not_int(x) (~)(x::Bool) = !x (&)(x::Bool, y::Bool) = and_int(x, y) (|)(x::Bool, y::Bool) = or_int(x, y) """ xor(x, y) โŠป(x, y) Bitwise exclusive or of `x` and `y`. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if one of the arguments is `missing`. The infix operation `a โŠป b` is a synonym for `xor(a,b)`, and `โŠป` can be typed by tab-completing `\\xor` or `\\veebar` in the Julia REPL. # Examples ```jldoctest julia> xor(true, false) true julia> xor(true, true) false julia> xor(true, missing) missing julia> false โŠป false false julia> [true; true; false] .โŠป [true; false; false] 3-element BitVector: 0 1 0 ``` """ xor(x::Bool, y::Bool) = (x != y) """ nand(x, y) โŠผ(x, y) Bitwise nand (not and) of `x` and `y`. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if one of the arguments is `missing`. The infix operation `a โŠผ b` is a synonym for `nand(a,b)`, and `โŠผ` can be typed by tab-completing `\\nand` or `\\barwedge` in the Julia REPL. # Examples ```jldoctest julia> nand(true, false) true julia> nand(true, true) false julia> nand(true, missing) missing julia> false โŠผ false true julia> [true; true; false] .โŠผ [true; false; false] 3-element BitVector: 0 1 1 ``` """ nand(x...) = ~(&)(x...) """ nor(x, y) โŠฝ(x, y) Bitwise nor (not or) of `x` and `y`. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if one of the arguments is `missing` and the other is not `true`. The infix operation `a โŠฝ b` is a synonym for `nor(a,b)`, and `โŠฝ` can be typed by tab-completing `\\nor` or `\\barvee` in the Julia REPL. # Examples ```jldoctest julia> nor(true, false) false julia> nor(true, true) false julia> nor(true, missing) false julia> false โŠฝ false true julia> false โŠฝ missing missing julia> [true; true; false] .โŠฝ [true; false; false] 3-element BitVector: 0 0 1 ``` """ nor(x...) = ~(|)(x...) >>(x::Bool, c::UInt) = Int(x) >> c <<(x::Bool, c::UInt) = Int(x) << c >>>(x::Bool, c::UInt) = Int(x) >>> c signbit(x::Bool) = false sign(x::Bool) = x abs(x::Bool) = x abs2(x::Bool) = x iszero(x::Bool) = !x isone(x::Bool) = x <(x::Bool, y::Bool) = y&!x <=(x::Bool, y::Bool) = y|!x ## do arithmetic as Int ## +(x::Bool) = Int(x) -(x::Bool) = -Int(x) +(x::Bool, y::Bool) = Int(x) + Int(y) -(x::Bool, y::Bool) = Int(x) - Int(y) *(x::Bool, y::Bool) = x & y ^(x::Bool, y::Bool) = x | !y ^(x::Integer, y::Bool) = ifelse(y, x, one(x)) # preserve -0.0 in `false + -0.0` function +(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat return ifelse(x, oneunit(y) + y, y) end +(y::AbstractFloat, x::Bool) = x + y # make `false` a "strong zero": false*NaN == 0.0 function *(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat return ifelse(x, y, copysign(zero(y), y)) end *(y::AbstractFloat, x::Bool) = x * y div(x::Bool, y::Bool) = y ? x : throw(DivideError()) rem(x::Bool, y::Bool) = y ? false : throw(DivideError()) mod(x::Bool, y::Bool) = rem(x,y) M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/number.jl๋# This file is a part of Julia. License is MIT: https://julialang.org/license ## generic operations on numbers ## # Numbers are convertible convert(::Type{T}, x::T) where {T<:Number} = x convert(::Type{T}, x::Number) where {T<:Number} = T(x)::T """ isinteger(x) -> Bool Test whether `x` is numerically equal to some integer. # Examples ```jldoctest julia> isinteger(4.0) true ``` """ isinteger(x::Integer) = true """ iszero(x) Return `true` if `x == zero(x)`; if `x` is an array, this checks whether all of the elements of `x` are zero. See also: [`isone`](@ref), [`isinteger`](@ref), [`isfinite`](@ref), [`isnan`](@ref). # Examples ```jldoctest julia> iszero(0.0) true julia> iszero([1, 9, 0]) false julia> iszero([false, 0, 0]) true ``` """ iszero(x) = x == zero(x) # fallback method """ isone(x) Return `true` if `x == one(x)`; if `x` is an array, this checks whether `x` is an identity matrix. # Examples ```jldoctest julia> isone(1.0) true julia> isone([1 0; 0 2]) false julia> isone([1 0; 0 true]) true ``` """ isone(x) = x == one(x) # fallback method """ isfinite(f) -> Bool Test whether a number is finite. # Examples ```jldoctest julia> isfinite(5) true julia> isfinite(NaN32) false ``` """ isfinite(x::Number) = iszero(x - x) size(x::Number) = () size(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : 1 axes(x::Number) = () axes(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : OneTo(1) eltype(::Type{T}) where {T<:Number} = T ndims(x::Number) = 0 ndims(::Type{<:Number}) = 0 length(x::Number) = 1 firstindex(x::Number) = 1 firstindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1 lastindex(x::Number) = 1 lastindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1 IteratorSize(::Type{<:Number}) = HasShape{0}() keys(::Number) = OneTo(1) getindex(x::Number) = x function getindex(x::Number, i::Integer) @inline @boundscheck i == 1 || throw(BoundsError(x, i)) x end function getindex(x::Number, I::Integer...) @inline @boundscheck all(isone, I) || throw(BoundsError(x, I)) x end get(x::Number, i::Integer, default) = isone(i) ? x : default get(x::Number, ind::Tuple, default) = all(isone, ind) ? x : default get(f::Callable, x::Number, i::Integer) = isone(i) ? x : f() get(f::Callable, x::Number, ind::Tuple) = all(isone, ind) ? x : f() first(x::Number) = x last(x::Number) = x copy(x::Number) = x # some code treats numbers as collection-like """ signbit(x) Return `true` if the value of the sign of `x` is negative, otherwise `false`. See also [`sign`](@ref) and [`copysign`](@ref). # Examples ```jldoctest julia> signbit(-4) true julia> signbit(5) false julia> signbit(5.5) false julia> signbit(-4.1) true ``` """ signbit(x::Real) = x < 0 """ sign(x) Return zero if `x==0` and ``x/|x|`` otherwise (i.e., ยฑ1 for real `x`). See also [`signbit`](@ref), [`zero`](@ref), [`copysign`](@ref), [`flipsign`](@ref). # Examples ```jldoctest julia> sign(-4.0) -1.0 julia> sign(99) 1 julia> sign(-0.0) -0.0 julia> sign(0 + im) 0.0 + 1.0im ``` """ sign(x::Number) = iszero(x) ? x/abs(oneunit(x)) : x/abs(x) sign(x::Real) = ifelse(x < zero(x), oftype(one(x),-1), ifelse(x > zero(x), one(x), typeof(one(x))(x))) sign(x::Unsigned) = ifelse(x > zero(x), one(x), oftype(one(x),0)) abs(x::Real) = ifelse(signbit(x), -x, x) """ abs2(x) Squared absolute value of `x`. This can be faster than `abs(x)^2`, especially for complex numbers where `abs(x)` requires a square root via [`hypot`](@ref). See also [`abs`](@ref), [`conj`](@ref), [`real`](@ref). # Examples ```jldoctest julia> abs2(-3) 9 julia> abs2(3.0 + 4.0im) 25.0 julia> sum(abs2, [1+2im, 3+4im]) # LinearAlgebra.norm(x)^2 30 ``` """ abs2(x::Number) = abs(x)^2 abs2(x::Real) = x*x """ flipsign(x, y) Return `x` with its sign flipped if `y` is negative. For example `abs(x) = flipsign(x,x)`. # Examples ```jldoctest julia> flipsign(5, 3) 5 julia> flipsign(5, -3) -5 ``` """ flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, +x) # the + is for type-stability on Bool """ copysign(x, y) -> z Return `z` which has the magnitude of `x` and the same sign as `y`. # Examples ```jldoctest julia> copysign(1, -2) -1 julia> copysign(-1, 2) 1 ``` """ copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, +x) conj(x::Real) = x transpose(x::Number) = x adjoint(x::Number) = conj(x) angle(z::Real) = atan(zero(z), z) """ inv(x) Return the multiplicative inverse of `x`, such that `x*inv(x)` or `inv(x)*x` yields [`one(x)`](@ref) (the multiplicative identity) up to roundoff errors. If `x` is a number, this is essentially the same as `one(x)/x`, but for some types `inv(x)` may be slightly more efficient. # Examples ```jldoctest julia> inv(2) 0.5 julia> inv(1 + 2im) 0.2 - 0.4im julia> inv(1 + 2im) * (1 + 2im) 1.0 + 0.0im julia> inv(2//3) 3//2 ``` !!! compat "Julia 1.2" `inv(::Missing)` requires at least Julia 1.2. """ inv(x::Number) = one(x)/x """ widemul(x, y) Multiply `x` and `y`, giving the result as a larger type. See also [`promote`](@ref), [`Base.add_sum`](@ref). # Examples ```jldoctest julia> widemul(Float32(3.0), 4.0) isa BigFloat true julia> typemax(Int8) * typemax(Int8) 1 julia> widemul(typemax(Int8), typemax(Int8)) # == 127^2 16129 ``` """ widemul(x::Number, y::Number) = widen(x)*widen(y) iterate(x::Number) = (x, nothing) iterate(x::Number, ::Any) = nothing isempty(x::Number) = false in(x::Number, y::Number) = x == y map(f, x::Number, ys::Number...) = f(x, ys...) """ zero(x) zero(::Type) Get the additive identity element for the type of `x` (`x` can also specify the type itself). See also [`iszero`](@ref), [`one`](@ref), [`oneunit`](@ref), [`oftype`](@ref). # Examples ```jldoctest julia> zero(1) 0 julia> zero(big"2.0") 0.0 julia> zero(rand(2,2)) 2ร—2 Matrix{Float64}: 0.0 0.0 0.0 0.0 ``` """ zero(x::Number) = oftype(x,0) zero(::Type{T}) where {T<:Number} = convert(T,0) zero(::Type{Union{}}, slurp...) = Union{}(0) """ one(x) one(T::type) Return a multiplicative identity for `x`: a value such that `one(x)*x == x*one(x) == x`. Alternatively `one(T)` can take a type `T`, in which case `one` returns a multiplicative identity for any `x` of type `T`. If possible, `one(x)` returns a value of the same type as `x`, and `one(T)` returns a value of type `T`. However, this may not be the case for types representing dimensionful quantities (e.g. time in days), since the multiplicative identity must be dimensionless. In that case, `one(x)` should return an identity value of the same precision (and shape, for matrices) as `x`. If you want a quantity that is of the same type as `x`, or of type `T`, even if `x` is dimensionful, use [`oneunit`](@ref) instead. See also the [`identity`](@ref) function, and `I` in [`LinearAlgebra`](@ref man-linalg) for the identity matrix. # Examples ```jldoctest julia> one(3.7) 1.0 julia> one(Int) 1 julia> import Dates; one(Dates.Day(1)) 1 ``` """ one(::Type{T}) where {T<:Number} = convert(T,1) one(x::T) where {T<:Number} = one(T) one(::Type{Union{}}, slurp...) = Union{}(1) # note that convert(T, 1) should throw an error if T is dimensionful, # so this fallback definition should be okay. """ oneunit(x::T) oneunit(T::Type) Return `T(one(x))`, where `T` is either the type of the argument or (if a type is passed) the argument. This differs from [`one`](@ref) for dimensionful quantities: `one` is dimensionless (a multiplicative identity) while `oneunit` is dimensionful (of the same type as `x`, or of type `T`). # Examples ```jldoctest julia> oneunit(3.7) 1.0 julia> import Dates; oneunit(Dates.Day) 1 day ``` """ oneunit(x::T) where {T} = T(one(x)) oneunit(::Type{T}) where {T} = T(one(T)) oneunit(::Type{Union{}}, slurp...) = Union{}(1) """ big(T::Type) Compute the type that represents the numeric type `T` with arbitrary precision. Equivalent to `typeof(big(zero(T)))`. # Examples ```jldoctest julia> big(Rational) Rational{BigInt} julia> big(Float64) BigFloat julia> big(Complex{Int}) Complex{BigInt} ``` """ big(::Type{T}) where {T<:Number} = typeof(big(zero(T))) big(::Type{Union{}}, slurp...) = Union{}(0) J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/int.jl+x# This file is a part of Julia. License is MIT: https://julialang.org/license ## integer arithmetic ## # The tuples and types that do not include 128 bit sizes are necessary to handle # certain issues on 32-bit machines, and also to simplify promotion rules, as # they are also used elsewhere where Int128/UInt128 support is separated out, # such as in hashing2.jl const BitSigned32_types = (Int8, Int16, Int32) const BitUnsigned32_types = (UInt8, UInt16, UInt32) const BitInteger32_types = (BitSigned32_types..., BitUnsigned32_types...) const BitSigned64_types = (BitSigned32_types..., Int64) const BitUnsigned64_types = (BitUnsigned32_types..., UInt64) const BitInteger64_types = (BitSigned64_types..., BitUnsigned64_types...) const BitSigned_types = (BitSigned64_types..., Int128) const BitUnsigned_types = (BitUnsigned64_types..., UInt128) const BitInteger_types = (BitSigned_types..., BitUnsigned_types...) const BitSignedSmall_types = Int === Int64 ? ( Int8, Int16, Int32) : ( Int8, Int16) const BitUnsignedSmall_types = Int === Int64 ? (UInt8, UInt16, UInt32) : (UInt8, UInt16) const BitIntegerSmall_types = (BitSignedSmall_types..., BitUnsignedSmall_types...) const BitSigned32 = Union{BitSigned32_types...} const BitUnsigned32 = Union{BitUnsigned32_types...} const BitInteger32 = Union{BitInteger32_types...} const BitSigned64 = Union{BitSigned64_types...} const BitUnsigned64 = Union{BitUnsigned64_types...} const BitInteger64 = Union{BitInteger64_types...} const BitSigned = Union{BitSigned_types...} const BitUnsigned = Union{BitUnsigned_types...} const BitInteger = Union{BitInteger_types...} const BitSignedSmall = Union{BitSignedSmall_types...} const BitUnsignedSmall = Union{BitUnsignedSmall_types...} const BitIntegerSmall = Union{BitIntegerSmall_types...} const BitSigned64T = Union{Type{Int8}, Type{Int16}, Type{Int32}, Type{Int64}} const BitUnsigned64T = Union{Type{UInt8}, Type{UInt16}, Type{UInt32}, Type{UInt64}} const BitIntegerType = Union{map(T->Type{T}, BitInteger_types)...} # >> this use of `unsigned` is defined somewhere else << the docstring should migrate there """ unsigned(T::Integer) Convert an integer bitstype to the unsigned type of the same size. # Examples ```jldoctest julia> unsigned(Int16) UInt16 julia> unsigned(UInt64) UInt64 ``` """ unsigned """ signed(T::Integer) Convert an integer bitstype to the signed type of the same size. # Examples ```jldoctest julia> signed(UInt16) Int16 julia> signed(UInt64) Int64 ``` """ signed(::Type{Bool}) = Int signed(::Type{UInt8}) = Int8 signed(::Type{UInt16}) = Int16 signed(::Type{UInt32}) = Int32 signed(::Type{UInt64}) = Int64 signed(::Type{UInt128}) = Int128 signed(::Type{T}) where {T<:Signed} = T ## integer comparisons ## (<)(x::T, y::T) where {T<:BitSigned} = slt_int(x, y) (-)(x::BitInteger) = neg_int(x) (-)(x::T, y::T) where {T<:BitInteger} = sub_int(x, y) (+)(x::T, y::T) where {T<:BitInteger} = add_int(x, y) (*)(x::T, y::T) where {T<:BitInteger} = mul_int(x, y) negate(x) = -x negate(x::Unsigned) = -convert(Signed, x) #widenegate(x) = -convert(widen(signed(typeof(x))), x) inv(x::Integer) = float(one(x)) / float(x) (/)(x::T, y::T) where {T<:Integer} = float(x) / float(y) # skip promotion for system integer types (/)(x::BitInteger, y::BitInteger) = float(x) / float(y) """ isodd(x::Number) -> Bool Return `true` if `x` is an odd integer (that is, an integer not divisible by 2), and `false` otherwise. !!! compat "Julia 1.7" Non-`Integer` arguments require Julia 1.7 or later. # Examples ```jldoctest julia> isodd(9) true julia> isodd(10) false ``` """ isodd(n::Number) = isreal(n) && isodd(real(n)) isodd(n::Real) = isinteger(n) && !iszero(rem(Integer(n), 2)) """ iseven(x::Number) -> Bool Return `true` if `x` is an even integer (that is, an integer divisible by 2), and `false` otherwise. !!! compat "Julia 1.7" Non-`Integer` arguments require Julia 1.7 or later. # Examples ```jldoctest julia> iseven(9) false julia> iseven(10) true ``` """ iseven(n::Number) = isreal(n) && iseven(real(n)) iseven(n::Real) = isinteger(n) && iszero(rem(Integer(n), 2)) signbit(x::Integer) = x < 0 signbit(x::Unsigned) = false flipsign(x::T, y::T) where {T<:BitSigned} = flipsign_int(x, y) flipsign(x::BitSigned, y::BitSigned) = flipsign_int(promote(x, y)...) % typeof(x) flipsign(x::Signed, y::Float16) = flipsign(x, bitcast(Int16, y)) flipsign(x::Signed, y::Float32) = flipsign(x, bitcast(Int32, y)) flipsign(x::Signed, y::Float64) = flipsign(x, bitcast(Int64, y)) flipsign(x::Signed, y::Real) = flipsign(x, -oftype(x, signbit(y))) copysign(x::Signed, y::Signed) = flipsign(x, x โŠป y) copysign(x::Signed, y::Float16) = copysign(x, bitcast(Int16, y)) copysign(x::Signed, y::Float32) = copysign(x, bitcast(Int32, y)) copysign(x::Signed, y::Float64) = copysign(x, bitcast(Int64, y)) copysign(x::Signed, y::Real) = copysign(x, -oftype(x, signbit(y))) """ abs(x) The absolute value of `x`. When `abs` is applied to signed integers, overflow may occur, resulting in the return of a negative value. This overflow occurs only when `abs` is applied to the minimum representable value of a signed integer. That is, when `x == typemin(typeof(x))`, `abs(x) == x < 0`, not `-x` as might be expected. See also: [`abs2`](@ref), [`unsigned`](@ref), [`sign`](@ref). # Examples ```jldoctest julia> abs(-3) 3 julia> abs(1 + im) 1.4142135623730951 julia> abs.(Int8[-128 -127 -126 0 126 127]) # overflow at typemin(Int8) 1ร—6 Matrix{Int8}: -128 127 126 0 126 127 julia> maximum(abs, [1, -2, 3, -4]) 4 ``` """ function abs end abs(x::Unsigned) = x abs(x::Signed) = flipsign(x,x) ~(n::Integer) = -n-1 """ unsigned(x) Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as unsigned without checking for negative values. See also: [`signed`](@ref), [`sign`](@ref), [`signbit`](@ref). # Examples ```jldoctest julia> unsigned(-2) 0xfffffffffffffffe julia> unsigned(Int8(2)) 0x02 julia> typeof(ans) UInt8 julia> signed(unsigned(-2)) -2 ``` """ unsigned(x) = x % typeof(convert(Unsigned, zero(x))) unsigned(x::BitSigned) = reinterpret(typeof(convert(Unsigned, zero(x))), x) """ signed(x) Convert a number to a signed integer. If the argument is unsigned, it is reinterpreted as signed without checking for overflow. See also: [`unsigned`](@ref), [`sign`](@ref), [`signbit`](@ref). """ signed(x) = x % typeof(convert(Signed, zero(x))) signed(x::BitUnsigned) = reinterpret(typeof(convert(Signed, zero(x))), x) div(x::BitSigned, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x) div(x::Unsigned, y::BitSigned) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y)) rem(x::BitSigned, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) rem(x::Unsigned, y::BitSigned) = rem(x, unsigned(abs(y))) function divrem(x::BitSigned, y::Unsigned) q, r = divrem(unsigned(abs(x)), y) flipsign(signed(q), x), flipsign(signed(r), x) end function divrem(x::Unsigned, y::BitSigned) q, r = divrem(x, unsigned(abs(y))) unsigned(flipsign(signed(q), y)), r end """ mod(x, y) rem(x, y, RoundDown) The reduction of `x` modulo `y`, or equivalently, the remainder of `x` after floored division by `y`, i.e. `x - y*fld(x,y)` if computed without intermediate rounding. The result will have the same sign as `y`, and magnitude less than `abs(y)` (with some exceptions, see note below). !!! note When used with floating point values, the exact result may not be representable by the type, and so rounding error may occur. In particular, if the exact result is very close to `y`, then it may be rounded to `y`. See also: [`rem`](@ref), [`div`](@ref), [`fld`](@ref), [`mod1`](@ref), [`invmod`](@ref). ```jldoctest julia> mod(8, 3) 2 julia> mod(9, 3) 0 julia> mod(8.9, 3) 2.9000000000000004 julia> mod(eps(), 3) 2.220446049250313e-16 julia> mod(-eps(), 3) 3.0 julia> mod.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: 1 2 0 1 2 0 1 2 0 1 2 ``` """ function mod(x::T, y::T) where T<:Integer y == -1 && return T(0) # avoid potential overflow in fld return x - fld(x, y) * y end mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) mod(x::T, y::T) where {T<:Unsigned} = rem(x, y) # Don't promote integers for div/rem/mod since there is no danger of overflow, # while there is a substantial performance penalty to 64-bit promotion. div(x::T, y::T) where {T<:BitSigned64} = checked_sdiv_int(x, y) rem(x::T, y::T) where {T<:BitSigned64} = checked_srem_int(x, y) div(x::T, y::T) where {T<:BitUnsigned64} = checked_udiv_int(x, y) rem(x::T, y::T) where {T<:BitUnsigned64} = checked_urem_int(x, y) ## integer bitwise operations ## """ ~(x) Bitwise not. See also: [`!`](@ref), [`&`](@ref), [`|`](@ref). # Examples ```jldoctest julia> ~4 -5 julia> ~10 -11 julia> ~true false ``` """ (~)(x::BitInteger) = not_int(x) """ x & y Bitwise and. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if one operand is `missing` and the other is `true`. Add parentheses for function application form: `(&)(x, y)`. See also: [`|`](@ref), [`xor`](@ref), [`&&`](@ref). # Examples ```jldoctest julia> 4 & 10 0 julia> 4 & 12 4 julia> true & missing missing julia> false & missing false ``` """ (&)(x::T, y::T) where {T<:BitInteger} = and_int(x, y) """ x | y Bitwise or. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), returning [`missing`](@ref) if one operand is `missing` and the other is `false`. See also: [`&`](@ref), [`xor`](@ref), [`||`](@ref). # Examples ```jldoctest julia> 4 | 10 14 julia> 4 | 1 5 julia> true | missing true julia> false | missing missing ``` """ (|)(x::T, y::T) where {T<:BitInteger} = or_int(x, y) xor(x::T, y::T) where {T<:BitInteger} = xor_int(x, y) """ bswap(n) Reverse the byte order of `n`. (See also [`ntoh`](@ref) and [`hton`](@ref) to convert between the current native byte order and big-endian order.) # Examples ```jldoctest julia> a = bswap(0x10203040) 0x40302010 julia> bswap(a) 0x10203040 julia> string(1, base = 2) "1" julia> string(bswap(1), base = 2) "100000000000000000000000000000000000000000000000000000000" ``` """ bswap(x::Union{Int8, UInt8, Bool}) = x bswap(x::Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}) = bswap_int(x) """ count_ones(x::Integer) -> Integer Number of ones in the binary representation of `x`. # Examples ```jldoctest julia> count_ones(7) 3 julia> count_ones(Int32(-1)) 32 ``` """ count_ones(x::BitInteger) = (ctpop_int(x) % Int)::Int """ leading_zeros(x::Integer) -> Integer Number of zeros leading the binary representation of `x`. # Examples ```jldoctest julia> leading_zeros(Int32(1)) 31 ``` """ leading_zeros(x::BitInteger) = (ctlz_int(x) % Int)::Int """ trailing_zeros(x::Integer) -> Integer Number of zeros trailing the binary representation of `x`. # Examples ```jldoctest julia> trailing_zeros(2) 1 ``` """ trailing_zeros(x::BitInteger) = (cttz_int(x) % Int)::Int """ count_zeros(x::Integer) -> Integer Number of zeros in the binary representation of `x`. # Examples ```jldoctest julia> count_zeros(Int32(2 ^ 16 - 1)) 16 julia> count_zeros(-1) 0 ``` """ count_zeros(x::Integer) = count_ones(~x) """ leading_ones(x::Integer) -> Integer Number of ones leading the binary representation of `x`. # Examples ```jldoctest julia> leading_ones(UInt32(2 ^ 32 - 2)) 31 ``` """ leading_ones(x::Integer) = leading_zeros(~x) """ trailing_ones(x::Integer) -> Integer Number of ones trailing the binary representation of `x`. # Examples ```jldoctest julia> trailing_ones(3) 2 ``` """ trailing_ones(x::Integer) = trailing_zeros(~x) """ top_set_bit(x::Integer) -> Integer The number of bits in `x`'s binary representation, excluding leading zeros. Equivalently, the position of the most significant set bit in `x`'s binary representation, measured from the least significant side. Negative `x` are only supported when `x::BitSigned`. See also: [`ndigits0z`](@ref), [`ndigits`](@ref). # Examples ```jldoctest julia> Base.top_set_bit(4) 3 julia> Base.top_set_bit(0) 0 julia> Base.top_set_bit(-1) 64 ``` """ top_set_bit(x::BitInteger) = 8sizeof(x) - leading_zeros(x) ## integer comparisons ## (< )(x::T, y::T) where {T<:BitUnsigned} = ult_int(x, y) (<=)(x::T, y::T) where {T<:BitSigned} = sle_int(x, y) (<=)(x::T, y::T) where {T<:BitUnsigned} = ule_int(x, y) ==(x::BitSigned, y::BitUnsigned) = (x >= 0) & (unsigned(x) == y) ==(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x == unsigned(y)) <( x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) < y) <( x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x < unsigned(y)) <=(x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) <= y) <=(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x <= unsigned(y)) ## integer shifts ## # unsigned shift counts always shift in the same direction >>(x::BitSigned, y::BitUnsigned) = ashr_int(x, y) >>(x::BitUnsigned, y::BitUnsigned) = lshr_int(x, y) <<(x::BitInteger, y::BitUnsigned) = shl_int(x, y) >>>(x::BitInteger, y::BitUnsigned) = lshr_int(x, y) # signed shift counts can shift in either direction # note: this early during bootstrap, `>=` is not yet available # note: we only define Int shift counts here; the generic case is handled later >>(x::BitInteger, y::Int) = ifelse(0 <= y, x >> unsigned(y), x << unsigned(-y)) <<(x::BitInteger, y::Int) = ifelse(0 <= y, x << unsigned(y), x >> unsigned(-y)) >>>(x::BitInteger, y::Int) = ifelse(0 <= y, x >>> unsigned(y), x << unsigned(-y)) for to in BitInteger_types, from in (BitInteger_types..., Bool) if !(to === from) if Core.sizeof(to) < Core.sizeof(from) @eval rem(x::($from), ::Type{$to}) = trunc_int($to, x) elseif from === Bool @eval rem(x::($from), ::Type{$to}) = convert($to, x) elseif Core.sizeof(from) < Core.sizeof(to) if from <: Signed @eval rem(x::($from), ::Type{$to}) = sext_int($to, x) else @eval rem(x::($from), ::Type{$to}) = convert($to, x) end else @eval rem(x::($from), ::Type{$to}) = bitcast($to, x) end end end ## integer bitwise rotations ## """ bitrotate(x::Base.BitInteger, k::Integer) `bitrotate(x, k)` implements bitwise rotation. It returns the value of `x` with its bits rotated left `k` times. A negative value of `k` will rotate to the right instead. !!! compat "Julia 1.5" This function requires Julia 1.5 or later. See also: [`<<`](@ref), [`circshift`](@ref), [`BitArray`](@ref). ```jldoctest julia> bitrotate(UInt8(114), 2) 0xc9 julia> bitstring(bitrotate(0b01110010, 2)) "11001001" julia> bitstring(bitrotate(0b01110010, -2)) "10011100" julia> bitstring(bitrotate(0b01110010, 8)) "01110010" ``` """ bitrotate(x::T, k::Integer) where {T <: BitInteger} = (x << ((sizeof(T) << 3 - 1) & k)) | (x >>> ((sizeof(T) << 3 - 1) & -k)) # @doc isn't available when running in Core at this point. # Tuple syntax for documentation two function signatures at the same time # doesn't work either at this point. if nameof(@__MODULE__) === :Base for fname in (:mod, :rem) @eval @doc """ rem(x::Integer, T::Type{<:Integer}) -> T mod(x::Integer, T::Type{<:Integer}) -> T %(x::Integer, T::Type{<:Integer}) -> T Find `y::T` such that `x` โ‰ก `y` (mod n), where n is the number of integers representable in `T`, and `y` is an integer in `[typemin(T),typemax(T)]`. If `T` can represent any integer (e.g. `T == BigInt`), then this operation corresponds to a conversion to `T`. # Examples ```jldoctest julia> x = 129 % Int8 -127 julia> typeof(x) Int8 julia> x = 129 % BigInt 129 julia> typeof(x) BigInt ``` """ $fname(x::Integer, T::Type{<:Integer}) end end rem(x::T, ::Type{T}) where {T<:Integer} = x rem(x::Signed, ::Type{Unsigned}) = x % unsigned(typeof(x)) rem(x::Unsigned, ::Type{Signed}) = x % signed(typeof(x)) rem(x::Integer, T::Type{<:Integer}) = convert(T, x) # `x % T` falls back to `convert` rem(x::Integer, ::Type{Bool}) = ((x & 1) != 0) mod(x::Integer, ::Type{T}) where {T<:Integer} = rem(x, T) unsafe_trunc(::Type{T}, x::Integer) where {T<:Integer} = rem(x, T) """ trunc([T,] x) trunc(x; digits::Integer= [, base = 10]) trunc(x; sigdigits::Integer= [, base = 10]) `trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value is less than or equal to the absolute value of `x`. `trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not representable. Keywords `digits`, `sigdigits` and `base` work as for [`round`](@ref). See also: [`%`](@ref rem), [`floor`](@ref), [`unsigned`](@ref), [`unsafe_trunc`](@ref). # Examples ```jldoctest julia> trunc(2.22) 2.0 julia> trunc(-2.22, digits=1) -2.2 julia> trunc(Int, -2.22) -2 ``` """ function trunc end """ floor([T,] x) floor(x; digits::Integer= [, base = 10]) floor(x; sigdigits::Integer= [, base = 10]) `floor(x)` returns the nearest integral value of the same type as `x` that is less than or equal to `x`. `floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not representable. Keywords `digits`, `sigdigits` and `base` work as for [`round`](@ref). """ function floor end """ ceil([T,] x) ceil(x; digits::Integer= [, base = 10]) ceil(x; sigdigits::Integer= [, base = 10]) `ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or equal to `x`. `ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not representable. Keywords `digits`, `sigdigits` and `base` work as for [`round`](@ref). """ function ceil end round(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) trunc(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) floor(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) ceil(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) ## integer construction ## """ @int128_str str Parse `str` as an [`Int128`](@ref). Throw an `ArgumentError` if the string is not a valid integer. # Examples ```jldoctest julia> int128"123456789123" 123456789123 julia> int128"123456789123.4" ERROR: LoadError: ArgumentError: invalid base 10 digit '.' in "123456789123.4" [...] ``` """ macro int128_str(s) return parse(Int128, s) end """ @uint128_str str Parse `str` as an [`UInt128`](@ref). Throw an `ArgumentError` if the string is not a valid integer. # Examples ``` julia> uint128"123456789123" 0x00000000000000000000001cbe991a83 julia> uint128"-123456789123" ERROR: LoadError: ArgumentError: invalid base 10 digit '-' in "-123456789123" [...] ``` """ macro uint128_str(s) return parse(UInt128, s) end """ @big_str str Parse a string into a [`BigInt`](@ref) or [`BigFloat`](@ref), and throw an `ArgumentError` if the string is not a valid number. For integers `_` is allowed in the string as a separator. # Examples ```jldoctest julia> big"123_456" 123456 julia> big"7891.5" 7891.5 julia> big"_" ERROR: ArgumentError: invalid number format _ for BigInt or BigFloat [...] ``` """ macro big_str(s) message = "invalid number format $s for BigInt or BigFloat" throw_error = :(throw(ArgumentError($message))) if '_' in s # remove _ in s[2:end-1] bf = IOBuffer(maxsize=lastindex(s)) c = s[1] print(bf, c) is_prev_underscore = (c == '_') is_prev_dot = (c == '.') for c in SubString(s, 2, lastindex(s)-1) c != '_' && print(bf, c) c == '_' && is_prev_dot && return throw_error c == '.' && is_prev_underscore && return throw_error is_prev_underscore = (c == '_') is_prev_dot = (c == '.') end print(bf, s[end]) s = String(take!(bf)) end n = tryparse(BigInt, s) n === nothing || return n n = tryparse(BigFloat, s) n === nothing || return n return throw_error end ## integer promotions ## # with different sizes, promote to larger type promote_rule(::Type{Int16}, ::Union{Type{Int8}, Type{UInt8}}) = Int16 promote_rule(::Type{Int32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = Int32 promote_rule(::Type{Int64}, ::Union{Type{Int16}, Type{Int32}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = Int64 promote_rule(::Type{Int128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = Int128 promote_rule(::Type{UInt16}, ::Union{Type{Int8}, Type{UInt8}}) = UInt16 promote_rule(::Type{UInt32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = UInt32 promote_rule(::Type{UInt64}, ::Union{Type{Int16}, Type{Int32}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = UInt64 promote_rule(::Type{UInt128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = UInt128 # with mixed signedness and same size, Unsigned wins promote_rule(::Type{UInt8}, ::Type{Int8} ) = UInt8 promote_rule(::Type{UInt16}, ::Type{Int16} ) = UInt16 promote_rule(::Type{UInt32}, ::Type{Int32} ) = UInt32 promote_rule(::Type{UInt64}, ::Type{Int64} ) = UInt64 promote_rule(::Type{UInt128}, ::Type{Int128}) = UInt128 ## traits ## """ typemin(T) The lowest value representable by the given (real) numeric DataType `T`. See also: [`floatmin`](@ref), [`typemax`](@ref), [`eps`](@ref). # Examples ```jldoctest julia> typemin(Int8) -128 julia> typemin(UInt32) 0x00000000 julia> typemin(Float16) -Inf16 julia> typemin(Float32) -Inf32 julia> nextfloat(-Inf32) # smallest finite Float32 floating point number -3.4028235f38 ``` """ function typemin end """ typemax(T) The highest value representable by the given (real) numeric `DataType`. See also: [`floatmax`](@ref), [`typemin`](@ref), [`eps`](@ref). # Examples ```jldoctest julia> typemax(Int8) 127 julia> typemax(UInt32) 0xffffffff julia> typemax(Float64) Inf julia> typemax(Float32) Inf32 julia> floatmax(Float32) # largest finite Float32 floating point number 3.4028235f38 ``` """ function typemax end typemin(::Type{Int8 }) = Int8(-128) typemax(::Type{Int8 }) = Int8(127) typemin(::Type{UInt8 }) = UInt8(0) typemax(::Type{UInt8 }) = UInt8(255) typemin(::Type{Int16 }) = Int16(-32768) typemax(::Type{Int16 }) = Int16(32767) typemin(::Type{UInt16}) = UInt16(0) typemax(::Type{UInt16}) = UInt16(65535) typemin(::Type{Int32 }) = Int32(-2147483648) typemax(::Type{Int32 }) = Int32(2147483647) typemin(::Type{UInt32}) = UInt32(0) typemax(::Type{UInt32}) = UInt32(4294967295) typemin(::Type{Int64 }) = -9223372036854775808 typemax(::Type{Int64 }) = 9223372036854775807 typemin(::Type{UInt64}) = UInt64(0) typemax(::Type{UInt64}) = 0xffffffffffffffff @eval typemin(::Type{UInt128}) = $(convert(UInt128, 0)) @eval typemax(::Type{UInt128}) = $(bitcast(UInt128, convert(Int128, -1))) @eval typemin(::Type{Int128} ) = $(convert(Int128, 1) << 127) @eval typemax(::Type{Int128} ) = $(bitcast(Int128, typemax(UInt128) >> 1)) widen(::Type{Int8}) = Int16 widen(::Type{Int16}) = Int32 widen(::Type{Int32}) = Int64 widen(::Type{Int64}) = Int128 widen(::Type{UInt8}) = UInt16 widen(::Type{UInt16}) = UInt32 widen(::Type{UInt32}) = UInt64 widen(::Type{UInt64}) = UInt128 # a few special cases, # Int64*UInt64 => Int128 # |x|<=2^(k-1), |y|<=2^k-1 => |x*y|<=2^(2k-1)-1 widemul(x::Signed,y::Unsigned) = widen(x) * signed(widen(y)) widemul(x::Unsigned,y::Signed) = signed(widen(x)) * widen(y) # multplication by Bool doesn't require widening widemul(x::Bool,y::Bool) = x * y widemul(x::Bool,y::Number) = x * y widemul(x::Number,y::Bool) = x * y ## wide multiplication, Int128 multiply and divide ## if Core.sizeof(Int) == 4 function widemul(u::Int64, v::Int64) local u0::UInt64, v0::UInt64, w0::UInt64 local u1::Int64, v1::Int64, w1::UInt64, w2::Int64, t::UInt64 u0 = u & 0xffffffff; u1 = u >> 32 v0 = v & 0xffffffff; v1 = v >> 32 w0 = u0 * v0 t = reinterpret(UInt64, u1) * v0 + (w0 >>> 32) w2 = reinterpret(Int64, t) >> 32 w1 = u0 * reinterpret(UInt64, v1) + (t & 0xffffffff) hi = u1 * v1 + w2 + (reinterpret(Int64, w1) >> 32) lo = w0 & 0xffffffff + (w1 << 32) return Int128(hi) << 64 + Int128(lo) end function widemul(u::UInt64, v::UInt64) local u0::UInt64, v0::UInt64, w0::UInt64 local u1::UInt64, v1::UInt64, w1::UInt64, w2::UInt64, t::UInt64 u0 = u & 0xffffffff; u1 = u >>> 32 v0 = v & 0xffffffff; v1 = v >>> 32 w0 = u0 * v0 t = u1 * v0 + (w0 >>> 32) w2 = t >>> 32 w1 = u0 * v1 + (t & 0xffffffff) hi = u1 * v1 + w2 + (w1 >>> 32) lo = w0 & 0xffffffff + (w1 << 32) return UInt128(hi) << 64 + UInt128(lo) end function *(u::Int128, v::Int128) u0 = u % UInt64; u1 = Int64(u >> 64) v0 = v % UInt64; v1 = Int64(v >> 64) lolo = widemul(u0, v0) lohi = widemul(reinterpret(Int64, u0), v1) hilo = widemul(u1, reinterpret(Int64, v0)) t = reinterpret(UInt128, hilo) + (lolo >>> 64) w1 = reinterpret(UInt128, lohi) + (t & 0xffffffffffffffff) return Int128(lolo & 0xffffffffffffffff) + reinterpret(Int128, w1) << 64 end function *(u::UInt128, v::UInt128) u0 = u % UInt64; u1 = UInt64(u>>>64) v0 = v % UInt64; v1 = UInt64(v>>>64) lolo = widemul(u0, v0) lohi = widemul(u0, v1) hilo = widemul(u1, v0) t = hilo + (lolo >>> 64) w1 = lohi + (t & 0xffffffffffffffff) return (lolo & 0xffffffffffffffff) + UInt128(w1) << 64 end function _setbit(x::UInt128, i) # faster version of `return x | (UInt128(1) << i)` j = i >> 5 y = UInt128(one(UInt32) << (i & 0x1f)) if j == 0 return x | y elseif j == 1 return x | (y << 32) elseif j == 2 return x | (y << 64) elseif j == 3 return x | (y << 96) end return x end function divrem(x::UInt128, y::UInt128) iszero(y) && throw(DivideError()) if (x >> 64) % UInt64 == 0 if (y >> 64) % UInt64 == 0 # fast path: upper 64 bits are zero, so we can fallback to UInt64 division q64, x64 = divrem(x % UInt64, y % UInt64) return UInt128(q64), UInt128(x64) else # this implies y>x, so return zero(UInt128), x end end n = leading_zeros(y) - leading_zeros(x) q = zero(UInt128) ys = y << n while n >= 0 # ys == y * 2^n if ys <= x x -= ys q = _setbit(q, n) if (x >> 64) % UInt64 == 0 # exit early, similar to above fast path if (y >> 64) % UInt64 == 0 q64, x64 = divrem(x % UInt64, y % UInt64) q |= q64 x = UInt128(x64) end return q, x end end ys >>>= 1 n -= 1 end return q, x end function div(x::Int128, y::Int128) (x == typemin(Int128)) & (y == -1) && throw(DivideError()) return Int128(div(BigInt(x), BigInt(y)))::Int128 end div(x::UInt128, y::UInt128) = divrem(x, y)[1] function rem(x::Int128, y::Int128) return Int128(rem(BigInt(x), BigInt(y)))::Int128 end function rem(x::UInt128, y::UInt128) iszero(y) && throw(DivideError()) if (x >> 64) % UInt64 == 0 if (y >> 64) % UInt64 == 0 # fast path: upper 64 bits are zero, so we can fallback to UInt64 division return UInt128(rem(x % UInt64, y % UInt64)) else # this implies y>x, so return x end end n = leading_zeros(y) - leading_zeros(x) ys = y << n while n >= 0 # ys == y * 2^n if ys <= x x -= ys if (x >> 64) % UInt64 == 0 # exit early, similar to above fast path if (y >> 64) % UInt64 == 0 x = UInt128(rem(x % UInt64, y % UInt64)) end return x end end ys >>>= 1 n -= 1 end return x end function mod(x::Int128, y::Int128) return Int128(mod(BigInt(x), BigInt(y)))::Int128 end else *(x::T, y::T) where {T<:Union{Int128,UInt128}} = mul_int(x, y) div(x::Int128, y::Int128) = checked_sdiv_int(x, y) div(x::UInt128, y::UInt128) = checked_udiv_int(x, y) rem(x::Int128, y::Int128) = checked_srem_int(x, y) rem(x::UInt128, y::UInt128) = checked_urem_int(x, y) end # issue #15489: since integer ops are unchecked, they shouldn't check promotion for op in (:+, :-, :*, :&, :|, :xor) @eval function $op(a::Integer, b::Integer) T = promote_typeof(a, b) aT, bT = a % T, b % T not_sametype((a, b), (aT, bT)) return $op(aT, bT) end end const _mask1_uint128 = (UInt128(0x5555555555555555) << 64) | UInt128(0x5555555555555555) const _mask2_uint128 = (UInt128(0x3333333333333333) << 64) | UInt128(0x3333333333333333) const _mask4_uint128 = (UInt128(0x0f0f0f0f0f0f0f0f) << 64) | UInt128(0x0f0f0f0f0f0f0f0f) """ bitreverse(x) Reverse the order of bits in integer `x`. `x` must have a fixed bit width, e.g. be an `Int16` or `Int32`. !!! compat "Julia 1.5" This function requires Julia 1.5 or later. # Examples ```jldoctest julia> bitreverse(0x8080808080808080) 0x0101010101010101 julia> reverse(bitstring(0xa06e)) == bitstring(bitreverse(0xa06e)) true ``` """ function bitreverse(x::BitInteger) # TODO: consider using llvm.bitreverse intrinsic z = unsigned(x) mask1 = _mask1_uint128 % typeof(z) mask2 = _mask2_uint128 % typeof(z) mask4 = _mask4_uint128 % typeof(z) z = ((z & mask1) << 1) | ((z >> 1) & mask1) z = ((z & mask2) << 2) | ((z >> 2) & mask2) z = ((z & mask4) << 4) | ((z >> 4) & mask4) return bswap(z) % typeof(x) end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/operators.jl# This file is a part of Julia. License is MIT: https://julialang.org/license ## types ## """ <:(T1, T2) Subtype operator: returns `true` if and only if all values of type `T1` are also of type `T2`. # Examples ```jldoctest julia> Float64 <: AbstractFloat true julia> Vector{Int} <: AbstractArray true julia> Matrix{Float64} <: Matrix{AbstractFloat} false ``` """ (<:) """ >:(T1, T2) Supertype operator, equivalent to `T2 <: T1`. """ (>:)(@nospecialize(a), @nospecialize(b)) = (b <: a) """ supertype(T::DataType) Return the supertype of DataType `T`. # Examples ```jldoctest julia> supertype(Int32) Signed ``` """ supertype(T::DataType) = (@_total_meta; T.super) supertype(T::UnionAll) = (@_total_meta; UnionAll(T.var, supertype(T.body))) ## generic comparison ## """ ==(x, y) Generic equality operator. Falls back to [`===`](@ref). Should be implemented for all types with a notion of equality, based on the abstract value that an instance represents. For example, all numeric types are compared by numeric value, ignoring type. Strings are compared as sequences of characters, ignoring encoding. For collections, `==` is generally called recursively on all contents, though other properties (like the shape for arrays) may also be taken into account. This operator follows IEEE semantics for floating-point numbers: `0.0 == -0.0` and `NaN != NaN`. The result is of type `Bool`, except when one of the operands is [`missing`](@ref), in which case `missing` is returned ([three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic)). For collections, `missing` is returned if at least one of the operands contains a `missing` value and all non-missing values are equal. Use [`isequal`](@ref) or [`===`](@ref) to always get a `Bool` result. # Implementation New numeric types should implement this function for two arguments of the new type, and handle comparison to other types via promotion rules where possible. [`isequal`](@ref) falls back to `==`, so new methods of `==` will be used by the [`Dict`](@ref) type to compare keys. If your type will be used as a dictionary key, it should therefore also implement [`hash`](@ref). If some type defines `==`, [`isequal`](@ref), and [`isless`](@ref) then it should also implement [`<`](@ref) to ensure consistency of comparisons. """ == """ isequal(x, y) -> Bool Similar to [`==`](@ref), except for the treatment of floating point numbers and of missing values. `isequal` treats all floating-point `NaN` values as equal to each other, treats `-0.0` as unequal to `0.0`, and [`missing`](@ref) as equal to `missing`. Always returns a `Bool` value. `isequal` is an equivalence relation - it is reflexive (`===` implies `isequal`), symmetric (`isequal(a, b)` implies `isequal(b, a)`) and transitive (`isequal(a, b)` and `isequal(b, c)` implies `isequal(a, c)`). # Implementation The default implementation of `isequal` calls `==`, so a type that does not involve floating-point values generally only needs to define `==`. `isequal` is the comparison function used by hash tables (`Dict`). `isequal(x,y)` must imply that `hash(x) == hash(y)`. This typically means that types for which a custom `==` or `isequal` method exists must implement a corresponding [`hash`](@ref) method (and vice versa). Collections typically implement `isequal` by calling `isequal` recursively on all contents. Furthermore, `isequal` is linked with [`isless`](@ref), and they work together to define a fixed total ordering, where exactly one of `isequal(x, y)`, `isless(x, y)`, or `isless(y, x)` must be `true` (and the other two `false`). Scalar types generally do not need to implement `isequal` separate from `==`, unless they represent floating-point numbers amenable to a more efficient implementation than that provided as a generic fallback (based on `isnan`, `signbit`, and `==`). # Examples ```jldoctest julia> isequal([1., NaN], [1., NaN]) true julia> [1., NaN] == [1., NaN] false julia> 0.0 == -0.0 true julia> isequal(0.0, -0.0) false julia> missing == missing missing julia> isequal(missing, missing) true ``` """ isequal(x, y) = (x == y)::Bool # all `missing` cases are handled in missing.jl signequal(x, y) = signbit(x)::Bool == signbit(y)::Bool signless(x, y) = signbit(x)::Bool & !signbit(y)::Bool isequal(x::AbstractFloat, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y)::Bool isequal(x::Real, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y)::Bool isequal(x::AbstractFloat, y::Real ) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y)::Bool """ isless(x, y) Test whether `x` is less than `y`, according to a fixed total order (defined together with [`isequal`](@ref)). `isless` is not defined for pairs `(x, y)` of all types. However, if it is defined, it is expected to satisfy the following: - If `isless(x, y)` is defined, then so is `isless(y, x)` and `isequal(x, y)`, and exactly one of those three yields `true`. - The relation defined by `isless` is transitive, i.e., `isless(x, y) && isless(y, z)` implies `isless(x, z)`. Values that are normally unordered, such as `NaN`, are ordered after regular values. [`missing`](@ref) values are ordered last. This is the default comparison used by [`sort!`](@ref). # Implementation Non-numeric types with a total order should implement this function. Numeric types only need to implement it if they have special values such as `NaN`. Types with a partial order should implement [`<`](@ref). See the documentation on [Alternate Orderings](@ref) for how to define alternate ordering methods that can be used in sorting and related functions. # Examples ```jldoctest julia> isless(1, 3) true julia> isless("Red", "Blue") false ``` """ function isless end isless(x::AbstractFloat, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) isless(x::Real, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) isless(x::AbstractFloat, y::Real ) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) # Performance optimization to reduce branching # This is useful for sorting tuples of integers # TODO: remove this when the compiler can optimize the generic version better # See #48724 and #48753 isless(a::Tuple{BitInteger, BitInteger}, b::Tuple{BitInteger, BitInteger}) = isless(a[1], b[1]) | (isequal(a[1], b[1]) & isless(a[2], b[2])) """ isgreater(x, y) Not the inverse of `isless`! Test whether `x` is greater than `y`, according to a fixed total order compatible with `min`. Defined with `isless`, this function is usually `isless(y, x)`, but `NaN` and [`missing`](@ref) are ordered as smaller than any regular value with `missing` smaller than `NaN`. So `isless` defines an ascending total order with `NaN` and `missing` as the largest values and `isgreater` defines a descending total order with `NaN` and `missing` as the smallest values. !!! note Like `min`, `isgreater` orders containers (tuples, vectors, etc) lexicographically with `isless(y, x)` rather than recursively with itself: ```jldoctest julia> Base.isgreater(1, NaN) # 1 is greater than NaN true julia> Base.isgreater((1,), (NaN,)) # But (1,) is not greater than (NaN,) false julia> sort([1, 2, 3, NaN]; lt=Base.isgreater) 4-element Vector{Float64}: 3.0 2.0 1.0 NaN julia> sort(tuple.([1, 2, 3, NaN]); lt=Base.isgreater) 4-element Vector{Tuple{Float64}}: (NaN,) (3.0,) (2.0,) (1.0,) ``` # Implementation This is unexported. Types should not usually implement this function. Instead, implement `isless`. """ isgreater(x, y) = isunordered(x) || isunordered(y) ? isless(x, y) : isless(y, x) """ isunordered(x) Return `true` if `x` is a value that is not orderable according to [`<`](@ref), such as `NaN` or `missing`. The values that evaluate to `true` with this predicate may be orderable with respect to other orderings such as [`isless`](@ref). !!! compat "Julia 1.7" This function requires Julia 1.7 or later. """ isunordered(x) = false isunordered(x::AbstractFloat) = isnan(x) isunordered(x::Missing) = true ==(T::Type, S::Type) = (@_total_meta; ccall(:jl_types_equal, Cint, (Any, Any), T, S) != 0) !=(T::Type, S::Type) = (@_total_meta; !(T == S)) ==(T::TypeVar, S::Type) = false ==(T::Type, S::TypeVar) = false ## comparison fallbacks ## """ !=(x, y) โ‰ (x,y) Not-equals comparison operator. Always gives the opposite answer as [`==`](@ref). # Implementation New types should generally not implement this, and rely on the fallback definition `!=(x,y) = !(x==y)` instead. # Examples ```jldoctest julia> 3 != 2 true julia> "foo" โ‰  "foo" false ``` """ !=(x, y) = !(x == y) const โ‰  = != """ ===(x,y) -> Bool โ‰ก(x,y) -> Bool Determine whether `x` and `y` are identical, in the sense that no program could distinguish them. First the types of `x` and `y` are compared. If those are identical, mutable objects are compared by address in memory and immutable objects (such as numbers) are compared by contents at the bit level. This function is sometimes called "egal". It always returns a `Bool` value. # Examples ```jldoctest julia> a = [1, 2]; b = [1, 2]; julia> a == b true julia> a === b false julia> a === a true ``` """ === const โ‰ก = === """ !==(x, y) โ‰ข(x,y) Always gives the opposite answer as [`===`](@ref). # Examples ```jldoctest julia> a = [1, 2]; b = [1, 2]; julia> a โ‰ข b true julia> a โ‰ข a false ``` """ !==(@nospecialize(x), @nospecialize(y)) = !(x === y) const โ‰ข = !== """ <(x, y) Less-than comparison operator. Falls back to [`isless`](@ref). Because of the behavior of floating-point NaN values, this operator implements a partial order. # Implementation New types with a canonical partial order should implement this function for two arguments of the new type. Types with a canonical total order should implement [`isless`](@ref) instead. See also [`isunordered`](@ref). # Examples ```jldoctest julia> 'a' < 'b' true julia> "abc" < "abd" true julia> 5 < 3 false ``` """ <(x, y) = isless(x, y) """ >(x, y) Greater-than comparison operator. Falls back to `y < x`. # Implementation Generally, new types should implement [`<`](@ref) instead of this function, and rely on the fallback definition `>(x, y) = y < x`. # Examples ```jldoctest julia> 'a' > 'b' false julia> 7 > 3 > 1 true julia> "abc" > "abd" false julia> 5 > 3 true ``` """ >(x, y) = y < x """ <=(x, y) โ‰ค(x,y) Less-than-or-equals comparison operator. Falls back to `(x < y) | (x == y)`. # Examples ```jldoctest julia> 'a' <= 'b' true julia> 7 โ‰ค 7 โ‰ค 9 true julia> "abc" โ‰ค "abc" true julia> 5 <= 3 false ``` """ <=(x, y) = (x < y) | (x == y) const โ‰ค = <= """ >=(x, y) โ‰ฅ(x,y) Greater-than-or-equals comparison operator. Falls back to `y <= x`. # Examples ```jldoctest julia> 'a' >= 'b' false julia> 7 โ‰ฅ 7 โ‰ฅ 3 true julia> "abc" โ‰ฅ "abc" true julia> 5 >= 3 true ``` """ >=(x, y) = (y <= x) const โ‰ฅ = >= # this definition allows Number types to implement < instead of isless, # which is more idiomatic: isless(x::Real, y::Real) = x cmp(1, 2) -1 julia> cmp(2, 1) 1 julia> cmp(2+im, 3-im) ERROR: MethodError: no method matching isless(::Complex{Int64}, ::Complex{Int64}) [...] ``` """ cmp(x, y) = isless(x, y) ? -1 : ifelse(isless(y, x), 1, 0) """ cmp(<, x, y) Return -1, 0, or 1 depending on whether `x` is less than, equal to, or greater than `y`, respectively. The first argument specifies a less-than comparison function to use. """ cmp(<, x, y) = (x < y) ? -1 : ifelse(y < x, 1, 0) # cmp returns -1, 0, +1 indicating ordering cmp(x::Integer, y::Integer) = ifelse(isless(x, y), -1, ifelse(isless(y, x), 1, 0)) """ max(x, y, ...) Return the maximum of the arguments (with respect to [`isless`](@ref)). See also the [`maximum`](@ref) function to take the maximum element from a collection. # Examples ```jldoctest julia> max(2, 5, 1) 5 ``` """ max(x, y) = ifelse(isless(y, x), x, y) """ min(x, y, ...) Return the minimum of the arguments (with respect to [`isless`](@ref)). See also the [`minimum`](@ref) function to take the minimum element from a collection. # Examples ```jldoctest julia> min(2, 5, 1) 1 ``` """ min(x,y) = ifelse(isless(y, x), y, x) """ minmax(x, y) Return `(min(x,y), max(x,y))`. See also [`extrema`](@ref) that returns `(minimum(x), maximum(x))`. # Examples ```jldoctest julia> minmax('c','b') ('b', 'c') ``` """ minmax(x,y) = isless(y, x) ? (y, x) : (x, y) ## definitions providing basic traits of arithmetic operators ## """ identity(x) The identity function. Returns its argument. See also: [`one`](@ref), [`oneunit`](@ref), and [`LinearAlgebra`](@ref man-linalg)'s `I`. # Examples ```jldoctest julia> identity("Well, what did you expect?") "Well, what did you expect?" ``` """ identity(@nospecialize x) = x +(x::Number) = x *(x::Number) = x (&)(x::Integer) = x (|)(x::Integer) = x xor(x::Integer) = x const โŠป = xor const โŠผ = nand const โŠฝ = nor # foldl for argument lists. expand fully up to a point, then # switch to a loop. this allows small cases like `a+b+c+d` to be managed # efficiently, without a major slowdown for `+(x...)` when `x` is big. # n.b.: keep this method count small, so it can be inferred without hitting the # method count limit in inference afoldl(op, a) = a function afoldl(op, a, bs...) l = length(bs) i = 0; y = a; l == i && return y #@nexprs 31 i -> (y = op(y, bs[i]); l == i && return y) i = 1; y = op(y, bs[i]); l == i && return y i = 2; y = op(y, bs[i]); l == i && return y i = 3; y = op(y, bs[i]); l == i && return y i = 4; y = op(y, bs[i]); l == i && return y i = 5; y = op(y, bs[i]); l == i && return y i = 6; y = op(y, bs[i]); l == i && return y i = 7; y = op(y, bs[i]); l == i && return y i = 8; y = op(y, bs[i]); l == i && return y i = 9; y = op(y, bs[i]); l == i && return y i = 10; y = op(y, bs[i]); l == i && return y i = 11; y = op(y, bs[i]); l == i && return y i = 12; y = op(y, bs[i]); l == i && return y i = 13; y = op(y, bs[i]); l == i && return y i = 14; y = op(y, bs[i]); l == i && return y i = 15; y = op(y, bs[i]); l == i && return y i = 16; y = op(y, bs[i]); l == i && return y i = 17; y = op(y, bs[i]); l == i && return y i = 18; y = op(y, bs[i]); l == i && return y i = 19; y = op(y, bs[i]); l == i && return y i = 20; y = op(y, bs[i]); l == i && return y i = 21; y = op(y, bs[i]); l == i && return y i = 22; y = op(y, bs[i]); l == i && return y i = 23; y = op(y, bs[i]); l == i && return y i = 24; y = op(y, bs[i]); l == i && return y i = 25; y = op(y, bs[i]); l == i && return y i = 26; y = op(y, bs[i]); l == i && return y i = 27; y = op(y, bs[i]); l == i && return y i = 28; y = op(y, bs[i]); l == i && return y i = 29; y = op(y, bs[i]); l == i && return y i = 30; y = op(y, bs[i]); l == i && return y i = 31; y = op(y, bs[i]); l == i && return y for i in (i + 1):l y = op(y, bs[i]) end return y end setfield!(typeof(afoldl).name.mt, :max_args, 34, :monotonic) for op in (:+, :*, :&, :|, :xor, :min, :max, :kron) @eval begin # note: these definitions must not cause a dispatch loop when +(a,b) is # not defined, and must only try to call 2-argument definitions, so # that defining +(a,b) is sufficient for full functionality. ($op)(a, b, c, xs...) = (@inline; afoldl($op, ($op)(($op)(a,b),c), xs...)) # a further concern is that it's easy for a type like (Int,Int...) # to match many definitions, so we need to keep the number of # definitions down to avoid losing type information. end end function kron! end const var"'" = adjoint """ \\(x, y) Left division operator: multiplication of `y` by the inverse of `x` on the left. Gives floating-point results for integer arguments. # Examples ```jldoctest julia> 3 \\ 6 2.0 julia> inv(3) * 6 2.0 julia> A = [4 3; 2 1]; x = [5, 6]; julia> A \\ x 2-element Vector{Float64}: 6.5 -7.0 julia> inv(A) * x 2-element Vector{Float64}: 6.5 -7.0 ``` """ \(x,y) = adjoint(adjoint(y)/adjoint(x)) # Core <<, >>, and >>> take either Int or UInt as second arg. Signed shift # counts can shift in either direction, and are translated here to unsigned # counts. Integer datatypes only need to implement the unsigned version. """ <<(x, n) Left bit shift operator, `x << n`. For `n >= 0`, the result is `x` shifted left by `n` bits, filling with `0`s. This is equivalent to `x * 2^n`. For `n < 0`, this is equivalent to `x >> -n`. # Examples ```jldoctest julia> Int8(3) << 2 12 julia> bitstring(Int8(3)) "00000011" julia> bitstring(Int8(12)) "00001100" ``` See also [`>>`](@ref), [`>>>`](@ref), [`exp2`](@ref), [`ldexp`](@ref). """ function <<(x::Integer, c::Integer) @inline typemin(Int) <= c <= typemax(Int) && return x << (c % Int) (x >= 0 || c >= 0) && return zero(x) << 0 # for type stability oftype(x, -1) end function <<(x::Integer, c::Unsigned) @inline if c isa UInt throw(MethodError(<<, (x, c))) end c <= typemax(UInt) ? x << (c % UInt) : zero(x) << UInt(0) end <<(x::Integer, c::Int) = c >= 0 ? x << unsigned(c) : x >> unsigned(-c) """ >>(x, n) Right bit shift operator, `x >> n`. For `n >= 0`, the result is `x` shifted right by `n` bits, filling with `0`s if `x >= 0`, `1`s if `x < 0`, preserving the sign of `x`. This is equivalent to `fld(x, 2^n)`. For `n < 0`, this is equivalent to `x << -n`. # Examples ```jldoctest julia> Int8(13) >> 2 3 julia> bitstring(Int8(13)) "00001101" julia> bitstring(Int8(3)) "00000011" julia> Int8(-14) >> 2 -4 julia> bitstring(Int8(-14)) "11110010" julia> bitstring(Int8(-4)) "11111100" ``` See also [`>>>`](@ref), [`<<`](@ref). """ function >>(x::Integer, c::Integer) @inline if c isa UInt throw(MethodError(>>, (x, c))) end typemin(Int) <= c <= typemax(Int) && return x >> (c % Int) (x >= 0 || c < 0) && return zero(x) >> 0 oftype(x, -1) end >>(x::Integer, c::Int) = c >= 0 ? x >> unsigned(c) : x << unsigned(-c) """ >>>(x, n) Unsigned right bit shift operator, `x >>> n`. For `n >= 0`, the result is `x` shifted right by `n` bits, filling with `0`s. For `n < 0`, this is equivalent to `x << -n`. For [`Unsigned`](@ref) integer types, this is equivalent to [`>>`](@ref). For [`Signed`](@ref) integer types, this is equivalent to `signed(unsigned(x) >> n)`. # Examples ```jldoctest julia> Int8(-14) >>> 2 60 julia> bitstring(Int8(-14)) "11110010" julia> bitstring(Int8(60)) "00111100" ``` [`BigInt`](@ref)s are treated as if having infinite size, so no filling is required and this is equivalent to [`>>`](@ref). See also [`>>`](@ref), [`<<`](@ref). """ function >>>(x::Integer, c::Integer) @inline typemin(Int) <= c <= typemax(Int) ? x >>> (c % Int) : zero(x) >>> 0 end function >>>(x::Integer, c::Unsigned) @inline if c isa UInt throw(MethodError(>>>, (x, c))) end c <= typemax(UInt) ? x >>> (c % UInt) : zero(x) >>> 0 end >>>(x::Integer, c::Int) = c >= 0 ? x >>> unsigned(c) : x << unsigned(-c) # operator alias """ rem(x, y) %(x, y) Remainder from Euclidean division, returning a value of the same sign as `x`, and smaller in magnitude than `y`. This value is always exact. See also: [`div`](@ref), [`mod`](@ref), [`mod1`](@ref), [`divrem`](@ref). # Examples ```jldoctest julia> x = 15; y = 4; julia> x % y 3 julia> x == div(x, y) * y + rem(x, y) true julia> rem.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: -2 -1 0 -2 -1 0 1 2 0 1 2 ``` """ rem const % = rem """ div(x, y) รท(x, y) The quotient from Euclidean (integer) division. Generally equivalent to a mathematical operation x/y without a fractional part. See also: [`cld`](@ref), [`fld`](@ref), [`rem`](@ref), [`divrem`](@ref). # Examples ```jldoctest julia> 9 รท 4 2 julia> -5 รท 3 -1 julia> 5.0 รท 2 2.0 julia> div.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: -1 -1 -1 0 0 0 0 0 1 1 1 ``` """ div const รท = div """ mod1(x, y) Modulus after flooring division, returning a value `r` such that `mod(r, y) == mod(x, y)` in the range ``(0, y]`` for positive `y` and in the range ``[y,0)`` for negative `y`. With integer arguments and positive `y`, this is equal to `mod(x, 1:y)`, and hence natural for 1-based indexing. By comparison, `mod(x, y) == mod(x, 0:y-1)` is natural for computations with offsets or strides. See also [`mod`](@ref), [`fld1`](@ref), [`fldmod1`](@ref). # Examples ```jldoctest julia> mod1(4, 2) 2 julia> mod1.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: 1 2 3 1 2 3 1 2 3 1 2 julia> mod1.([-0.1, 0, 0.1, 1, 2, 2.9, 3, 3.1]', 3) 1ร—8 Matrix{Float64}: 2.9 3.0 0.1 1.0 2.0 2.9 3.0 0.1 ``` """ mod1(x::T, y::T) where {T<:Real} = (m = mod(x, y); ifelse(m == 0, y, m)) """ fld1(x, y) Flooring division, returning a value consistent with `mod1(x,y)` See also [`mod1`](@ref), [`fldmod1`](@ref). # Examples ```jldoctest julia> x = 15; y = 4; julia> fld1(x, y) 4 julia> x == fld(x, y) * y + mod(x, y) true julia> x == (fld1(x, y) - 1) * y + mod1(x, y) true ``` """ fld1(x::T, y::T) where {T<:Real} = (m = mod1(x, y); fld((x - m) + y, y)) function fld1(x::T, y::T) where T<:Integer d = div(x, y) return d + (!signbit(x โŠป y) & (d * y != x)) end """ fldmod1(x, y) Return `(fld1(x,y), mod1(x,y))`. See also [`fld1`](@ref), [`mod1`](@ref). """ fldmod1(x, y) = (fld1(x, y), mod1(x, y)) """ widen(x) If `x` is a type, return a "larger" type, defined so that arithmetic operations `+` and `-` are guaranteed not to overflow nor lose precision for any combination of values that type `x` can hold. For fixed-size integer types less than 128 bits, `widen` will return a type with twice the number of bits. If `x` is a value, it is converted to `widen(typeof(x))`. # Examples ```jldoctest julia> widen(Int32) Int64 julia> widen(1.5f0) 1.5 ``` """ widen(x::T) where {T} = convert(widen(T), x) widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,))) widen(x::Type{Union{}}, slurp...) = throw(MethodError(widen, (Union{},))) # function pipelining """ |>(x, f) Infix operator which applies function `f` to the argument `x`. This allows `f(g(x))` to be written `x |> g |> f`. When used with anonymous functions, parentheses are typically required around the definition to get the intended chain. # Examples ```jldoctest julia> 4 |> inv 0.25 julia> [2, 3, 5] |> sum |> inv 0.1 julia> [0 1; 2 3] .|> (x -> x^2) |> sum 14 ``` """ |>(x, f) = f(x) _stable_typeof(x) = typeof(x) _stable_typeof(::Type{T}) where {T} = @isdefined(T) ? Type{T} : DataType """ f = Returns(value) Create a callable `f` such that `f(args...; kw...) === value` holds. # Examples ```jldoctest julia> f = Returns(42); julia> f(1) 42 julia> f("hello", x=32) 42 julia> f.value 42 ``` !!! compat "Julia 1.7" `Returns` requires at least Julia 1.7. """ struct Returns{V} <: Function value::V Returns{V}(value) where {V} = new{V}(value) Returns(value) = new{_stable_typeof(value)}(value) end (obj::Returns)(@nospecialize(args...); @nospecialize(kw...)) = obj.value # function composition """ f โˆ˜ g Compose functions: i.e. `(f โˆ˜ g)(args...; kwargs...)` means `f(g(args...; kwargs...))`. The `โˆ˜` symbol can be entered in the Julia REPL (and most editors, appropriately configured) by typing `\\circ`. Function composition also works in prefix form: `โˆ˜(f, g)` is the same as `f โˆ˜ g`. The prefix form supports composition of multiple functions: `โˆ˜(f, g, h) = f โˆ˜ g โˆ˜ h` and splatting `โˆ˜(fs...)` for composing an iterable collection of functions. The last argument to `โˆ˜` execute first. !!! compat "Julia 1.4" Multiple function composition requires at least Julia 1.4. !!! compat "Julia 1.5" Composition of one function โˆ˜(f) requires at least Julia 1.5. !!! compat "Julia 1.7" Using keyword arguments requires at least Julia 1.7. # Examples ```jldoctest julia> map(uppercaseโˆ˜first, ["apple", "banana", "carrot"]) 3-element Vector{Char}: 'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase) 'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase) 'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase) julia> (==(6)โˆ˜length).(["apple", "banana", "carrot"]) 3-element BitVector: 0 1 1 julia> fs = [ x -> 2x x -> x-1 x -> x/2 x -> x+1 ]; julia> โˆ˜(fs...)(3) 2.0 ``` See also [`ComposedFunction`](@ref), [`!f::Function`](@ref). """ function โˆ˜ end """ ComposedFunction{Outer,Inner} <: Function Represents the composition of two callable objects `outer::Outer` and `inner::Inner`. That is ```julia ComposedFunction(outer, inner)(args...; kw...) === outer(inner(args...; kw...)) ``` The preferred way to construct an instance of `ComposedFunction` is to use the composition operator [`โˆ˜`](@ref): ```jldoctest julia> sin โˆ˜ cos === ComposedFunction(sin, cos) true julia> typeof(sinโˆ˜cos) ComposedFunction{typeof(sin), typeof(cos)} ``` The composed pieces are stored in the fields of `ComposedFunction` and can be retrieved as follows: ```jldoctest julia> composition = sin โˆ˜ cos sin โˆ˜ cos julia> composition.outer === sin true julia> composition.inner === cos true ``` !!! compat "Julia 1.6" ComposedFunction requires at least Julia 1.6. In earlier versions `โˆ˜` returns an anonymous function instead. See also [`โˆ˜`](@ref). """ struct ComposedFunction{O,I} <: Function outer::O inner::I ComposedFunction{O, I}(outer, inner) where {O, I} = new{O, I}(outer, inner) ComposedFunction(outer, inner) = new{Core.Typeof(outer),Core.Typeof(inner)}(outer, inner) end (c::ComposedFunction)(x...; kw...) = call_composed(unwrap_composed(c), x, kw) unwrap_composed(c::ComposedFunction) = (unwrap_composed(c.outer)..., unwrap_composed(c.inner)...) unwrap_composed(c) = (maybeconstructor(c),) call_composed(fs, x, kw) = (@inline; fs[1](call_composed(tail(fs), x, kw))) call_composed(fs::Tuple{Any}, x, kw) = fs[1](x...; kw...) struct Constructor{F} <: Function end (::Constructor{F})(args...; kw...) where {F} = (@inline; F(args...; kw...)) maybeconstructor(::Type{F}) where {F} = Constructor{F}() maybeconstructor(f) = f โˆ˜(f) = f โˆ˜(f, g) = ComposedFunction(f, g) โˆ˜(f, g, h...) = โˆ˜(f โˆ˜ g, h...) function show(io::IO, c::ComposedFunction) c.outer isa ComposedFunction ? show(io, c.outer) : _showcomposed(io, c.outer) print(io, " โˆ˜ ") _showcomposed(io, c.inner) end #shows !f instead of (!) โˆ˜ f when ! is the outermost function function show(io::IO, c::ComposedFunction{typeof(!)}) print(io, '!') _showcomposed(io, c.inner) end _showcomposed(io::IO, x) = show(io, x) #display operators like + and - inside parens _showcomposed(io::IO, f::Function) = isoperator(Symbol(f)) ? (print(io, '('); show(io, f); print(io, ')')) : show(io, f) #nesting for chained composition _showcomposed(io::IO, f::ComposedFunction) = (print(io, '('); show(io, f); print(io, ')')) #no nesting when ! is the outer function in a composition chain _showcomposed(io::IO, f::ComposedFunction{typeof(!)}) = show(io, f) """ !f::Function Predicate function negation: when the argument of `!` is a function, it returns a composed function which computes the boolean negation of `f`. See also [`โˆ˜`](@ref). # Examples ```jldoctest julia> str = "โˆ€ ฮต > 0, โˆƒ ฮด > 0: |x-y| < ฮด โ‡’ |f(x)-f(y)| < ฮต" "โˆ€ ฮต > 0, โˆƒ ฮด > 0: |x-y| < ฮด โ‡’ |f(x)-f(y)| < ฮต" julia> filter(isletter, str) "ฮตฮดxyฮดfxfyฮต" julia> filter(!isletter, str) "โˆ€ > 0, โˆƒ > 0: |-| < โ‡’ |()-()| < " ``` !!! compat "Julia 1.9" Starting with Julia 1.9, `!f` returns a [`ComposedFunction`](@ref) instead of an anonymous function. """ !(f::Function) = (!) โˆ˜ f !(f::ComposedFunction{typeof(!)}) = f.inner #allows !!f === f """ Fix1(f, x) A type representing a partially-applied version of the two-argument function `f`, with the first argument fixed to the value "x". In other words, `Fix1(f, x)` behaves similarly to `y->f(x, y)`. See also [`Fix2`](@ref Base.Fix2). """ struct Fix1{F,T} <: Function f::F x::T Fix1(f::F, x) where {F} = new{F,_stable_typeof(x)}(f, x) Fix1(f::Type{F}, x) where {F} = new{Type{F},_stable_typeof(x)}(f, x) end (f::Fix1)(y) = f.f(f.x, y) """ Fix2(f, x) A type representing a partially-applied version of the two-argument function `f`, with the second argument fixed to the value "x". In other words, `Fix2(f, x)` behaves similarly to `y->f(y, x)`. """ struct Fix2{F,T} <: Function f::F x::T Fix2(f::F, x) where {F} = new{F,_stable_typeof(x)}(f, x) Fix2(f::Type{F}, x) where {F} = new{Type{F},_stable_typeof(x)}(f, x) end (f::Fix2)(y) = f.f(y, f.x) """ isequal(x) Create a function that compares its argument to `x` using [`isequal`](@ref), i.e. a function equivalent to `y -> isequal(y, x)`. The returned function is of type `Base.Fix2{typeof(isequal)}`, which can be used to implement specialized methods. """ isequal(x) = Fix2(isequal, x) """ ==(x) Create a function that compares its argument to `x` using [`==`](@ref), i.e. a function equivalent to `y -> y == x`. The returned function is of type `Base.Fix2{typeof(==)}`, which can be used to implement specialized methods. """ ==(x) = Fix2(==, x) """ !=(x) Create a function that compares its argument to `x` using [`!=`](@ref), i.e. a function equivalent to `y -> y != x`. The returned function is of type `Base.Fix2{typeof(!=)}`, which can be used to implement specialized methods. !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ !=(x) = Fix2(!=, x) """ >=(x) Create a function that compares its argument to `x` using [`>=`](@ref), i.e. a function equivalent to `y -> y >= x`. The returned function is of type `Base.Fix2{typeof(>=)}`, which can be used to implement specialized methods. !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ >=(x) = Fix2(>=, x) """ <=(x) Create a function that compares its argument to `x` using [`<=`](@ref), i.e. a function equivalent to `y -> y <= x`. The returned function is of type `Base.Fix2{typeof(<=)}`, which can be used to implement specialized methods. !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ <=(x) = Fix2(<=, x) """ >(x) Create a function that compares its argument to `x` using [`>`](@ref), i.e. a function equivalent to `y -> y > x`. The returned function is of type `Base.Fix2{typeof(>)}`, which can be used to implement specialized methods. !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ >(x) = Fix2(>, x) """ <(x) Create a function that compares its argument to `x` using [`<`](@ref), i.e. a function equivalent to `y -> y < x`. The returned function is of type `Base.Fix2{typeof(<)}`, which can be used to implement specialized methods. !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ <(x) = Fix2(<, x) """ splat(f) Equivalent to ```julia my_splat(f) = args->f(args...) ``` i.e. given a function returns a new function that takes one argument and splats it into the original function. This is useful as an adaptor to pass a multi-argument function in a context that expects a single argument, but passes a tuple as that single argument. # Example usage: ```jldoctest julia> map(splat(+), zip(1:3,4:6)) 3-element Vector{Int64}: 5 7 9 julia> my_add = splat(+) splat(+) julia> my_add((1,2,3)) 6 ``` """ splat(f) = Splat(f) """ Base.Splat{F} <: Function Represents a splatted function. That is ```julia Base.Splat(f)(args) === f(args...) ``` The preferred way to construct an instance of `Base.Splat` is to use the [`splat`](@ref) function. !!! compat "Julia 1.9" Splat requires at least Julia 1.9. In earlier versions `splat` returns an anonymous function instead. See also [`splat`](@ref). """ struct Splat{F} <: Function f::F Splat(f) = new{Core.Typeof(f)}(f) end (s::Splat)(args) = s.f(args...) print(io::IO, s::Splat) = print(io, "splat(", s.f, ')') show(io::IO, s::Splat) = print(io, s) ## in and related operators """ in(collection) โˆˆ(collection) Create a function that checks whether its argument is [`in`](@ref) `collection`, i.e. a function equivalent to `y -> y in collection`. See also [`insorted`](@ref) for use with sorted collections. The returned function is of type `Base.Fix2{typeof(in)}`, which can be used to implement specialized methods. """ in(x) = Fix2(in, x) function in(x, itr) anymissing = false for y in itr v = (y == x) if ismissing(v) anymissing = true elseif v return true end end return anymissing ? missing : false end const โˆˆ = in โˆ‰(x, itr) = !โˆˆ(x, itr) โˆ‰(itr) = Fix2(โˆ‰, itr) """ โˆ‹(collection, item) -> Bool Like [`in`](@ref), but with arguments in reverse order. Avoid adding methods to this function; define `in` instead. """ โˆ‹(itr, x) = in(x, itr) """ โˆ‹(item) Create a function that checks whether its argument contains the given `item`, i.e. a function equivalent to `y -> item in y`. !!! compat "Julia 1.6" This method requires Julia 1.6 or later. """ โˆ‹(x) = Fix2(โˆ‹, x) โˆŒ(itr, x) = !โˆ‹(itr, x) โˆŒ(x) = Fix2(โˆŒ, x) """ in(item, collection) -> Bool โˆˆ(item, collection) -> Bool Determine whether an item is in the given collection, in the sense that it is [`==`](@ref) to one of the values generated by iterating over the collection. Return a `Bool` value, except if `item` is [`missing`](@ref) or `collection` contains `missing` but not `item`, in which case `missing` is returned ([three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), matching the behavior of [`any`](@ref) and [`==`](@ref)). Some collections follow a slightly different definition. For example, [`Set`](@ref)s check whether the item [`isequal`](@ref) to one of the elements; [`Dict`](@ref)s look for `key=>value` pairs, and the `key` is compared using [`isequal`](@ref). To test for the presence of a key in a dictionary, use [`haskey`](@ref) or `k in keys(dict)`. For the collections mentioned above, the result is always a `Bool`. When broadcasting with `in.(items, collection)` or `items .โˆˆ collection`, both `item` and `collection` are broadcasted over, which is often not what is intended. For example, if both arguments are vectors (and the dimensions match), the result is a vector indicating whether each value in collection `items` is `in` the value at the corresponding position in `collection`. To get a vector indicating whether each value in `items` is in `collection`, wrap `collection` in a tuple or a `Ref` like this: `in.(items, Ref(collection))` or `items .โˆˆ Ref(collection)`. See also: [`โˆ‰`](@ref), [`insorted`](@ref), [`contains`](@ref), [`occursin`](@ref), [`issubset`](@ref). # Examples ```jldoctest julia> a = 1:3:20 1:3:19 julia> 4 in a true julia> 5 in a false julia> missing in [1, 2] missing julia> 1 in [2, missing] missing julia> 1 in [1, missing] true julia> missing in Set([1, 2]) false julia> (1=>missing) in Dict(1=>10, 2=>20) missing julia> [1, 2] .โˆˆ [2, 3] 2-element BitVector: 0 0 julia> [1, 2] .โˆˆ ([2, 3],) 2-element BitVector: 0 1 ``` """ in """ โˆ‰(item, collection) -> Bool โˆŒ(collection, item) -> Bool Negation of `โˆˆ` and `โˆ‹`, i.e. checks that `item` is not in `collection`. When broadcasting with `items .โˆ‰ collection`, both `item` and `collection` are broadcasted over, which is often not what is intended. For example, if both arguments are vectors (and the dimensions match), the result is a vector indicating whether each value in collection `items` is not in the value at the corresponding position in `collection`. To get a vector indicating whether each value in `items` is not in `collection`, wrap `collection` in a tuple or a `Ref` like this: `items .โˆ‰ Ref(collection)`. # Examples ```jldoctest julia> 1 โˆ‰ 2:4 true julia> 1 โˆ‰ 1:3 false julia> [1, 2] .โˆ‰ [2, 3] 2-element BitVector: 1 1 julia> [1, 2] .โˆ‰ ([2, 3],) 2-element BitVector: 1 0 ``` """ โˆ‰, โˆŒ N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/pointer.jlอ.# This file is a part of Julia. License is MIT: https://julialang.org/license """ Ptr{T} A memory address referring to data of type `T`. However, there is no guarantee that the memory is actually valid, or that it actually represents data of the specified type. """ Ptr ## converting pointers to an appropriate unsigned ## """ C_NULL The C null pointer constant, sometimes used when calling external code. """ const C_NULL = bitcast(Ptr{Cvoid}, 0) # TODO: deprecate these conversions. C doesn't even allow them. # pointer to integer convert(::Type{T}, x::Ptr) where {T<:Integer} = T(UInt(x))::T # integer to pointer convert(::Type{Ptr{T}}, x::Union{Int,UInt}) where {T} = Ptr{T}(x) # pointer to pointer convert(::Type{Ptr{T}}, p::Ptr{T}) where {T} = p convert(::Type{Ptr{T}}, p::Ptr) where {T} = bitcast(Ptr{T}, p)::Ptr{T} # object to pointer (when used with ccall) """ unsafe_convert(T, x) Convert `x` to a C argument of type `T` where the input `x` must be the return value of `cconvert(T, ...)`. In cases where [`convert`](@ref) would need to take a Julia object and turn it into a `Ptr`, this function should be used to define and perform that conversion. Be careful to ensure that a Julia reference to `x` exists as long as the result of this function will be used. Accordingly, the argument `x` to this function should never be an expression, only a variable name or field reference. For example, `x=a.b.c` is acceptable, but `x=[a,b,c]` is not. The `unsafe` prefix on this function indicates that using the result of this function after the `x` argument to this function is no longer accessible to the program may cause undefined behavior, including program corruption or segfaults, at any later time. See also [`cconvert`](@ref) """ function unsafe_convert end unsafe_convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any,), x) unsafe_convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x) unsafe_convert(::Type{Ptr{UInt8}}, s::String) = ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s) unsafe_convert(::Type{Ptr{Int8}}, s::String) = ccall(:jl_string_ptr, Ptr{Int8}, (Any,), s) # convert strings to String etc. to pass as pointers cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s) cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s) unsafe_convert(::Type{Ptr{T}}, a::Array{T}) where {T} = ccall(:jl_array_ptr, Ptr{T}, (Any,), a) unsafe_convert(::Type{Ptr{S}}, a::AbstractArray{T}) where {S,T} = convert(Ptr{S}, unsafe_convert(Ptr{T}, a)) unsafe_convert(::Type{Ptr{T}}, a::AbstractArray{T}) where {T} = error("conversion to pointer not defined for $(typeof(a))") # unsafe pointer to array conversions """ unsafe_wrap(Array, pointer::Ptr{T}, dims; own = false) Wrap a Julia `Array` object around the data at the address given by `pointer`, without making a copy. The pointer element type `T` determines the array element type. `dims` is either an integer (for a 1d array) or a tuple of the array dimensions. `own` optionally specifies whether Julia should take ownership of the memory, calling `free` on the pointer when the array is no longer referenced. This function is labeled "unsafe" because it will crash if `pointer` is not a valid memory address to data of the requested length. Unlike [`unsafe_load`](@ref) and [`unsafe_store!`](@ref), the programmer is responsible also for ensuring that the underlying data is not accessed through two arrays of different element type, similar to the strict aliasing rule in C. """ function unsafe_wrap(::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, p::Ptr{T}, dims::NTuple{N,Int}; own::Bool = false) where {T,N} ccall(:jl_ptr_to_array, Array{T,N}, (Any, Ptr{Cvoid}, Any, Int32), Array{T,N}, p, dims, own) end function unsafe_wrap(::Union{Type{Array},Type{Array{T}},Type{Array{T,1}}}, p::Ptr{T}, d::Integer; own::Bool = false) where {T} ccall(:jl_ptr_to_array_1d, Array{T,1}, (Any, Ptr{Cvoid}, Csize_t, Cint), Array{T,1}, p, d, own) end unsafe_wrap(Atype::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, p::Ptr{T}, dims::NTuple{N,<:Integer}; own::Bool = false) where {T,N} = unsafe_wrap(Atype, p, convert(Tuple{Vararg{Int}}, dims), own = own) """ unsafe_load(p::Ptr{T}, i::Integer=1) unsafe_load(p::Ptr{T}, order::Symbol) unsafe_load(p::Ptr{T}, i::Integer, order::Symbol) Load a value of type `T` from the address of the `i`th element (1-indexed) starting at `p`. This is equivalent to the C expression `p[i-1]`. Optionally, an atomic memory ordering can be provided. The `unsafe` prefix on this function indicates that no validation is performed on the pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program or return garbage answers. Unlike C, dereferencing memory region allocated as different type may be valid provided that the types are compatible. !!! compat "Julia 1.10" The `order` argument is available as of Julia 1.10. See also: [`atomic`](@ref) """ unsafe_load(p::Ptr, i::Integer=1) = pointerref(p, Int(i), 1) unsafe_load(p::Ptr, order::Symbol) = atomic_pointerref(p, order) function unsafe_load(p::Ptr, i::Integer, order::Symbol) unsafe_load(p + (elsize(typeof(p)) * (Int(i) - 1)), order) end """ unsafe_store!(p::Ptr{T}, x, i::Integer=1) unsafe_store!(p::Ptr{T}, x, order::Symbol) unsafe_store!(p::Ptr{T}, x, i::Integer, order::Symbol) Store a value of type `T` to the address of the `i`th element (1-indexed) starting at `p`. This is equivalent to the C expression `p[i-1] = x`. Optionally, an atomic memory ordering can be provided. The `unsafe` prefix on this function indicates that no validation is performed on the pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program. Unlike C, storing memory region allocated as different type may be valid provided that that the types are compatible. !!! compat "Julia 1.10" The `order` argument is available as of Julia 1.10. See also: [`atomic`](@ref) """ unsafe_store!(p::Ptr{Any}, @nospecialize(x), i::Integer=1) = pointerset(p, x, Int(i), 1) unsafe_store!(p::Ptr{T}, x, i::Integer=1) where {T} = pointerset(p, convert(T,x), Int(i), 1) unsafe_store!(p::Ptr{T}, x, order::Symbol) where {T} = atomic_pointerset(p, x isa T ? x : convert(T,x), order) function unsafe_store!(p::Ptr, x, i::Integer, order::Symbol) unsafe_store!(p + (elsize(typeof(p)) * (Int(i) - 1)), x, order) end """ unsafe_modify!(p::Ptr{T}, op, x, [order::Symbol]) -> Pair These atomically perform the operations to get and set a memory address after applying the function `op`. If supported by the hardware (for example, atomic increment), this may be optimized to the appropriate hardware instruction, otherwise its execution will be similar to: y = unsafe_load(p) z = op(y, x) unsafe_store!(p, z) return y => z The `unsafe` prefix on this function indicates that no validation is performed on the pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program. !!! compat "Julia 1.10" This function requires at least Julia 1.10. See also: [`modifyproperty!`](@ref Base.modifyproperty!), [`atomic`](@ref) """ function unsafe_modify!(p::Ptr, op, x, order::Symbol=:not_atomic) return atomic_pointermodify(p, op, x, order) end """ unsafe_replace!(p::Ptr{T}, expected, desired, [success_order::Symbol[, fail_order::Symbol=success_order]]) -> (; old, success::Bool) These atomically perform the operations to get and conditionally set a memory address to a given value. If supported by the hardware, this may be optimized to the appropriate hardware instruction, otherwise its execution will be similar to: y = unsafe_load(p, fail_order) ok = y === expected if ok unsafe_store!(p, desired, success_order) end return (; old = y, success = ok) The `unsafe` prefix on this function indicates that no validation is performed on the pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program. !!! compat "Julia 1.10" This function requires at least Julia 1.10. See also: [`replaceproperty!`](@ref Base.replaceproperty!), [`atomic`](@ref) """ function unsafe_replace!(p::Ptr{T}, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) where {T} @inline xT = desired isa T ? desired : convert(T, desired) return atomic_pointerreplace(p, expected, xT, success_order, fail_order) end function unsafe_replace!(p::Ptr{Any}, @nospecialize(expected), @nospecialize(desired), success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) return atomic_pointerreplace(p, expected, desired, success_order, fail_order) end """ unsafe_swap!(p::Ptr{T}, x, [order::Symbol]) These atomically perform the operations to simultaneously get and set a memory address. If supported by the hardware, this may be optimized to the appropriate hardware instruction, otherwise its execution will be similar to: y = unsafe_load(p) unsafe_store!(p, x) return y The `unsafe` prefix on this function indicates that no validation is performed on the pointer `p` to ensure that it is valid. Like C, the programmer is responsible for ensuring that referenced memory is not freed or garbage collected while invoking this function. Incorrect usage may segfault your program. !!! compat "Julia 1.10" This function requires at least Julia 1.10. See also: [`swapproperty!`](@ref Base.swapproperty!), [`atomic`](@ref) """ function unsafe_swap!(p::Ptr{Any}, x, order::Symbol=:not_atomic) return atomic_pointerswap(p, x, order) end function unsafe_swap!(p::Ptr{T}, x, order::Symbol=:not_atomic) where {T} @inline xT = x isa T ? x : convert(T, x) return atomic_pointerswap(p, xT, order) end # convert a raw Ptr to an object reference, and vice-versa """ unsafe_pointer_to_objref(p::Ptr) Convert a `Ptr` to an object reference. Assumes the pointer refers to a valid heap-allocated Julia object. If this is not the case, undefined behavior results, hence this function is considered "unsafe" and should be used with care. See also [`pointer_from_objref`](@ref). """ unsafe_pointer_to_objref(x::Ptr) = ccall(:jl_value_ptr, Any, (Ptr{Cvoid},), x) """ pointer_from_objref(x) Get the memory address of a Julia object as a `Ptr`. The existence of the resulting `Ptr` will not protect the object from garbage collection, so you must ensure that the object remains referenced for the whole time that the `Ptr` will be used. This function may not be called on immutable objects, since they do not have stable memory addresses. See also [`unsafe_pointer_to_objref`](@ref). """ function pointer_from_objref(@nospecialize(x)) @inline ismutable(x) || error("pointer_from_objref cannot be used on immutable objects") ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x) end ## limited pointer arithmetic & comparison ## isequal(x::Ptr, y::Ptr) = (x === y) isless(x::Ptr{T}, y::Ptr{T}) where {T} = x < y ==(x::Ptr, y::Ptr) = UInt(x) == UInt(y) <(x::Ptr, y::Ptr) = UInt(x) < UInt(y) -(x::Ptr, y::Ptr) = UInt(x) - UInt(y) +(x::Ptr, y::Integer) = oftype(x, add_ptr(UInt(x), (y % UInt) % UInt)) -(x::Ptr, y::Integer) = oftype(x, sub_ptr(UInt(x), (y % UInt) % UInt)) +(x::Integer, y::Ptr) = y + x unsigned(x::Ptr) = UInt(x) signed(x::Ptr) = Int(x) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/refvalue.jl# This file is a part of Julia. License is MIT: https://julialang.org/license ### Methods for a Ref object that can store a single value of any type mutable struct RefValue{T} <: Ref{T} x::T RefValue{T}() where {T} = new() RefValue{T}(x) where {T} = new(x) end RefValue(x::T) where {T} = RefValue{T}(x) """ isassigned(ref::RefValue) -> Bool Test whether the given [`Ref`](@ref) is associated with a value. This is always true for a [`Ref`](@ref) of a bitstype object. Return `false` if the reference is undefined. # Examples ```jldoctest julia> ref = Ref{Function}() Base.RefValue{Function}(#undef) julia> isassigned(ref) false julia> ref[] = (foobar(x) = x) foobar (generic function with 1 method) julia> isassigned(ref) true julia> isassigned(Ref{Int}()) true ``` """ isassigned(x::RefValue) = isdefined(x, :x) function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefValue{T})::P where T if allocatedinline(T) p = pointer_from_objref(b) elseif isconcretetype(T) && ismutabletype(T) p = pointer_from_objref(b.x) else # If the slot is not leaf type, it could be either immutable or not. # If it is actually an immutable, then we can't take it's pointer directly # Instead, explicitly load the pointer from the `RefValue`, # which also ensures this returns same pointer as the one rooted in the `RefValue` object. p = atomic_pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), :monotonic) end if p == C_NULL throw(UndefRefError()) end return p end function unsafe_convert(::Type{Ptr{Any}}, b::RefValue{Any})::Ptr{Any} return pointer_from_objref(b) end getindex(b::RefValue) = b.x setindex!(b::RefValue, x) = (b.x = x; b) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/cmem.jl=# This file is a part of Julia. License is MIT: https://julialang.org/license """ memcpy(dst::Ptr, src::Ptr, n::Integer) -> Ptr{Cvoid} Call `memcpy` from the C standard library. !!! compat "Julia 1.10" Support for `memcpy` requires at least Julia 1.10. """ function memcpy(dst::Ptr, src::Ptr, n::Integer) ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), dst, src, n) end """ memmove(dst::Ptr, src::Ptr, n::Integer) -> Ptr{Cvoid} Call `memmove` from the C standard library. !!! compat "Julia 1.10" Support for `memmove` requires at least Julia 1.10. """ function memmove(dst::Ptr, src::Ptr, n::Integer) ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), dst, src, n) end """ memset(dst::Ptr, val, n::Integer) -> Ptr{Cvoid} Call `memset` from the C standard library. !!! compat "Julia 1.10" Support for `memset` requires at least Julia 1.10. """ function memset(p::Ptr, val, n::Integer) ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), p, val, n) end """ memcmp(a::Ptr, b::Ptr, n::Integer) -> Int Call `memcmp` from the C standard library. !!! compat "Julia 1.10" Support for `memcmp` requires at least Julia 1.9. """ function memcmp(a::Ptr, b::Ptr, n::Integer) ccall(:memcmp, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), a, b, n % Csize_t) % Int end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/refpointer.jl€# This file is a part of Julia. License is MIT: https://julialang.org/license """ Ref{T} An object that safely references data of type `T`. This type is guaranteed to point to valid, Julia-allocated memory of the correct type. The underlying data is protected from freeing by the garbage collector as long as the `Ref` itself is referenced. In Julia, `Ref` objects are dereferenced (loaded or stored) with `[]`. Creation of a `Ref` to a value `x` of type `T` is usually written `Ref(x)`. Additionally, for creating interior pointers to containers (such as Array or Ptr), it can be written `Ref(a, i)` for creating a reference to the `i`-th element of `a`. `Ref{T}()` creates a reference to a value of type `T` without initialization. For a bitstype `T`, the value will be whatever currently resides in the memory allocated. For a non-bitstype `T`, the reference will be undefined and attempting to dereference it will result in an error, "UndefRefError: access to undefined reference". To check if a `Ref` is an undefined reference, use [`isassigned(ref::RefValue)`](@ref). For example, `isassigned(Ref{T}())` is `false` if `T` is not a bitstype. If `T` is a bitstype, `isassigned(Ref{T}())` will always be true. When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref` object will be converted to a native pointer to the data it references. For most `T`, or when converted to a `Ptr{Cvoid}`, this is a pointer to the object data. When `T` is an `isbits` type, this value may be safely mutated, otherwise mutation is strictly undefined behavior. As a special case, setting `T = Any` will instead cause the creation of a pointer to the reference itself when converted to a `Ptr{Any}` (a `jl_value_t const* const*` if T is immutable, else a `jl_value_t *const *`). When converted to a `Ptr{Cvoid}`, it will still return a pointer to the data region as for any other `T`. A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initialize it. # Use in broadcasting `Ref` is sometimes used in broadcasting in order to treat the referenced values as a scalar. # Examples ```jldoctest julia> Ref(5) Base.RefValue{Int64}(5) julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) # Treat reference values as scalar during broadcasting 3-element BitVector: 1 0 0 julia> Ref{Function}() # Undefined reference to a non-bitstype, Function Base.RefValue{Function}(#undef) julia> try Ref{Function}()[] # Dereferencing an undefined reference will result in an error catch e println(e) end UndefRefError() julia> Ref{Int64}()[]; # A reference to a bitstype refers to an undetermined value if not given julia> isassigned(Ref{Int64}()) # A reference to a bitstype is always assigned true julia> Ref{Int64}(0)[] == 0 # Explicitly give a value for a bitstype reference true ``` """ Ref # C NUL-terminated string pointers; these can be used in ccall # instead of Ptr{Cchar} and Ptr{Cwchar_t}, respectively, to enforce # a check for embedded NUL chars in the string (to avoid silent truncation). if Int === Int64 primitive type Cstring 64 end primitive type Cwstring 64 end else primitive type Cstring 32 end primitive type Cwstring 32 end end ### General Methods for Ref{T} type eltype(x::Type{<:Ref{T}}) where {T} = @isdefined(T) ? T : Any convert(::Type{Ref{T}}, x::Ref{T}) where {T} = x size(x::Ref) = () axes(x::Ref) = () length(x::Ref) = 1 isempty(x::Ref) = false ndims(x::Ref) = 0 ndims(::Type{<:Ref}) = 0 iterate(r::Ref) = (r[], nothing) iterate(r::Ref, s) = nothing IteratorSize(::Type{<:Ref}) = HasShape{0}() # create Ref objects for general object conversion unsafe_convert(::Type{Ref{T}}, x::Ref{T}) where {T} = unsafe_convert(Ptr{T}, x) unsafe_convert(::Type{Ref{T}}, x) where {T} = unsafe_convert(Ptr{T}, x) convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x)::RefValue{T} ### Methods for a Ref object that is backed by an array at index i struct RefArray{T,A<:AbstractArray{T},R} <: Ref{T} x::A i::Int roots::R # should be either ::Nothing or ::Any RefArray{T,A,R}(x,i,roots=nothing) where {T,A<:AbstractArray{T},R} = new(x,i,roots) end RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, i, roots) RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing) RefArray(x::AbstractArray{T}, i::Integer, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, Int(i), roots) RefArray(x::AbstractArray{T}, i::Integer, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, Int(i), nothing) convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1) function unsafe_convert(P::Union{Type{Ptr{T}},Type{Ptr{Cvoid}}}, b::RefArray{T})::P where T if allocatedinline(T) p = pointer(b.x, b.i) elseif isconcretetype(T) && ismutabletype(T) p = pointer_from_objref(b.x[b.i]) else # see comment on equivalent branch for RefValue p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid})) end return p end function unsafe_convert(::Type{Ptr{Any}}, b::RefArray{Any})::Ptr{Any} return pointer(b.x, b.i) end ### if is_primary_base_module Ref(x::Any) = RefValue(x) Ref{T}() where {T} = RefValue{T}() # Ref{T}() Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x) Ref(x::Ref, i::Integer) = (i != 1 && error("Ref only has one element"); x) Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T) # convert Arrays to pointer arrays for ccall function Ref{P}(a::Array{<:Union{Ptr,Cwstring,Cstring}}) where P<:Union{Ptr,Cwstring,Cstring} return RefArray(a) # effectively a no-op end function Ref{P}(a::Array{T}) where P<:Union{Ptr,Cwstring,Cstring} where T if (!isbitstype(T) && T <: eltype(P)) # this Array already has the right memory layout for the requested Ref return RefArray(a,1,false) # root something, so that this function is type-stable else ptrs = Vector{P}(undef, length(a)+1) roots = Vector{Any}(undef, length(a)) for i = 1:length(a) root = cconvert(P, a[i]) ptrs[i] = unsafe_convert(P, root)::P roots[i] = root end ptrs[length(a)+1] = C_NULL return RefArray(ptrs,1,roots) end end Ref(x::AbstractArray, i::Integer) = RefArray(x, i) end cconvert(::Type{Ptr{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a cconvert(::Type{Ref{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a cconvert(::Type{Ptr{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a) cconvert(::Type{Ref{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a) # pass NTuple{N,T} as Ptr{T}/Ref{T} cconvert(::Type{Ref{T}}, t::NTuple{N,T}) where {N,T} = Ref{NTuple{N,T}}(t) cconvert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = r unsafe_convert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r)) unsafe_convert(::Type{Ptr{T}}, r::Ref{NTuple{N,T}}) where {N,T} = convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r)) unsafe_convert(::Type{Ptr{T}}, r::Ptr{NTuple{N,T}}) where {N,T} = convert(Ptr{T}, r) ### getindex(b::RefArray) = b.x[b.i] setindex!(b::RefArray, x) = (b.x[b.i] = x; b) ### """ LLVMPtr{T, AS} A pointer type that more closely resembles LLVM semantics: It includes the pointer address space, and will be passed as an actual pointer instead of an integer. This type is mainly used to interface with code that has strict requirements about pointers, e.g., intrinsics that are selected based on the address space, or back-ends that require pointers to be identifiable by their types. """ Core.LLVMPtr N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/checked.jl0# This file is a part of Julia. License is MIT: https://julialang.org/license # Support for checked integer arithmetic """ Checked The Checked module provides arithmetic functions for the built-in signed and unsigned Integer types which throw an error when an overflow occurs. They are named like `checked_sub`, `checked_div`, etc. In addition, `add_with_overflow`, `sub_with_overflow`, `mul_with_overflow` return both the unchecked results and a boolean value denoting the presence of an overflow. """ module Checked export checked_neg, checked_abs, checked_add, checked_sub, checked_mul, checked_div, checked_rem, checked_fld, checked_mod, checked_cld, checked_length, add_with_overflow, sub_with_overflow, mul_with_overflow import Core.Intrinsics: checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int, checked_srem_int, checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, checked_urem_int import ..no_op_err, ..@inline, ..@noinline, ..checked_length # define promotion behavior for checked operations checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) checked_sub(x::Integer, y::Integer) = checked_sub(promote(x,y)...) checked_mul(x::Integer, y::Integer) = checked_mul(promote(x,y)...) checked_div(x::Integer, y::Integer) = checked_div(promote(x,y)...) checked_rem(x::Integer, y::Integer) = checked_rem(promote(x,y)...) checked_fld(x::Integer, y::Integer) = checked_fld(promote(x,y)...) checked_mod(x::Integer, y::Integer) = checked_mod(promote(x,y)...) checked_cld(x::Integer, y::Integer) = checked_cld(promote(x,y)...) # fallback catchall rules to prevent infinite recursion if promotion succeeds, # but no method exists to handle those types checked_abs(x::T) where {T<:Integer} = no_op_err("checked_abs", T) const SignedInt = Union{Int8,Int16,Int32,Int64,Int128} const UnsignedInt = Union{UInt8,UInt16,UInt32,UInt64,UInt128} # LLVM has several code generation bugs for checked integer arithmetic (see e.g. # #4905). We thus distinguish between operations that can be implemented via # intrinsics, and operations for which we have to provide workarounds. # Note: As far as this code has been tested, most checked_* functions are # working fine in LLVM. (Note that division is still handled via `base/int.jl`, # which always checks for overflow, and which provides its own sets of # workarounds for LLVM codegen bugs.) However, the comments in `base/int.jl` # and in issue #4905 are more pessimistic. For the time being, we thus retain # the ability to handle codegen bugs in LLVM, until the code here has been # tested on more systems and architectures. It also seems that things depend on # which compiler that was used to build LLVM (i.e. either gcc or clang). # These unions are used for most checked functions: # BrokenSignedInt # BrokenUnsignedInt # These unions are used for checked_{mul,div,rem}: # BrokenSignedIntMul # BrokenUnsignedIntMul # This code runs early during bootstrap, and we can't use Julia's version # strings yet const llvm_version = Int(ccall(:jl_get_LLVM_VERSION, UInt32, ())) brokenSignedInt = Union{} brokenUnsignedInt = Union{} brokenSignedIntMul = Int128 brokenUnsignedIntMul = UInt128 if Core.sizeof(Ptr{Cvoid}) == 4 brokenSignedIntMul = Union{brokenSignedIntMul, Int64} brokenUnsignedIntMul = Union{brokenUnsignedIntMul, UInt64} end const BrokenSignedInt = brokenSignedInt const BrokenUnsignedInt = brokenUnsignedInt const BrokenSignedIntMul = brokenSignedIntMul const BrokenUnsignedIntMul = brokenUnsignedIntMul # Use these definitions to test the non-LLVM implementations # const BrokenSignedInt = SignedInt # const BrokenUnsignedInt = UnsignedInt # const BrokenSignedIntMul = SignedInt # const BrokenUnsignedIntMul = UnsignedInt """ Base.checked_neg(x) Calculates `-x`, checking for overflow errors where applicable. For example, standard two's complement signed integers (e.g. `Int`) cannot represent `-typemin(Int)`, thus leading to an overflow. The overflow protection may impose a perceptible performance penalty. """ function checked_neg(x::T) where T<:Integer checked_sub(T(0), x) end throw_overflowerr_negation(x) = (@noinline; throw(OverflowError(Base.invokelatest(string, "checked arithmetic: cannot compute -x for x = ", x, "::", typeof(x))))) if BrokenSignedInt != Union{} function checked_neg(x::BrokenSignedInt) r = -x (x<0) & (r<0) && throw_overflowerr_negation(x) r end end if BrokenUnsignedInt != Union{} function checked_neg(x::T) where T<:BrokenUnsignedInt x != 0 && throw_overflowerr_negation(x) T(0) end end """ Base.checked_abs(x) Calculates `abs(x)`, checking for overflow errors where applicable. For example, standard two's complement signed integers (e.g. `Int`) cannot represent `abs(typemin(Int))`, thus leading to an overflow. The overflow protection may impose a perceptible performance penalty. """ function checked_abs end function checked_abs(x::SignedInt) r = ifelse(x<0, -x, x) r<0 || return r msg = LazyString("checked arithmetic: cannot compute |x| for x = ", x, "::", typeof(x)) throw(OverflowError(msg)) end checked_abs(x::UnsignedInt) = x checked_abs(x::Bool) = x """ Base.add_with_overflow(x, y) -> (r, f) Calculates `r = x+y`, with the flag `f` indicating whether overflow has occurred. """ function add_with_overflow end add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) add_with_overflow(x::Bool, y::Bool) = (x+y, false) if BrokenSignedInt != Union{} function add_with_overflow(x::T, y::T) where T<:BrokenSignedInt r = x + y # x and y have the same sign, and the result has a different sign f = (x<0) == (y<0) != (r<0) r, f end end if BrokenUnsignedInt != Union{} function add_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt # x + y > typemax(T) # Note: ~y == -y-1 x + y, x > ~y end end throw_overflowerr_binaryop(op, x, y) = (@noinline; throw(OverflowError(LazyString(x, " ", op, " ", y, " overflowed for type ", typeof(x))))) """ Base.checked_add(x, y) Calculates `x+y`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ function checked_add(x::T, y::T) where T<:Integer @inline z, b = add_with_overflow(x, y) b && throw_overflowerr_binaryop(:+, x, y) z end # Handle multiple arguments checked_add(x) = x checked_add(x::Bool) = +x checked_add(x1::T, x2::T, x3::T) where {T} = checked_add(checked_add(x1, x2), x3) checked_add(x1::T, x2::T, x3::T, x4::T) where {T} = checked_add(checked_add(x1, x2), x3, x4) checked_add(x1::T, x2::T, x3::T, x4::T, x5::T) where {T} = checked_add(checked_add(x1, x2), x3, x4, x5) checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) where {T} = checked_add(checked_add(x1, x2), x3, x4, x5, x6) checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) where {T} = checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7) checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) where {T} = checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7, x8) """ Base.sub_with_overflow(x, y) -> (r, f) Calculates `r = x-y`, with the flag `f` indicating whether overflow has occurred. """ function sub_with_overflow end sub_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_ssub_int(x, y) sub_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_usub_int(x, y) sub_with_overflow(x::Bool, y::Bool) = (x-y, false) if BrokenSignedInt != Union{} function sub_with_overflow(x::T, y::T) where T<:BrokenSignedInt r = x - y # x and y have different signs, and the result has a different sign than x f = (x<0) != (y<0) == (r<0) r, f end end if BrokenUnsignedInt != Union{} function sub_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt # x - y < 0 x - y, x < y end end """ Base.checked_sub(x, y) Calculates `x-y`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ function checked_sub(x::T, y::T) where T<:Integer @inline z, b = sub_with_overflow(x, y) b && throw_overflowerr_binaryop(:-, x, y) z end """ Base.mul_with_overflow(x, y) -> (r, f) Calculates `r = x*y`, with the flag `f` indicating whether overflow has occurred. """ function mul_with_overflow end mul_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_smul_int(x, y) mul_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_umul_int(x, y) mul_with_overflow(x::Bool, y::Bool) = (x*y, false) if BrokenSignedIntMul != Union{} && BrokenSignedIntMul != Int128 function mul_with_overflow(x::T, y::T) where T<:BrokenSignedIntMul r = widemul(x, y) f = r % T != r r % T, f end end if BrokenUnsignedIntMul != Union{} && BrokenUnsignedIntMul != UInt128 function mul_with_overflow(x::T, y::T) where T<:BrokenUnsignedIntMul r = widemul(x, y) f = r % T != r r % T, f end end if Int128 <: BrokenSignedIntMul # Avoid BigInt function mul_with_overflow(x::T, y::T) where T<:Int128 f = if y > 0 # x * y > typemax(T) # x * y < typemin(T) x > fld(typemax(T), y) || x < cld(typemin(T), y) elseif y < 0 # x * y > typemax(T) # x * y < typemin(T) # y == -1 can overflow fld x < cld(typemax(T), y) || y != -1 && x > fld(typemin(T), y) else false end x*y, f end end if UInt128 <: BrokenUnsignedIntMul # Avoid BigInt function mul_with_overflow(x::T, y::T) where T<:UInt128 # x * y > typemax(T) x * y, y > 0 && x > fld(typemax(T), y) end end """ Base.checked_mul(x, y) Calculates `x*y`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ function checked_mul(x::T, y::T) where T<:Integer @inline z, b = mul_with_overflow(x, y) b && throw_overflowerr_binaryop(:*, x, y) z end # Handle multiple arguments checked_mul(x) = x checked_mul(x1::T, x2::T, x3::T) where {T} = checked_mul(checked_mul(x1, x2), x3) checked_mul(x1::T, x2::T, x3::T, x4::T) where {T} = checked_mul(checked_mul(x1, x2), x3, x4) checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T) where {T} = checked_mul(checked_mul(x1, x2), x3, x4, x5) checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) where {T} = checked_mul(checked_mul(x1, x2), x3, x4, x5, x6) checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) where {T} = checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7) checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) where {T} = checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7, x8) """ Base.checked_div(x, y) Calculates `div(x,y)`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ checked_div(x::T, y::T) where {T<:Integer} = div(x, y) # Base.div already checks """ Base.checked_rem(x, y) Calculates `x%y`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ checked_rem(x::T, y::T) where {T<:Integer} = rem(x, y) # Base.rem already checks """ Base.checked_fld(x, y) Calculates `fld(x,y)`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ checked_fld(x::T, y::T) where {T<:Integer} = fld(x, y) # Base.fld already checks """ Base.checked_mod(x, y) Calculates `mod(x,y)`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ checked_mod(x::T, y::T) where {T<:Integer} = mod(x, y) # Base.mod already checks """ Base.checked_cld(x, y) Calculates `cld(x,y)`, checking for overflow errors where applicable. The overflow protection may impose a perceptible performance penalty. """ checked_cld(x::T, y::T) where {T<:Integer} = cld(x, y) # Base.cld already checks """ Base.checked_length(r) Calculates `length(r)`, but may check for overflow errors where applicable when the result doesn't fit into `Union{Integer(eltype(r)),Int}`. """ checked_length(r) = length(r) # for most things, length doesn't error end S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/lazy.jlš """ LazyString <: AbstractString A lazy representation of string interpolation. This is useful when a string needs to be constructed in a context where performing the actual interpolation and string construction is unnecessary or undesirable (e.g. in error paths of functions). This type is designed to be cheap to construct at runtime, trying to offload as much work as possible to either the macro or later printing operations. # Examples ```jldoctest julia> n = 5; str = LazyString("n is ", n) "n is 5" ``` See also [`@lazy_str`](@ref). !!! compat "Julia 1.8" `LazyString` requires Julia 1.8 or later. # Extended help ## Safety properties for concurrent programs A lazy string itself does not introduce any concurrency problems even if it is printed in multiple Julia tasks. However, if `print` methods on a captured value can have a concurrency issue when invoked without synchronizations, printing the lazy string may cause an issue. Furthermore, the `print` methods on the captured values may be invoked multiple times, though only exactly one result will be returned. !!! compat "Julia 1.9" `LazyString` is safe in the above sense in Julia 1.9 and later. """ mutable struct LazyString <: AbstractString const parts::Tuple # Created on first access @atomic str::Union{String,Nothing} global _LazyString(parts, str) = new(parts, str) LazyString(args...) = new(args, nothing) end """ lazy"str" Create a [`LazyString`](@ref) using regular string interpolation syntax. Note that interpolations are *evaluated* at LazyString construction time, but *printing* is delayed until the first access to the string. See [`LazyString`](@ref) documentation for the safety properties for concurrent programs. # Examples ``` julia> n = 5; str = lazy"n is \$n" "n is 5" julia> typeof(str) LazyString ``` !!! compat "Julia 1.8" `lazy"str"` requires Julia 1.8 or later. """ macro lazy_str(text) parts = Any[] lastidx = idx = 1 while (idx = findnext('$', text, idx)) !== nothing lastidx < idx && push!(parts, text[lastidx:prevind(text, idx)]) idx += 1 expr, idx = Meta.parseatom(text, idx; filename=string(__source__.file)) push!(parts, esc(expr)) lastidx = idx end lastidx <= lastindex(text) && push!(parts, text[lastidx:end]) :(LazyString($(parts...))) end function String(l::LazyString) old = @atomic :acquire l.str old === nothing || return old str = sprint() do io for p in l.parts print(io, p) end end old, ok = @atomicreplace :acquire_release :acquire l.str nothing => str return ok ? str : (old::String) end hash(s::LazyString, h::UInt64) = hash(String(s), h) lastindex(s::LazyString) = lastindex(String(s)) iterate(s::LazyString) = iterate(String(s)) iterate(s::LazyString, i::Integer) = iterate(String(s), i) isequal(a::LazyString, b::LazyString) = isequal(String(a), String(b)) ==(a::LazyString, b::LazyString) = (String(a) == String(b)) ncodeunits(s::LazyString) = ncodeunits(String(s)) codeunit(s::LazyString) = codeunit(String(s)) codeunit(s::LazyString, i::Integer) = codeunit(String(s), i) isvalid(s::LazyString, i::Integer) = isvalid(String(s), i) N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/indices.jlฯM# This file is a part of Julia. License is MIT: https://julialang.org/license """ Dims{N} An `NTuple` of `N` `Int`s used to represent the dimensions of an [`AbstractArray`](@ref). """ Dims{N} = NTuple{N,Int} DimsInteger{N} = NTuple{N,Integer} Indices{N} = NTuple{N,AbstractUnitRange} ## Traits for array types ## abstract type IndexStyle end """ IndexLinear() Subtype of [`IndexStyle`](@ref) used to describe arrays which are optimally indexed by one linear index. A linear indexing style uses one integer index to describe the position in the array (even if it's a multidimensional array) and column-major ordering is used to efficiently access the elements. This means that requesting [`eachindex`](@ref) from an array that is `IndexLinear` will return a simple one-dimensional range, even if it is multidimensional. A custom array that reports its `IndexStyle` as `IndexLinear` only needs to implement indexing (and indexed assignment) with a single `Int` index; all other indexing expressions โ€” including multidimensional accesses โ€” will be recomputed to the linear index. For example, if `A` were a `2ร—3` custom matrix with linear indexing, and we referenced `A[1, 3]`, this would be recomputed to the equivalent linear index and call `A[5]` since `1 + 2*(3 - 1) = 5`. See also [`IndexCartesian`](@ref). """ struct IndexLinear <: IndexStyle end """ IndexCartesian() Subtype of [`IndexStyle`](@ref) used to describe arrays which are optimally indexed by a Cartesian index. This is the default for new custom [`AbstractArray`](@ref) subtypes. A Cartesian indexing style uses multiple integer indices to describe the position in a multidimensional array, with exactly one index per dimension. This means that requesting [`eachindex`](@ref) from an array that is `IndexCartesian` will return a range of [`CartesianIndices`](@ref). A `N`-dimensional custom array that reports its `IndexStyle` as `IndexCartesian` needs to implement indexing (and indexed assignment) with exactly `N` `Int` indices; all other indexing expressions โ€” including linear indexing โ€” will be recomputed to the equivalent Cartesian location. For example, if `A` were a `2ร—3` custom matrix with cartesian indexing, and we referenced `A[5]`, this would be recomputed to the equivalent Cartesian index and call `A[1, 3]` since `5 = 1 + 2*(3 - 1)`. It is significantly more expensive to compute Cartesian indices from a linear index than it is to go the other way. The former operation requires division โ€” a very costly operation โ€” whereas the latter only uses multiplication and addition and is essentially free. This asymmetry means it is far more costly to use linear indexing with an `IndexCartesian` array than it is to use Cartesian indexing with an `IndexLinear` array. See also [`IndexLinear`](@ref). """ struct IndexCartesian <: IndexStyle end """ IndexStyle(A) IndexStyle(typeof(A)) `IndexStyle` specifies the "native indexing style" for array `A`. When you define a new [`AbstractArray`](@ref) type, you can choose to implement either linear indexing (with [`IndexLinear`](@ref)) or cartesian indexing. If you decide to only implement linear indexing, then you must set this trait for your array type: Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() The default is [`IndexCartesian()`](@ref). Julia's internal indexing machinery will automatically (and invisibly) recompute all indexing operations into the preferred style. This allows users to access elements of your array using any indexing style, even when explicit methods have not been provided. If you define both styles of indexing for your `AbstractArray`, this trait can be used to select the most performant indexing style. Some methods check this trait on their inputs, and dispatch to different algorithms depending on the most efficient access pattern. In particular, [`eachindex`](@ref) creates an iterator whose type depends on the setting of this trait. """ IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) IndexStyle(::Type{Union{}}, slurp...) = IndexLinear() IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() IndexStyle(::Type{<:Array}) = IndexLinear() IndexStyle(::Type{<:AbstractRange}) = IndexLinear() IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear() IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian() # array shape rules promote_shape(::Tuple{}, ::Tuple{}) = () function promote_shape(a::Tuple{Int,}, b::Tuple{Int,}) if a[1] != b[1] throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end function promote_shape(a::Tuple{Int,Int}, b::Tuple{Int,}) if a[1] != b[1] || a[2] != 1 throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end promote_shape(a::Tuple{Int,}, b::Tuple{Int,Int}) = promote_shape(b, a) function promote_shape(a::Tuple{Int, Int}, b::Tuple{Int, Int}) if a[1] != b[1] || a[2] != b[2] throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end """ promote_shape(s1, s2) Check two array shapes for compatibility, allowing trailing singleton dimensions, and return whichever shape has more dimensions. # Examples ```jldoctest julia> a = fill(1, (3,4,1,1,1)); julia> b = fill(1, (3,4)); julia> promote_shape(a,b) (Base.OneTo(3), Base.OneTo(4), Base.OneTo(1), Base.OneTo(1), Base.OneTo(1)) julia> promote_shape((2,3,1,4), (2, 3, 1, 4, 1)) (2, 3, 1, 4, 1) ``` """ function promote_shape(a::Dims, b::Dims) if length(a) < length(b) return promote_shape(b, a) end for i=1:length(b) if a[i] != b[i] throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b, mismatch at $i")) end end for i=length(b)+1:length(a) if a[i] != 1 throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) end end return a end function promote_shape(a::AbstractArray, b::AbstractArray) promote_shape(axes(a), axes(b)) end function promote_shape(a::Indices, b::Indices) if length(a) < length(b) return promote_shape(b, a) end for i=1:length(b) if a[i] != b[i] throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b, mismatch at $i")) end end for i=length(b)+1:length(a) if a[i] != 1:1 throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) end end return a end function throw_setindex_mismatch(X, I) if length(I) == 1 throw(DimensionMismatch("tried to assign $(length(X)) elements to $(I[1]) destinations")) else throw(DimensionMismatch("tried to assign $(dims2string(size(X))) array to $(dims2string(I)) destination")) end end # check for valid sizes in A[I...] = X where X <: AbstractArray # we want to allow dimensions that are equal up to permutation, but only # for permutations that leave array elements in the same linear order. # those are the permutations that preserve the order of the non-singleton # dimensions. function setindex_shape_check(X::AbstractArray, I::Integer...) li = ndims(X) lj = length(I) i = j = 1 while true ii = length(axes(X,i)) jj = I[j] if i == li || j == lj while i < li i += 1 ii *= length(axes(X,i)) end while j < lj j += 1 jj *= I[j] end if ii != jj throw_setindex_mismatch(X, I) end return end if ii == jj i += 1 j += 1 elseif ii == 1 i += 1 elseif jj == 1 j += 1 else throw_setindex_mismatch(X, I) end end end setindex_shape_check(X::AbstractArray) = (length(X)==1 || throw_setindex_mismatch(X,())) setindex_shape_check(X::AbstractArray, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) setindex_shape_check(X::AbstractArray{<:Any, 0}, i::Integer...) = (length(X) == prod(i) || throw_setindex_mismatch(X, i)) setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer) = (length(X)==i || throw_setindex_mismatch(X, (i,))) setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer, j::Integer) = (length(X)==i*j || throw_setindex_mismatch(X, (i,j))) function setindex_shape_check(X::AbstractArray{<:Any,2}, i::Integer, j::Integer) if length(X) != i*j throw_setindex_mismatch(X, (i,j)) end sx1 = length(axes(X,1)) if !(i == 1 || i == sx1 || sx1 == 1) throw_setindex_mismatch(X, (i,j)) end end setindex_shape_check(::Any...) = throw(ArgumentError("indexed assignment with a single value to possibly many locations is not supported; perhaps use broadcasting `.=` instead?")) # convert to a supported index type (array or Int) """ to_index(A, i) Convert index `i` to an `Int` or array of indices to be used as an index into array `A`. Custom array types may specialize `to_index(::CustomArray, i)` to provide special indexing behaviors. Note that some index types (like `Colon`) require more context in order to transform them into an array of indices; those get converted in the more complicated `to_indices` function. By default, this simply calls the generic `to_index(i)`. This must return either an `Int` or an `AbstractArray` of scalar indices that are supported by `A`. """ to_index(A, i) = to_index(i) # This is ok for Array because values larger than # typemax(Int) will BoundsError anyway to_index(A::Array, i::UInt) = reinterpret(Int, i) """ to_index(i) Convert index `i` to an `Int` or array of `Int`s to be used as an index for all arrays. Custom index types may specialize `to_index(::CustomIndex)` to provide special indexing behaviors. This must return either an `Int` or an `AbstractArray` of `Int`s. """ to_index(i::Integer) = convert(Int,i)::Int to_index(i::Bool) = throw(ArgumentError("invalid index: $i of type Bool")) to_index(I::AbstractArray{Bool}) = LogicalIndex(I) to_index(I::AbstractArray) = I to_index(I::AbstractArray{Union{}}) = I to_index(I::AbstractArray{<:Union{AbstractArray, Colon}}) = throw(ArgumentError("invalid index: $(limitrepr(I)) of type $(typeof(I))")) to_index(::Colon) = throw(ArgumentError("colons must be converted by to_indices(...)")) to_index(i) = throw(ArgumentError("invalid index: $(limitrepr(i)) of type $(typeof(i))")) # The general to_indices is mostly defined in multidimensional.jl, but this # definition is required for bootstrap: """ to_indices(A, I::Tuple) Convert the tuple `I` to a tuple of indices for use in indexing into array `A`. The returned tuple must only contain either `Int`s or `AbstractArray`s of scalar indices that are supported by array `A`. It will error upon encountering a novel index type that it does not know how to process. For simple index types, it defers to the unexported `Base.to_index(A, i)` to process each index `i`. While this internal function is not intended to be called directly, `Base.to_index` may be extended by custom array or index types to provide custom indexing behaviors. More complicated index types may require more context about the dimension into which they index. To support those cases, `to_indices(A, I)` calls `to_indices(A, axes(A), I)`, which then recursively walks through both the given tuple of indices and the dimensional indices of `A` in tandem. As such, not all index types are guaranteed to propagate to `Base.to_index`. # Examples ```jldoctest julia> A = zeros(1,2,3,4); julia> to_indices(A, (1,1,2,2)) (1, 1, 2, 2) julia> to_indices(A, (1,1,2,20)) # no bounds checking (1, 1, 2, 20) julia> to_indices(A, (CartesianIndex((1,)), 2, CartesianIndex((3,4)))) # exotic index (1, 2, 3, 4) julia> to_indices(A, ([1,1], 1:2, 3, 4)) ([1, 1], 1:2, 3, 4) julia> to_indices(A, (1,2)) # no shape checking (1, 2) ``` """ to_indices(A, I::Tuple) = (@inline; to_indices(A, axes(A), I)) to_indices(A, I::Tuple{Any}) = (@inline; to_indices(A, (eachindex(IndexLinear(), A),), I)) # In simple cases, we know that we don't need to use axes(A), optimize those. # Having this here avoids invalidations from multidimensional.jl: to_indices(A, I::Tuple{Vararg{Union{Integer, CartesianIndex}}}) to_indices(A, I::Tuple{}) = () to_indices(A, I::Tuple{Vararg{Int}}) = I to_indices(A, I::Tuple{Vararg{Integer}}) = (@inline; to_indices(A, (), I)) to_indices(A, inds, ::Tuple{}) = () function to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) @inline head = _to_indices1(A, inds, I[1]) rest = to_indices(A, _cutdim(inds, I[1]), tail(I)) (head..., rest...) end _to_indices1(A, inds, I1) = (to_index(A, I1),) _cutdim(inds, I1) = safe_tail(inds) """ Slice(indices) Represent an AbstractUnitRange of indices as a vector of the indices themselves, with special handling to signal they represent a complete slice of a dimension (:). Upon calling `to_indices`, Colons are converted to Slice objects to represent the indices over which the Colon spans. Slice objects are themselves unit ranges with the same indices as those they wrap. This means that indexing into Slice objects with an integer always returns that exact integer, and they iterate over all the wrapped indices, even supporting offset indices. """ struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end Slice(S::Slice) = S Slice{T}(S::Slice) where {T<:AbstractUnitRange} = Slice{T}(T(S.indices)) axes(S::Slice) = (IdentityUnitRange(S.indices),) axes1(S::Slice) = IdentityUnitRange(S.indices) axes(S::Slice{<:OneTo}) = (S.indices,) axes1(S::Slice{<:OneTo}) = S.indices first(S::Slice) = first(S.indices) last(S::Slice) = last(S.indices) size(S::Slice) = (length(S.indices),) length(S::Slice) = length(S.indices) getindex(S::Slice, i::Int) = (@inline; @boundscheck checkbounds(S, i); i) getindex(S::Slice, i::AbstractUnitRange{<:Integer}) = (@inline; @boundscheck checkbounds(S, i); i) getindex(S::Slice, i::StepRange{<:Integer}) = (@inline; @boundscheck checkbounds(S, i); i) show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") iterate(S::Slice, s...) = iterate(S.indices, s...) """ IdentityUnitRange(range::AbstractUnitRange) Represent an AbstractUnitRange `range` as an offset vector such that `range[i] == i`. `IdentityUnitRange`s are frequently used as axes for offset arrays. """ struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end IdentityUnitRange(S::IdentityUnitRange) = S IdentityUnitRange{T}(S::IdentityUnitRange) where {T<:AbstractUnitRange} = IdentityUnitRange{T}(T(S.indices)) # IdentityUnitRanges are offset and thus have offset axes, so they are their own axes axes(S::IdentityUnitRange) = (S,) axes1(S::IdentityUnitRange) = S axes(S::IdentityUnitRange{<:OneTo}) = (S.indices,) axes1(S::IdentityUnitRange{<:OneTo}) = S.indices first(S::IdentityUnitRange) = first(S.indices) last(S::IdentityUnitRange) = last(S.indices) size(S::IdentityUnitRange) = (length(S.indices),) length(S::IdentityUnitRange) = length(S.indices) getindex(S::IdentityUnitRange, i::Int) = (@inline; @boundscheck checkbounds(S, i); i) getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@inline; @boundscheck checkbounds(S, i); i) getindex(S::IdentityUnitRange, i::StepRange{<:Integer}) = (@inline; @boundscheck checkbounds(S, i); i) show(io::IO, r::IdentityUnitRange) = print(io, "Base.IdentityUnitRange(", r.indices, ")") iterate(S::IdentityUnitRange, s...) = iterate(S.indices, s...) # For OneTo, the values and indices of the values are identical, so this may be defined in Base. # In general such an indexing operation would produce offset ranges getindex(S::OneTo, I::IdentityUnitRange{<:AbstractUnitRange{<:Integer}}) = (@inline; @boundscheck checkbounds(S, I); I) """ LinearIndices(A::AbstractArray) Return a `LinearIndices` array with the same shape and [`axes`](@ref) as `A`, holding the linear index of each entry in `A`. Indexing this array with cartesian indices allows mapping them to linear indices. For arrays with conventional indexing (indices start at 1), or any multidimensional array, linear indices range from 1 to `length(A)`. However, for `AbstractVector`s linear indices are `axes(A, 1)`, and therefore do not start at 1 for vectors with unconventional indexing. Calling this function is the "safe" way to write algorithms that exploit linear indexing. # Examples ```jldoctest julia> A = fill(1, (5,6,7)); julia> b = LinearIndices(A); julia> extrema(b) (1, 210) ``` LinearIndices(inds::CartesianIndices) -> R LinearIndices(sz::Dims) -> R LinearIndices((istart:istop, jstart:jstop, ...)) -> R Return a `LinearIndices` array with the specified shape or [`axes`](@ref). # Example The main purpose of this constructor is intuitive conversion from cartesian to linear indexing: ```jldoctest julia> linear = LinearIndices((1:3, 1:2)) 3ร—2 LinearIndices{2, Tuple{UnitRange{Int64}, UnitRange{Int64}}}: 1 4 2 5 3 6 julia> linear[1,2] 4 ``` """ struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} indices::R end convert(::Type{LinearIndices{N,R}}, inds::LinearIndices{N}) where {N,R<:NTuple{N,AbstractUnitRange{Int}}} = LinearIndices{N,R}(convert(R, inds.indices))::LinearIndices{N,R} LinearIndices(::Tuple{}) = LinearIndices{0,typeof(())}(()) LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = LinearIndices(map(r->convert(AbstractUnitRange{Int}, r), inds)) LinearIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = LinearIndices(map(_convert2ind, inds)) LinearIndices(A::Union{AbstractArray,SimpleVector}) = LinearIndices(axes(A)) _convert2ind(i::Integer) = Base.OneTo(i) _convert2ind(ind::AbstractUnitRange) = first(ind):last(ind) function indices_promote_type(::Type{Tuple{R1,Vararg{R1,N}}}, ::Type{Tuple{R2,Vararg{R2,N}}}) where {R1,R2,N} R = promote_type(R1, R2) return Tuple{R, Vararg{R, N}} end promote_rule(::Type{LinearIndices{N,R1}}, ::Type{LinearIndices{N,R2}}) where {N,R1,R2} = LinearIndices{N,indices_promote_type(R1,R2)} promote_rule(a::Type{Slice{T1}}, b::Type{Slice{T2}}) where {T1,T2} = el_same(promote_type(T1, T2), a, b) promote_rule(a::Type{IdentityUnitRange{T1}}, b::Type{IdentityUnitRange{T2}}) where {T1,T2} = el_same(promote_type(T1, T2), a, b) # AbstractArray implementation IndexStyle(::Type{<:LinearIndices}) = IndexLinear() axes(iter::LinearIndices) = map(axes1, iter.indices) size(iter::LinearIndices) = map(length, iter.indices) isassigned(iter::LinearIndices, i::Int) = checkbounds(Bool, iter, i) function getindex(iter::LinearIndices, i::Int) @inline @boundscheck checkbounds(iter, i) i end function getindex(iter::LinearIndices, i::AbstractRange{<:Integer}) @inline @boundscheck checkbounds(iter, i) @inbounds isa(iter, LinearIndices{1}) ? iter.indices[1][i] : (first(iter):last(iter))[i] end # More efficient iteration โ€” predominantly for non-vector LinearIndices # but one-dimensional LinearIndices must be special-cased to support OffsetArrays iterate(iter::LinearIndices{1}, s...) = iterate(axes1(iter.indices[1]), s...) iterate(iter::LinearIndices, i=1) = i > length(iter) ? nothing : (i, i+1) # Needed since firstindex and lastindex are defined in terms of LinearIndices first(iter::LinearIndices) = 1 first(iter::LinearIndices{1}) = (@inline; first(axes1(iter.indices[1]))) last(iter::LinearIndices) = (@inline; length(iter)) last(iter::LinearIndices{1}) = (@inline; last(axes1(iter.indices[1]))) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/array.jl๏# This file is a part of Julia. License is MIT: https://julialang.org/license ## array.jl: Dense arrays """ DimensionMismatch([msg]) The objects called do not have matching dimensionality. Optional argument `msg` is a descriptive error string. """ struct DimensionMismatch <: Exception msg::AbstractString end DimensionMismatch() = DimensionMismatch("") ## Type aliases for convenience ## """ AbstractVector{T} Supertype for one-dimensional arrays (or array-like types) with elements of type `T`. Alias for [`AbstractArray{T,1}`](@ref). """ const AbstractVector{T} = AbstractArray{T,1} """ AbstractMatrix{T} Supertype for two-dimensional arrays (or array-like types) with elements of type `T`. Alias for [`AbstractArray{T,2}`](@ref). """ const AbstractMatrix{T} = AbstractArray{T,2} """ AbstractVecOrMat{T} Union type of [`AbstractVector{T}`](@ref) and [`AbstractMatrix{T}`](@ref). """ const AbstractVecOrMat{T} = Union{AbstractVector{T}, AbstractMatrix{T}} const RangeIndex = Union{<:BitInteger, AbstractRange{<:BitInteger}} const DimOrInd = Union{Integer, AbstractUnitRange} const IntOrInd = Union{Int, AbstractUnitRange} const DimsOrInds{N} = NTuple{N,DimOrInd} const NeedsShaping = Union{Tuple{Integer,Vararg{Integer}}, Tuple{OneTo,Vararg{OneTo}}} """ Array{T,N} <: AbstractArray{T,N} `N`-dimensional dense array with elements of type `T`. """ Array """ Vector{T} <: AbstractVector{T} One-dimensional dense array with elements of type `T`, often used to represent a mathematical vector. Alias for [`Array{T,1}`](@ref). See also [`empty`](@ref), [`similar`](@ref) and [`zero`](@ref) for creating vectors. """ const Vector{T} = Array{T,1} """ Matrix{T} <: AbstractMatrix{T} Two-dimensional dense array with elements of type `T`, often used to represent a mathematical matrix. Alias for [`Array{T,2}`](@ref). See also [`fill`](@ref), [`zeros`](@ref), [`undef`](@ref) and [`similar`](@ref) for creating matrices. """ const Matrix{T} = Array{T,2} """ VecOrMat{T} Union type of [`Vector{T}`](@ref) and [`Matrix{T}`](@ref) which allows functions to accept either a Matrix or a Vector. # Examples ```jldoctest julia> Vector{Float64} <: VecOrMat{Float64} true julia> Matrix{Float64} <: VecOrMat{Float64} true julia> Array{Float64, 3} <: VecOrMat{Float64} false ``` """ const VecOrMat{T} = Union{Vector{T}, Matrix{T}} """ DenseArray{T, N} <: AbstractArray{T,N} `N`-dimensional dense array with elements of type `T`. The elements of a dense array are stored contiguously in memory. """ DenseArray """ DenseVector{T} One-dimensional [`DenseArray`](@ref) with elements of type `T`. Alias for `DenseArray{T,1}`. """ const DenseVector{T} = DenseArray{T,1} """ DenseMatrix{T} Two-dimensional [`DenseArray`](@ref) with elements of type `T`. Alias for `DenseArray{T,2}`. """ const DenseMatrix{T} = DenseArray{T,2} """ DenseVecOrMat{T} Union type of [`DenseVector{T}`](@ref) and [`DenseMatrix{T}`](@ref). """ const DenseVecOrMat{T} = Union{DenseVector{T}, DenseMatrix{T}} ## Basic functions ## using Core: arraysize, arrayset, const_arrayref """ @_safeindex This internal macro converts: - `getindex(xs::Tuple, )` -> `__inbounds_getindex(args...)` - `setindex!(xs::Vector, args...)` -> `__inbounds_setindex!(xs, args...)` to tell the compiler that indexing operations within the applied expression are always inbounds and do not need to taint `:consistent` and `:nothrow`. """ macro _safeindex(ex) return esc(_safeindex(__module__, ex)) end function _safeindex(__module__, ex) isa(ex, Expr) || return ex if ex.head === :(=) lhs = arrayref(true, ex.args, 1) if isa(lhs, Expr) && lhs.head === :ref # xs[i] = x rhs = arrayref(true, ex.args, 2) xs = arrayref(true, lhs.args, 1) args = Vector{Any}(undef, length(lhs.args)-1) for i = 2:length(lhs.args) arrayset(true, args, _safeindex(__module__, arrayref(true, lhs.args, i)), i-1) end return Expr(:call, GlobalRef(__module__, :__inbounds_setindex!), xs, _safeindex(__module__, rhs), args...) end elseif ex.head === :ref # xs[i] return Expr(:call, GlobalRef(__module__, :__inbounds_getindex), ex.args...) end args = Vector{Any}(undef, length(ex.args)) for i = 1:length(ex.args) arrayset(true, args, _safeindex(__module__, arrayref(true, ex.args, i)), i) end return Expr(ex.head, args...) end vect() = Vector{Any}() function vect(X::T...) where T @_terminates_locally_meta vec = Vector{T}(undef, length(X)) @_safeindex for i = 1:length(X) vec[i] = X[i] end return vec end """ vect(X...) Create a [`Vector`](@ref) with element type computed from the `promote_typeof` of the argument, containing the argument list. # Examples ```jldoctest julia> a = Base.vect(UInt8(1), 2.5, 1//2) 3-element Vector{Float64}: 1.0 2.5 0.5 ``` """ function vect(X...) T = promote_typeof(X...) return T[X...] end size(a::Array, d::Integer) = arraysize(a, d isa Int ? d : convert(Int, d)) size(a::Vector) = (arraysize(a,1),) size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) size(a::Array{<:Any,N}) where {N} = (@inline; ntuple(M -> size(a, M), Val(N))::Dims) asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) allocatedinline(@nospecialize T::Type) = (@_total_meta; ccall(:jl_stored_inline, Cint, (Any,), T) != Cint(0)) """ Base.isbitsunion(::Type{T}) Return whether a type is an "is-bits" Union type, meaning each type included in a Union is [`isbitstype`](@ref). # Examples ```jldoctest julia> Base.isbitsunion(Union{Float64, UInt8}) true julia> Base.isbitsunion(Union{Float64, String}) false ``` """ isbitsunion(u::Union) = allocatedinline(u) isbitsunion(x) = false function _unsetindex!(A::Array{T}, i::Int) where {T} @inline @boundscheck checkbounds(A, i) t = @_gc_preserve_begin A p = Ptr{Ptr{Cvoid}}(pointer(A, i)) if !allocatedinline(T) Intrinsics.atomic_pointerset(p, C_NULL, :monotonic) elseif T isa DataType if !datatype_pointerfree(T) for j = 1:Core.sizeof(Ptr{Cvoid}):Core.sizeof(T) Intrinsics.atomic_pointerset(p + j - 1, C_NULL, :monotonic) end end end @_gc_preserve_end t return A end """ Base.bitsunionsize(U::Union) -> Int For a `Union` of [`isbitstype`](@ref) types, return the size of the largest type; assumes `Base.isbitsunion(U) == true`. # Examples ```jldoctest julia> Base.bitsunionsize(Union{Float64, UInt8}) 8 julia> Base.bitsunionsize(Union{Float64, UInt8, Int128}) 16 ``` """ function bitsunionsize(u::Union) isinline, sz, _ = uniontype_layout(u) @assert isinline return sz end elsize(@nospecialize _::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T) function elsize(::Type{Ptr{T}}) where T # this only must return something valid for values which satisfy is_valid_intrinsic_elptr(T), # which includes Any and most concrete datatypes T === Any && return sizeof(Ptr{Any}) T isa DataType || sizeof(Any) # throws return LLT_ALIGN(Core.sizeof(T), datatype_alignment(T)) end elsize(::Type{Union{}}, slurp...) = 0 sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) @inline @boundscheck checkbounds(Bool, a, i...) || return false ii = (_sub2ind(size(a), i...) % UInt) - 1 ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1 end ## copy ## """ unsafe_copyto!(dest::Ptr{T}, src::Ptr{T}, N) Copy `N` elements from a source pointer to a destination, with no checking. The size of an element is determined by the type of the pointers. The `unsafe` prefix on this function indicates that no validation is performed on the pointers `dest` and `src` to ensure that they are valid. Incorrect usage may corrupt or segfault your program, in the same manner as C. """ function unsafe_copyto!(dest::Ptr{T}, src::Ptr{T}, n) where T # Do not use this to copy data between pointer arrays. # It can't be made safe no matter how carefully you checked. memmove(dest, src, n * aligned_sizeof(T)) return dest end function _unsafe_copyto!(dest, doffs, src, soffs, n) destp = pointer(dest, doffs) srcp = pointer(src, soffs) @inbounds if destp < srcp || destp > srcp + n for i = 1:n if isassigned(src, soffs + i - 1) dest[doffs + i - 1] = src[soffs + i - 1] else _unsetindex!(dest, doffs + i - 1) end end else for i = n:-1:1 if isassigned(src, soffs + i - 1) dest[doffs + i - 1] = src[soffs + i - 1] else _unsetindex!(dest, doffs + i - 1) end end end return dest end """ unsafe_copyto!(dest::Array, do, src::Array, so, N) Copy `N` elements from a source array to a destination, starting at the linear index `so` in the source and `do` in the destination (1-indexed). The `unsafe` prefix on this function indicates that no validation is performed to ensure that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in the same manner as C. $(_DOCS_ALIASING_WARNING) """ function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T t1 = @_gc_preserve_begin dest t2 = @_gc_preserve_begin src destp = pointer(dest, doffs) srcp = pointer(src, soffs) if !allocatedinline(T) ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), dest, destp, src, srcp, n) elseif isbitstype(T) memmove(destp, srcp, n * aligned_sizeof(T)) elseif isbitsunion(T) memmove(destp, srcp, n * aligned_sizeof(T)) # copy selector bytes memmove( ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), dest) + doffs - 1, ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1, n) else _unsafe_copyto!(dest, doffs, src, soffs, n) end @_gc_preserve_end t2 @_gc_preserve_end t1 return dest end unsafe_copyto!(dest::Array, doffs, src::Array, soffs, n) = _unsafe_copyto!(dest, doffs, src, soffs, n) """ copyto!(dest, do, src, so, N) Copy `N` elements from collection `src` starting at the linear index `so`, to array `dest` starting at the index `do`. Return `dest`. """ function copyto!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) return _copyto_impl!(dest, doffs, src, soffs, n) end # this is only needed to avoid possible ambiguities with methods added in some packages function copyto!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T return _copyto_impl!(dest, doffs, src, soffs, n) end function _copyto_impl!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) n == 0 && return dest n > 0 || _throw_argerror("Number of elements to copy must be nonnegative.") @boundscheck checkbounds(dest, doffs:doffs+n-1) @boundscheck checkbounds(src, soffs:soffs+n-1) unsafe_copyto!(dest, doffs, src, soffs, n) return dest end # Outlining this because otherwise a catastrophic inference slowdown # occurs, see discussion in #27874. # It is also mitigated by using a constant string. _throw_argerror(s) = (@noinline; throw(ArgumentError(s))) copyto!(dest::Array, src::Array) = copyto!(dest, 1, src, 1, length(src)) # also to avoid ambiguities in packages copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, length(src)) # N.B: The generic definition in multidimensional.jl covers, this, this is just here # for bootstrapping purposes. function fill!(dest::Array{T}, x) where T xT = x isa T ? x : convert(T, x)::T for i in eachindex(dest) @inbounds dest[i] = xT end return dest end """ copy(x) Create a shallow copy of `x`: the outer structure is copied, but not all internal values. For example, copying an array produces a new array with identically-same elements as the original. See also [`copy!`](@ref Base.copy!), [`copyto!`](@ref), [`deepcopy`](@ref). """ copy copy(a::T) where {T<:Array} = ccall(:jl_array_copy, Ref{T}, (Any,), a) ## Constructors ## similar(a::Array{T,1}) where {T} = Vector{T}(undef, size(a,1)) similar(a::Array{T,2}) where {T} = Matrix{T}(undef, size(a,1), size(a,2)) similar(a::Array{T,1}, S::Type) where {T} = Vector{S}(undef, size(a,1)) similar(a::Array{T,2}, S::Type) where {T} = Matrix{S}(undef, size(a,1), size(a,2)) similar(a::Array{T}, m::Int) where {T} = Vector{T}(undef, m) similar(a::Array, T::Type, dims::Dims{N}) where {N} = Array{T,N}(undef, dims) similar(a::Array{T}, dims::Dims{N}) where {T,N} = Array{T,N}(undef, dims) # T[x...] constructs Array{T,1} """ getindex(type[, elements...]) Construct a 1-d array of the specified type. This is usually called with the syntax `Type[]`. Element values can be specified using `Type[a,b,c,...]`. # Examples ```jldoctest julia> Int8[1, 2, 3] 3-element Vector{Int8}: 1 2 3 julia> getindex(Int8, 1, 2, 3) 3-element Vector{Int8}: 1 2 3 ``` """ function getindex(::Type{T}, vals...) where T @inline @_effect_free_terminates_locally_meta a = Vector{T}(undef, length(vals)) if vals isa NTuple @_safeindex for i in 1:length(vals) a[i] = vals[i] end else # use afoldl to avoid type instability inside loop afoldl(1, vals...) do i, v @inbounds a[i] = v return i + 1 end end return a end function getindex(::Type{Any}, @nospecialize vals...) @_effect_free_terminates_locally_meta a = Vector{Any}(undef, length(vals)) @_safeindex for i = 1:length(vals) a[i] = vals[i] end return a end getindex(::Type{Any}) = Vector{Any}() function fill!(a::Union{Array{UInt8}, Array{Int8}}, x::Integer) t = @_gc_preserve_begin a p = unsafe_convert(Ptr{Cvoid}, a) memset(p, x isa eltype(a) ? x : convert(eltype(a), x), length(a)) @_gc_preserve_end t return a end to_dim(d::Integer) = d to_dim(d::OneTo) = last(d) """ fill(value, dims::Tuple) fill(value, dims...) Create an array of size `dims` with every location set to `value`. For example, `fill(1.0, (5,5))` returns a 5ร—5 array of floats, with `1.0` in every location of the array. The dimension lengths `dims` may be specified as either a tuple or a sequence of arguments. An `N`-length tuple or `N` arguments following the `value` specify an `N`-dimensional array. Thus, a common idiom for creating a zero-dimensional array with its only location set to `x` is `fill(x)`. Every location of the returned array is set to (and is thus [`===`](@ref) to) the `value` that was passed; this means that if the `value` is itself modified, all elements of the `fill`ed array will reflect that modification because they're _still_ that very `value`. This is of no concern with `fill(1.0, (5,5))` as the `value` `1.0` is immutable and cannot itself be modified, but can be unexpected with mutable values like โ€” most commonly โ€” arrays. For example, `fill([], 3)` places _the very same_ empty array in all three locations of the returned vector: ```jldoctest julia> v = fill([], 3) 3-element Vector{Vector{Any}}: [] [] [] julia> v[1] === v[2] === v[3] true julia> value = v[1] Any[] julia> push!(value, 867_5309) 1-element Vector{Any}: 8675309 julia> v 3-element Vector{Vector{Any}}: [8675309] [8675309] [8675309] ``` To create an array of many independent inner arrays, use a [comprehension](@ref man-comprehensions) instead. This creates a new and distinct array on each iteration of the loop: ```jldoctest julia> v2 = [[] for _ in 1:3] 3-element Vector{Vector{Any}}: [] [] [] julia> v2[1] === v2[2] === v2[3] false julia> push!(v2[1], 8675309) 1-element Vector{Any}: 8675309 julia> v2 3-element Vector{Vector{Any}}: [8675309] [] [] ``` See also: [`fill!`](@ref), [`zeros`](@ref), [`ones`](@ref), [`similar`](@ref). # Examples ```jldoctest julia> fill(1.0, (2,3)) 2ร—3 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 julia> fill(42) 0-dimensional Array{Int64, 0}: 42 julia> A = fill(zeros(2), 2) # sets both elements to the same [0.0, 0.0] vector 2-element Vector{Vector{Float64}}: [0.0, 0.0] [0.0, 0.0] julia> A[1][1] = 42; # modifies the filled value to be [42.0, 0.0] julia> A # both A[1] and A[2] are the very same vector 2-element Vector{Vector{Float64}}: [42.0, 0.0] [42.0, 0.0] ``` """ function fill end fill(v, dims::DimOrInd...) = fill(v, dims) fill(v, dims::NTuple{N, Union{Integer, OneTo}}) where {N} = fill(v, map(to_dim, dims)) fill(v, dims::NTuple{N, Integer}) where {N} = (a=Array{typeof(v),N}(undef, dims); fill!(a, v); a) fill(v, dims::Tuple{}) = (a=Array{typeof(v),0}(undef, dims); fill!(a, v); a) """ zeros([T=Float64,] dims::Tuple) zeros([T=Float64,] dims...) Create an `Array`, with element type `T`, of all zeros with size specified by `dims`. See also [`fill`](@ref), [`ones`](@ref), [`zero`](@ref). # Examples ```jldoctest julia> zeros(1) 1-element Vector{Float64}: 0.0 julia> zeros(Int8, 2, 3) 2ร—3 Matrix{Int8}: 0 0 0 0 0 0 ``` """ function zeros end """ ones([T=Float64,] dims::Tuple) ones([T=Float64,] dims...) Create an `Array`, with element type `T`, of all ones with size specified by `dims`. See also [`fill`](@ref), [`zeros`](@ref). # Examples ```jldoctest julia> ones(1,2) 1ร—2 Matrix{Float64}: 1.0 1.0 julia> ones(ComplexF64, 2, 3) 2ร—3 Matrix{ComplexF64}: 1.0+0.0im 1.0+0.0im 1.0+0.0im 1.0+0.0im 1.0+0.0im 1.0+0.0im ``` """ function ones end for (fname, felt) in ((:zeros, :zero), (:ones, :one)) @eval begin $fname(dims::DimOrInd...) = $fname(dims) $fname(::Type{T}, dims::DimOrInd...) where {T} = $fname(T, dims) $fname(dims::Tuple{Vararg{DimOrInd}}) = $fname(Float64, dims) $fname(::Type{T}, dims::NTuple{N, Union{Integer, OneTo}}) where {T,N} = $fname(T, map(to_dim, dims)) function $fname(::Type{T}, dims::NTuple{N, Integer}) where {T,N} a = Array{T,N}(undef, dims) fill!(a, $felt(T)) return a end function $fname(::Type{T}, dims::Tuple{}) where {T} a = Array{T}(undef) fill!(a, $felt(T)) return a end end end function _one(unit::T, x::AbstractMatrix) where T require_one_based_indexing(x) m,n = size(x) m==n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) # Matrix{T}(I, m, m) I = zeros(T, m, m) for i in 1:m I[i,i] = unit end I end one(x::AbstractMatrix{T}) where {T} = _one(one(T), x) oneunit(x::AbstractMatrix{T}) where {T} = _one(oneunit(T), x) ## Conversions ## convert(::Type{T}, a::AbstractArray) where {T<:Array} = a isa T ? a : T(a)::T promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b) ## Constructors ## if nameof(@__MODULE__) === :Base # avoid method overwrite # constructors should make copies Array{T,N}(x::AbstractArray{S,N}) where {T,N,S} = copyto_axcheck!(Array{T,N}(undef, size(x)), x) AbstractArray{T,N}(A::AbstractArray{S,N}) where {T,N,S} = copyto_axcheck!(similar(A,T), A) end ## copying iterators to containers """ collect(element_type, collection) Return an `Array` with the given element type of all items in a collection or iterable. The result has the same shape and number of dimensions as `collection`. # Examples ```jldoctest julia> collect(Float64, 1:2:5) 3-element Vector{Float64}: 1.0 3.0 5.0 ``` """ collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr)) _collect(::Type{T}, itr, isz::Union{HasLength,HasShape}) where {T} = copyto!(_array_for(T, isz, _similar_shape(itr, isz)), itr) function _collect(::Type{T}, itr, isz::SizeUnknown) where T a = Vector{T}() for x in itr push!(a, x) end return a end # make a collection similar to `c` and appropriate for collecting `itr` _similar_for(c, ::Type{T}, itr, isz, shp) where {T} = similar(c, T) _similar_shape(itr, ::SizeUnknown) = nothing _similar_shape(itr, ::HasLength) = length(itr)::Integer _similar_shape(itr, ::HasShape) = axes(itr) _similar_for(c::AbstractArray, ::Type{T}, itr, ::SizeUnknown, ::Nothing) where {T} = similar(c, T, 0) _similar_for(c::AbstractArray, ::Type{T}, itr, ::HasLength, len::Integer) where {T} = similar(c, T, len) _similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape, axs) where {T} = similar(c, T, axs) # make a collection appropriate for collecting `itr::Generator` _array_for(::Type{T}, ::SizeUnknown, ::Nothing) where {T} = Vector{T}(undef, 0) _array_for(::Type{T}, ::HasLength, len::Integer) where {T} = Vector{T}(undef, Int(len)) _array_for(::Type{T}, ::HasShape{N}, axs) where {T,N} = similar(Array{T,N}, axs) # used by syntax lowering for simple typed comprehensions _array_for(::Type{T}, itr, isz) where {T} = _array_for(T, isz, _similar_shape(itr, isz)) """ collect(collection) Return an `Array` of all items in a collection or iterator. For dictionaries, returns `Vector{Pair{KeyType, ValType}}`. If the argument is array-like or is an iterator with the [`HasShape`](@ref IteratorSize) trait, the result will have the same shape and number of dimensions as the argument. Used by comprehensions to turn a generator into an `Array`. # Examples ```jldoctest julia> collect(1:2:13) 7-element Vector{Int64}: 1 3 5 7 9 11 13 julia> [x^2 for x in 1:8 if isodd(x)] 4-element Vector{Int64}: 1 9 25 49 ``` """ collect(itr) = _collect(1:1 #= Array =#, itr, IteratorEltype(itr), IteratorSize(itr)) collect(A::AbstractArray) = _collect_indices(axes(A), A) collect_similar(cont, itr) = _collect(cont, itr, IteratorEltype(itr), IteratorSize(itr)) _collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) = copyto!(_similar_for(cont, eltype(itr), itr, isz, _similar_shape(itr, isz)), itr) function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) a = _similar_for(cont, eltype(itr), itr, isz, nothing) for x in itr push!(a,x) end return a end _collect_indices(::Tuple{}, A) = copyto!(Array{eltype(A),0}(undef), A) _collect_indices(indsA::Tuple{Vararg{OneTo}}, A) = copyto!(Array{eltype(A)}(undef, length.(indsA)), A) function _collect_indices(indsA, A) B = Array{eltype(A)}(undef, length.(indsA)) copyto!(B, CartesianIndices(axes(B)), A, CartesianIndices(indsA)) end # NOTE: this function is not meant to be called, only inferred, for the # purpose of bounding the types of values generated by an iterator. function _iterator_upper_bound(itr) x = iterate(itr) while x !== nothing val = getfield(x, 1) if inferencebarrier(nothing) return val end x = iterate(itr, getfield(x, 2)) end throw(nothing) end # define this as a macro so that the call to Core.Compiler # gets inlined into the caller before recursion detection # gets a chance to see it, so that recursive calls to the caller # don't trigger the inference limiter if isdefined(Core, :Compiler) macro default_eltype(itr) I = esc(itr) return quote if $I isa Generator && ($I).f isa Type T = ($I).f else T = Core.Compiler.return_type(_iterator_upper_bound, Tuple{typeof($I)}) end promote_typejoin_union(T) end end else macro default_eltype(itr) I = esc(itr) return quote if $I isa Generator && ($I).f isa Type promote_typejoin_union($I.f) else Any end end end end function collect(itr::Generator) isz = IteratorSize(itr.iter) et = @default_eltype(itr) if isa(isz, SizeUnknown) return grow_to!(Vector{et}(), itr) else shp = _similar_shape(itr, isz) y = iterate(itr) if y === nothing return _array_for(et, isz, shp) end v1, st = y dest = _array_for(typeof(v1), isz, shp) # The typeassert gives inference a helping hand on the element type and dimensionality # (work-around for #28382) etโ€ฒ = et <: Type ? Type : et RT = dest isa AbstractArray ? AbstractArray{<:etโ€ฒ, ndims(dest)} : Any collect_to_with_first!(dest, v1, itr, st)::RT end end _collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = grow_to!(_similar_for(c, @default_eltype(itr), itr, isz, nothing), itr) function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) et = @default_eltype(itr) shp = _similar_shape(itr, isz) y = iterate(itr) if y === nothing return _similar_for(c, et, itr, isz, shp) end v1, st = y dest = _similar_for(c, typeof(v1), itr, isz, shp) # The typeassert gives inference a helping hand on the element type and dimensionality # (work-around for #28382) etโ€ฒ = et <: Type ? Type : et RT = dest isa AbstractArray ? AbstractArray{<:etโ€ฒ, ndims(dest)} : Any collect_to_with_first!(dest, v1, itr, st)::RT end function collect_to_with_first!(dest::AbstractArray, v1, itr, st) i1 = first(LinearIndices(dest)) dest[i1] = v1 return collect_to!(dest, itr, i1+1, st) end function collect_to_with_first!(dest, v1, itr, st) push!(dest, v1) return grow_to!(dest, itr, st) end function setindex_widen_up_to(dest::AbstractArray{T}, el, i) where T @inline new = similar(dest, promote_typejoin(T, typeof(el))) f = first(LinearIndices(dest)) copyto!(new, first(LinearIndices(new)), dest, f, i-f) @inbounds new[i] = el return new end function collect_to!(dest::AbstractArray{T}, itr, offs, st) where T # collect to dest array, checking the type of each result. if a result does not # match, widen the result type and re-dispatch. i = offs while true y = iterate(itr, st) y === nothing && break el, st = y if el isa T @inbounds dest[i] = el i += 1 else new = setindex_widen_up_to(dest, el, i) return collect_to!(new, itr, i+1, st) end end return dest end function grow_to!(dest, itr) y = iterate(itr) y === nothing && return dest dest2 = empty(dest, typeof(y[1])) push!(dest2, y[1]) grow_to!(dest2, itr, y[2]) end function push_widen(dest, el) @inline new = sizehint!(empty(dest, promote_typejoin(eltype(dest), typeof(el))), length(dest)) if new isa AbstractSet # TODO: merge back these two branches when copy! is re-enabled for sets/vectors union!(new, dest) else append!(new, dest) end push!(new, el) return new end function grow_to!(dest, itr, st) T = eltype(dest) y = iterate(itr, st) while y !== nothing el, st = y if el isa T push!(dest, el) else new = push_widen(dest, el) return grow_to!(new, itr, st) end y = iterate(itr, st) end return dest end ## Iteration ## iterate(A::Array, i=1) = (@inline; (i % UInt) - 1 < length(A) ? (@inbounds A[i], i + 1) : nothing) ## Indexing: getindex ## """ getindex(collection, key...) Retrieve the value(s) stored at the given key or index within a collection. The syntax `a[i,j,...]` is converted by the compiler to `getindex(a, i, j, ...)`. See also [`get`](@ref), [`keys`](@ref), [`eachindex`](@ref). # Examples ```jldoctest julia> A = Dict("a" => 1, "b" => 2) Dict{String, Int64} with 2 entries: "b" => 2 "a" => 1 julia> getindex(A, "a") 1 ``` """ function getindex end # Faster contiguous indexing using copyto! for AbstractUnitRange and Colon function getindex(A::Array, I::AbstractUnitRange{<:Integer}) @inline @boundscheck checkbounds(A, I) lI = length(I) X = similar(A, axes(I)) if lI > 0 copyto!(X, firstindex(X), A, first(I), lI) end return X end # getindex for carrying out logical indexing for AbstractUnitRange{Bool} as Bool <: Integer getindex(a::Array, r::AbstractUnitRange{Bool}) = getindex(a, to_index(r)) function getindex(A::Array, c::Colon) lI = length(A) X = similar(A, lI) if lI > 0 unsafe_copyto!(X, 1, A, 1, lI) end return X end # This is redundant with the abstract fallbacks, but needed for bootstrap function getindex(A::Array{S}, I::AbstractRange{Int}) where S return S[ A[i] for i in I ] end ## Indexing: setindex! ## """ setindex!(collection, value, key...) Store the given value at the given key or index within a collection. The syntax `a[i,j,...] = x` is converted by the compiler to `(setindex!(a, x, i, j, ...); x)`. # Examples ```jldoctest julia> a = Dict("a"=>1) Dict{String, Int64} with 1 entry: "a" => 1 julia> setindex!(a, 2, "b") Dict{String, Int64} with 2 entries: "b" => 2 "a" => 1 ``` """ function setindex! end @eval setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset($(Expr(:boundscheck)), A, x isa T ? x : convert(T,x)::T, i1) @eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@inline; arrayset($(Expr(:boundscheck)), A, x isa T ? x : convert(T,x)::T, i1, i2, I...)) __inbounds_setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset(false, A, convert(T,x)::T, i1) __inbounds_setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@inline; arrayset(false, A, convert(T,x)::T, i1, i2, I...)) # This is redundant with the abstract fallbacks but needed and helpful for bootstrap function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) @_propagate_inbounds_meta @boundscheck setindex_shape_check(X, length(I)) require_one_based_indexing(X) Xโ€ฒ = unalias(A, X) Iโ€ฒ = unalias(A, I) count = 1 for i in Iโ€ฒ @inbounds x = Xโ€ฒ[count] A[i] = x count += 1 end return A end # Faster contiguous setindex! with copyto! function setindex!(A::Array{T}, X::Array{T}, I::AbstractUnitRange{Int}) where T @inline @boundscheck checkbounds(A, I) lI = length(I) @boundscheck setindex_shape_check(X, lI) if lI > 0 unsafe_copyto!(A, first(I), X, 1, lI) end return A end function setindex!(A::Array{T}, X::Array{T}, c::Colon) where T @inline lI = length(A) @boundscheck setindex_shape_check(X, lI) if lI > 0 unsafe_copyto!(A, 1, X, 1, lI) end return A end # efficiently grow an array _growbeg!(a::Vector, delta::Integer) = ccall(:jl_array_grow_beg, Cvoid, (Any, UInt), a, delta) _growend!(a::Vector, delta::Integer) = ccall(:jl_array_grow_end, Cvoid, (Any, UInt), a, delta) _growat!(a::Vector, i::Integer, delta::Integer) = ccall(:jl_array_grow_at, Cvoid, (Any, Int, UInt), a, i - 1, delta) # efficiently delete part of an array _deletebeg!(a::Vector, delta::Integer) = ccall(:jl_array_del_beg, Cvoid, (Any, UInt), a, delta) _deleteend!(a::Vector, delta::Integer) = ccall(:jl_array_del_end, Cvoid, (Any, UInt), a, delta) _deleteat!(a::Vector, i::Integer, delta::Integer) = ccall(:jl_array_del_at, Cvoid, (Any, Int, UInt), a, i - 1, delta) ## Dequeue functionality ## """ push!(collection, items...) -> collection Insert one or more `items` in `collection`. If `collection` is an ordered container, the items are inserted at the end (in the given order). # Examples ```jldoctest julia> push!([1, 2, 3], 4, 5, 6) 6-element Vector{Int64}: 1 2 3 4 5 6 ``` If `collection` is ordered, use [`append!`](@ref) to add all the elements of another collection to it. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, 5, 6])`. For `AbstractSet` objects, [`union!`](@ref) can be used instead. See [`sizehint!`](@ref) for notes about the performance model. See also [`pushfirst!`](@ref). """ function push! end function push!(a::Vector{T}, item) where T # convert first so we don't grow the array if the assignment won't work itemT = item isa T ? item : convert(T, item)::T _growend!(a, 1) @_safeindex a[length(a)] = itemT return a end # specialize and optimize the single argument case function push!(a::Vector{Any}, @nospecialize x) _growend!(a, 1) @_safeindex a[length(a)] = x return a end function push!(a::Vector{Any}, @nospecialize x...) @_terminates_locally_meta na = length(a) nx = length(x) _growend!(a, nx) @_safeindex for i = 1:nx a[na+i] = x[i] end return a end """ append!(collection, collections...) -> collection. For an ordered container `collection`, add the elements of each `collections` to the end of it. !!! compat "Julia 1.6" Specifying multiple collections to be appended requires at least Julia 1.6. # Examples ```jldoctest julia> append!([1], [2, 3]) 3-element Vector{Int64}: 1 2 3 julia> append!([1, 2, 3], [4, 5], [6]) 6-element Vector{Int64}: 1 2 3 4 5 6 ``` Use [`push!`](@ref) to add individual items to `collection` which are not already themselves in another collection. The result of the preceding example is equivalent to `push!([1, 2, 3], 4, 5, 6)`. See [`sizehint!`](@ref) for notes about the performance model. See also [`vcat`](@ref) for vectors, [`union!`](@ref) for sets, and [`prepend!`](@ref) and [`pushfirst!`](@ref) for the opposite order. """ function append! end function append!(a::Vector, items::AbstractVector) itemindices = eachindex(items) n = length(itemindices) _growend!(a, n) copyto!(a, length(a)-n+1, items, first(itemindices), n) return a end append!(a::AbstractVector, iter) = _append!(a, IteratorSize(iter), iter) push!(a::AbstractVector, iter...) = append!(a, iter) append!(a::AbstractVector, iter...) = foldl(append!, iter, init=a) function _append!(a::AbstractVector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta n = length(a) i = lastindex(a) resize!(a, n+Int(length(iter))::Int) for (i, item) in zip(i+1:lastindex(a), iter) if isa(a, Vector) # give better effects for builtin vectors @_safeindex a[i] = item else a[i] = item end end a end function _append!(a::AbstractVector, ::IteratorSize, iter) for item in iter push!(a, item) end a end """ prepend!(a::Vector, collections...) -> collection Insert the elements of each `collections` to the beginning of `a`. When `collections` specifies multiple collections, order is maintained: elements of `collections[1]` will appear leftmost in `a`, and so on. !!! compat "Julia 1.6" Specifying multiple collections to be prepended requires at least Julia 1.6. # Examples ```jldoctest julia> prepend!([3], [1, 2]) 3-element Vector{Int64}: 1 2 3 julia> prepend!([6], [1, 2], [3, 4, 5]) 6-element Vector{Int64}: 1 2 3 4 5 6 ``` """ function prepend! end function prepend!(a::Vector, items::AbstractVector) itemindices = eachindex(items) n = length(itemindices) _growbeg!(a, n) if a === items copyto!(a, 1, items, n+1, n) else copyto!(a, 1, items, first(itemindices), n) end return a end prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter) pushfirst!(a::Vector, iter...) = prepend!(a, iter) prepend!(a::AbstractVector, iter...) = foldr((v, a) -> prepend!(a, v), iter, init=a) function _prepend!(a::Vector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta require_one_based_indexing(a) n = length(iter) _growbeg!(a, n) i = 0 for item in iter @_safeindex a[i += 1] = item end a end function _prepend!(a::Vector, ::IteratorSize, iter) n = 0 for item in iter n += 1 pushfirst!(a, item) end reverse!(a, 1, n) a end """ resize!(a::Vector, n::Integer) -> Vector Resize `a` to contain `n` elements. If `n` is smaller than the current collection length, the first `n` elements will be retained. If `n` is larger, the new elements are not guaranteed to be initialized. # Examples ```jldoctest julia> resize!([6, 5, 4, 3, 2, 1], 3) 3-element Vector{Int64}: 6 5 4 julia> a = resize!([6, 5, 4, 3, 2, 1], 8); julia> length(a) 8 julia> a[1:6] 6-element Vector{Int64}: 6 5 4 3 2 1 ``` """ function resize!(a::Vector, nl::Integer) l = length(a) if nl > l _growend!(a, nl-l) elseif nl != l if nl < 0 _throw_argerror("new length must be โ‰ฅ 0") end _deleteend!(a, l-nl) end return a end """ sizehint!(s, n) -> s Suggest that collection `s` reserve capacity for at least `n` elements. That is, if you expect that you're going to have to push a lot of values onto `s`, you can avoid the cost of incremental reallocation by doing it once up front; this can improve performance. See also [`resize!`](@ref). # Notes on the performance model For types that support `sizehint!`, 1. `push!` and `append!` methods generally may (but are not required to) preallocate extra storage. For types implemented in `Base`, they typically do, using a heuristic optimized for a general use case. 2. `sizehint!` may control this preallocation. Again, it typically does this for types in `Base`. 3. `empty!` is nearly costless (and O(1)) for types that support this kind of preallocation. """ function sizehint! end function sizehint!(a::Vector, sz::Integer) ccall(:jl_array_sizehint, Cvoid, (Any, UInt), a, sz) a end """ pop!(collection) -> item Remove an item in `collection` and return it. If `collection` is an ordered container, the last item is returned; for unordered containers, an arbitrary element is returned. See also: [`popfirst!`](@ref), [`popat!`](@ref), [`delete!`](@ref), [`deleteat!`](@ref), [`splice!`](@ref), and [`push!`](@ref). # Examples ```jldoctest julia> A=[1, 2, 3] 3-element Vector{Int64}: 1 2 3 julia> pop!(A) 3 julia> A 2-element Vector{Int64}: 1 2 julia> S = Set([1, 2]) Set{Int64} with 2 elements: 2 1 julia> pop!(S) 2 julia> S Set{Int64} with 1 element: 1 julia> pop!(Dict(1=>2)) 1 => 2 ``` """ function pop!(a::Vector) if isempty(a) _throw_argerror("array must be non-empty") end item = a[end] _deleteend!(a, 1) return item end """ popat!(a::Vector, i::Integer, [default]) Remove the item at the given `i` and return it. Subsequent items are shifted to fill the resulting gap. When `i` is not a valid index for `a`, return `default`, or throw an error if `default` is not specified. See also: [`pop!`](@ref), [`popfirst!`](@ref), [`deleteat!`](@ref), [`splice!`](@ref). !!! compat "Julia 1.5" This function is available as of Julia 1.5. # Examples ```jldoctest julia> a = [4, 3, 2, 1]; popat!(a, 2) 3 julia> a 3-element Vector{Int64}: 4 2 1 julia> popat!(a, 4, missing) missing julia> popat!(a, 4) ERROR: BoundsError: attempt to access 3-element Vector{Int64} at index [4] [...] ``` """ function popat!(a::Vector, i::Integer) x = a[i] _deleteat!(a, i, 1) x end function popat!(a::Vector, i::Integer, default) if 1 <= i <= length(a) x = @inbounds a[i] _deleteat!(a, i, 1) x else default end end """ pushfirst!(collection, items...) -> collection Insert one or more `items` at the beginning of `collection`. This function is called `unshift` in many other programming languages. # Examples ```jldoctest julia> pushfirst!([1, 2, 3, 4], 5, 6) 6-element Vector{Int64}: 5 6 1 2 3 4 ``` """ function pushfirst!(a::Vector{T}, item) where T item = item isa T ? item : convert(T, item)::T _growbeg!(a, 1) @_safeindex a[1] = item return a end # specialize and optimize the single argument case function pushfirst!(a::Vector{Any}, @nospecialize x) _growbeg!(a, 1) @_safeindex a[1] = x return a end function pushfirst!(a::Vector{Any}, @nospecialize x...) @_terminates_locally_meta na = length(a) nx = length(x) _growbeg!(a, nx) @_safeindex for i = 1:nx a[i] = x[i] end return a end """ popfirst!(collection) -> item Remove the first `item` from `collection`. This function is called `shift` in many other programming languages. See also: [`pop!`](@ref), [`popat!`](@ref), [`delete!`](@ref). # Examples ```jldoctest julia> A = [1, 2, 3, 4, 5, 6] 6-element Vector{Int64}: 1 2 3 4 5 6 julia> popfirst!(A) 1 julia> A 5-element Vector{Int64}: 2 3 4 5 6 ``` """ function popfirst!(a::Vector) if isempty(a) _throw_argerror("array must be non-empty") end item = a[1] _deletebeg!(a, 1) return item end """ insert!(a::Vector, index::Integer, item) Insert an `item` into `a` at the given `index`. `index` is the index of `item` in the resulting `a`. See also: [`push!`](@ref), [`replace`](@ref), [`popat!`](@ref), [`splice!`](@ref). # Examples ```jldoctest julia> insert!(Any[1:6;], 3, "here") 7-element Vector{Any}: 1 2 "here" 3 4 5 6 ``` """ function insert!(a::Array{T,1}, i::Integer, item) where T # Throw convert error before changing the shape of the array _item = item isa T ? item : convert(T, item)::T _growat!(a, i, 1) # _growat! already did bound check @inbounds a[i] = _item return a end """ deleteat!(a::Vector, i::Integer) Remove the item at the given `i` and return the modified `a`. Subsequent items are shifted to fill the resulting gap. See also: [`keepat!`](@ref), [`delete!`](@ref), [`popat!`](@ref), [`splice!`](@ref). # Examples ```jldoctest julia> deleteat!([6, 5, 4, 3, 2, 1], 2) 5-element Vector{Int64}: 6 4 3 2 1 ``` """ function deleteat!(a::Vector, i::Integer) i isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) _deleteat!(a, i, 1) return a end function deleteat!(a::Vector, r::AbstractUnitRange{<:Integer}) if eltype(r) === Bool return invoke(deleteat!, Tuple{Vector, AbstractVector{Bool}}, a, r) else n = length(a) f = first(r) f isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) isempty(r) || _deleteat!(a, f, length(r)) return a end end """ deleteat!(a::Vector, inds) Remove the items at the indices given by `inds`, and return the modified `a`. Subsequent items are shifted to fill the resulting gap. `inds` can be either an iterator or a collection of sorted and unique integer indices, or a boolean vector of the same length as `a` with `true` indicating entries to delete. # Examples ```jldoctest julia> deleteat!([6, 5, 4, 3, 2, 1], 1:2:5) 3-element Vector{Int64}: 5 3 1 julia> deleteat!([6, 5, 4, 3, 2, 1], [true, false, true, false, true, false]) 3-element Vector{Int64}: 5 3 1 julia> deleteat!([6, 5, 4, 3, 2, 1], (2, 2)) ERROR: ArgumentError: indices must be unique and sorted Stacktrace: [...] ``` """ deleteat!(a::Vector, inds) = _deleteat!(a, inds) deleteat!(a::Vector, inds::AbstractVector) = _deleteat!(a, to_indices(a, (inds,))[1]) struct Nowhere; end push!(::Nowhere, _) = nothing _growend!(::Nowhere, _) = nothing @inline function _push_deleted!(dltd, a::Vector, ind) if @inbounds isassigned(a, ind) push!(dltd, @inbounds a[ind]) else _growend!(dltd, 1) end end @inline function _copy_item!(a::Vector, p, q) if @inbounds isassigned(a, q) @inbounds a[p] = a[q] else _unsetindex!(a, p) end end function _deleteat!(a::Vector, inds, dltd=Nowhere()) n = length(a) y = iterate(inds) y === nothing && return a (p, s) = y checkbounds(a, p) _push_deleted!(dltd, a, p) q = p+1 while true y = iterate(inds, s) y === nothing && break (i,s) = y if !(q <= i <= n) if i < q _throw_argerror("indices must be unique and sorted") else throw(BoundsError()) end end while q < i _copy_item!(a, p, q) p += 1; q += 1 end _push_deleted!(dltd, a, i) q = i+1 end while q <= n _copy_item!(a, p, q) p += 1; q += 1 end _deleteend!(a, n-p+1) return a end # Simpler and more efficient version for logical indexing function deleteat!(a::Vector, inds::AbstractVector{Bool}) n = length(a) length(inds) == n || throw(BoundsError(a, inds)) p = 1 for (q, i) in enumerate(inds) _copy_item!(a, p, q) p += !i end _deleteend!(a, n-p+1) return a end const _default_splice = [] """ splice!(a::Vector, index::Integer, [replacement]) -> item Remove the item at the given index, and return the removed item. Subsequent items are shifted left to fill the resulting gap. If specified, replacement values from an ordered collection will be spliced in place of the removed item. See also: [`replace`](@ref), [`delete!`](@ref), [`deleteat!`](@ref), [`pop!`](@ref), [`popat!`](@ref). # Examples ```jldoctest julia> A = [6, 5, 4, 3, 2, 1]; splice!(A, 5) 2 julia> A 5-element Vector{Int64}: 6 5 4 3 1 julia> splice!(A, 5, -1) 1 julia> A 5-element Vector{Int64}: 6 5 4 3 -1 julia> splice!(A, 1, [-1, -2, -3]) 6 julia> A 7-element Vector{Int64}: -1 -2 -3 5 4 3 -1 ``` To insert `replacement` before an index `n` without removing any items, use `splice!(collection, n:n-1, replacement)`. """ function splice!(a::Vector, i::Integer, ins=_default_splice) v = a[i] m = length(ins) if m == 0 _deleteat!(a, i, 1) elseif m == 1 a[i] = ins[1] else _growat!(a, i, m-1) k = 1 for x in ins a[i+k-1] = x k += 1 end end return v end """ splice!(a::Vector, indices, [replacement]) -> items Remove items at specified indices, and return a collection containing the removed items. Subsequent items are shifted left to fill the resulting gaps. If specified, replacement values from an ordered collection will be spliced in place of the removed items; in this case, `indices` must be a `AbstractUnitRange`. To insert `replacement` before an index `n` without removing any items, use `splice!(collection, n:n-1, replacement)`. $(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.5" Prior to Julia 1.5, `indices` must always be a `UnitRange`. !!! compat "Julia 1.8" Prior to Julia 1.8, `indices` must be a `UnitRange` if splicing in replacement values. # Examples ```jldoctest julia> A = [-1, -2, -3, 5, 4, 3, -1]; splice!(A, 4:3, 2) Int64[] julia> A 8-element Vector{Int64}: -1 -2 -3 2 5 4 3 -1 ``` """ function splice!(a::Vector, r::AbstractUnitRange{<:Integer}, ins=_default_splice) v = a[r] m = length(ins) if m == 0 deleteat!(a, r) return v end n = length(a) f = first(r) l = last(r) d = length(r) if m < d delta = d - m _deleteat!(a, (f - 1 < n - l) ? f : (l - delta + 1), delta) elseif m > d _growat!(a, (f - 1 < n - l) ? f : (l + 1), m - d) end k = 1 for x in ins a[f+k-1] = x k += 1 end return v end splice!(a::Vector, inds) = (dltds = eltype(a)[]; _deleteat!(a, inds, dltds); dltds) function empty!(a::Vector) _deleteend!(a, length(a)) return a end # use memcmp for cmp on byte arrays function cmp(a::Array{UInt8,1}, b::Array{UInt8,1}) ta = @_gc_preserve_begin a tb = @_gc_preserve_begin b pa = unsafe_convert(Ptr{Cvoid}, a) pb = unsafe_convert(Ptr{Cvoid}, b) c = memcmp(pa, pb, min(length(a),length(b))) @_gc_preserve_end ta @_gc_preserve_end tb return c < 0 ? -1 : c > 0 ? +1 : cmp(length(a),length(b)) end const BitIntegerArray{N} = Union{map(T->Array{T,N}, BitInteger_types)...} where N # use memcmp for == on bit integer types function ==(a::Arr, b::Arr) where {Arr <: BitIntegerArray} if size(a) == size(b) ta = @_gc_preserve_begin a tb = @_gc_preserve_begin b pa = unsafe_convert(Ptr{Cvoid}, a) pb = unsafe_convert(Ptr{Cvoid}, b) c = memcmp(pa, pb, sizeof(eltype(Arr)) * length(a)) @_gc_preserve_end ta @_gc_preserve_end tb return c == 0 else return false end end function ==(a::Arr, b::Arr) where Arr <: BitIntegerArray{1} len = length(a) if len == length(b) ta = @_gc_preserve_begin a tb = @_gc_preserve_begin b T = eltype(Arr) pa = unsafe_convert(Ptr{T}, a) pb = unsafe_convert(Ptr{T}, b) c = memcmp(pa, pb, sizeof(T) * len) @_gc_preserve_end ta @_gc_preserve_end tb return c == 0 else return false end end """ reverse(v [, start=firstindex(v) [, stop=lastindex(v) ]] ) Return a copy of `v` reversed from start to stop. See also [`Iterators.reverse`](@ref) for reverse-order iteration without making a copy, and in-place [`reverse!`](@ref). # Examples ```jldoctest julia> A = Vector(1:5) 5-element Vector{Int64}: 1 2 3 4 5 julia> reverse(A) 5-element Vector{Int64}: 5 4 3 2 1 julia> reverse(A, 1, 4) 5-element Vector{Int64}: 4 3 2 1 5 julia> reverse(A, 3, 5) 5-element Vector{Int64}: 1 2 5 4 3 ``` """ function reverse(A::AbstractVector, start::Integer, stop::Integer=lastindex(A)) s, n = Int(start), Int(stop) B = similar(A) for i = firstindex(A):s-1 B[i] = A[i] end for i = s:n B[i] = A[n+s-i] end for i = n+1:lastindex(A) B[i] = A[i] end return B end # 1d special cases of reverse(A; dims) and reverse!(A; dims): for (f,_f) in ((:reverse,:_reverse), (:reverse!,:_reverse!)) @eval begin $f(A::AbstractVector; dims=:) = $_f(A, dims) $_f(A::AbstractVector, ::Colon) = $f(A, firstindex(A), lastindex(A)) $_f(A::AbstractVector, dim::Tuple{Integer}) = $_f(A, first(dim)) function $_f(A::AbstractVector, dim::Integer) dim == 1 || _throw_argerror(LazyString("invalid dimension ", dim, " โ‰  1")) return $_f(A, :) end end end function reverseind(a::AbstractVector, i::Integer) li = LinearIndices(a) first(li) + last(li) - i end # This implementation of `midpoint` is performance-optimized but safe # only if `lo <= hi`. midpoint(lo::T, hi::T) where T<:Integer = lo + ((hi - lo) >>> 0x01) midpoint(lo::Integer, hi::Integer) = midpoint(promote(lo, hi)...) """ reverse!(v [, start=firstindex(v) [, stop=lastindex(v) ]]) -> v In-place version of [`reverse`](@ref). # Examples ```jldoctest julia> A = Vector(1:5) 5-element Vector{Int64}: 1 2 3 4 5 julia> reverse!(A); julia> A 5-element Vector{Int64}: 5 4 3 2 1 ``` """ function reverse!(v::AbstractVector, start::Integer, stop::Integer=lastindex(v)) s, n = Int(start), Int(stop) if n > s # non-empty and non-trivial liv = LinearIndices(v) if !(first(liv) โ‰ค s โ‰ค last(liv)) throw(BoundsError(v, s)) elseif !(first(liv) โ‰ค n โ‰ค last(liv)) throw(BoundsError(v, n)) end r = n @inbounds for i in s:midpoint(s, n-1) v[i], v[r] = v[r], v[i] r -= 1 end end return v end # concatenations of (in)homogeneous combinations of vectors, horizontal and vertical vcat() = Vector{Any}() hcat() = Vector{Any}() function hcat(V::Vector{T}...) where T height = length(V[1]) for j = 2:length(V) if length(V[j]) != height throw(DimensionMismatch("vectors must have same lengths")) end end return [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] end hcat(A::Vector...) = cat(A...; dims=Val(2)) # more special than SparseArrays's hcat function vcat(arrays::Vector{T}...) where T n = 0 for a in arrays n += length(a) end arr = Vector{T}(undef, n) nd = 1 for a in arrays na = length(a) @assert nd + na <= 1 + length(arr) # Concurrent modification of arrays? unsafe_copyto!(arr, nd, a, 1, na) nd += na end return arr end vcat(A::Vector...) = cat(A...; dims=Val(1)) # more special than SparseArrays's vcat _cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(Returns(1), n-1)..., length(x))) ## find ## """ findnext(A, i) Find the next index after or including `i` of a `true` element of `A`, or `nothing` if not found. Indices are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> A = [false, false, true, false] 4-element Vector{Bool}: 0 0 1 0 julia> findnext(A, 1) 3 julia> findnext(A, 4) # returns nothing, but not printed in the REPL julia> A = [false false; true false] 2ร—2 Matrix{Bool}: 0 0 1 0 julia> findnext(A, CartesianIndex(1, 1)) CartesianIndex(2, 1) ``` """ findnext(A, start) = findnext(identity, A, start) """ findfirst(A) Return the index or key of the first `true` value in `A`. Return `nothing` if no such value is found. To search for other kinds of values, pass a predicate as the first argument. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). See also: [`findall`](@ref), [`findnext`](@ref), [`findlast`](@ref), [`searchsortedfirst`](@ref). # Examples ```jldoctest julia> A = [false, false, true, false] 4-element Vector{Bool}: 0 0 1 0 julia> findfirst(A) 3 julia> findfirst(falses(3)) # returns nothing, but not printed in the REPL julia> A = [false false; true false] 2ร—2 Matrix{Bool}: 0 0 1 0 julia> findfirst(A) CartesianIndex(2, 1) ``` """ findfirst(A) = findfirst(identity, A) # Needed for bootstrap, and allows defining only an optimized findnext method findfirst(A::AbstractArray) = findnext(A, first(keys(A))) """ findnext(predicate::Function, A, i) Find the next index after or including `i` of an element of `A` for which `predicate` returns `true`, or `nothing` if not found. Indices are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> A = [1, 4, 2, 2]; julia> findnext(isodd, A, 1) 1 julia> findnext(isodd, A, 2) # returns nothing, but not printed in the REPL julia> A = [1 4; 2 2]; julia> findnext(isodd, A, CartesianIndex(1, 1)) CartesianIndex(1, 1) ``` """ function findnext(testf::Function, A, start) i = oftype(first(keys(A)), start) l = last(keys(A)) i > l && return nothing while true testf(A[i]) && return i i == l && break # nextind(A, l) can throw/overflow i = nextind(A, i) end return nothing end """ findfirst(predicate::Function, A) Return the index or key of the first element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> A = [1, 4, 2, 2] 4-element Vector{Int64}: 1 4 2 2 julia> findfirst(iseven, A) 2 julia> findfirst(x -> x>10, A) # returns nothing, but not printed in the REPL julia> findfirst(isequal(4), A) 2 julia> A = [1 4; 2 2] 2ร—2 Matrix{Int64}: 1 4 2 2 julia> findfirst(iseven, A) CartesianIndex(2, 1) ``` """ function findfirst(testf::Function, A) for (i, a) in pairs(A) testf(a) && return i end return nothing end # Needed for bootstrap, and allows defining only an optimized findnext method findfirst(testf::Function, A::Union{AbstractArray, AbstractString}) = findnext(testf, A, first(keys(A))) findfirst(p::Union{Fix2{typeof(isequal),Int},Fix2{typeof(==),Int}}, r::OneTo{Int}) = 1 <= p.x <= r.stop ? p.x : nothing findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::AbstractUnitRange) where {T<:Integer} = first(r) <= p.x <= last(r) ? firstindex(r) + Int(p.x - first(r)) : nothing function findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::StepRange{T,S}) where {T,S} isempty(r) && return nothing minimum(r) <= p.x <= maximum(r) || return nothing d = convert(S, p.x - first(r))::S iszero(d % step(r)) || return nothing return d รท step(r) + 1 end """ findprev(A, i) Find the previous index before or including `i` of a `true` element of `A`, or `nothing` if not found. Indices are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). See also: [`findnext`](@ref), [`findfirst`](@ref), [`findall`](@ref). # Examples ```jldoctest julia> A = [false, false, true, true] 4-element Vector{Bool}: 0 0 1 1 julia> findprev(A, 3) 3 julia> findprev(A, 1) # returns nothing, but not printed in the REPL julia> A = [false false; true true] 2ร—2 Matrix{Bool}: 0 0 1 1 julia> findprev(A, CartesianIndex(2, 1)) CartesianIndex(2, 1) ``` """ findprev(A, start) = findprev(identity, A, start) """ findlast(A) Return the index or key of the last `true` value in `A`. Return `nothing` if there is no `true` value in `A`. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). See also: [`findfirst`](@ref), [`findprev`](@ref), [`findall`](@ref). # Examples ```jldoctest julia> A = [true, false, true, false] 4-element Vector{Bool}: 1 0 1 0 julia> findlast(A) 3 julia> A = falses(2,2); julia> findlast(A) # returns nothing, but not printed in the REPL julia> A = [true false; true false] 2ร—2 Matrix{Bool}: 1 0 1 0 julia> findlast(A) CartesianIndex(2, 1) ``` """ findlast(A) = findlast(identity, A) # Needed for bootstrap, and allows defining only an optimized findprev method findlast(A::AbstractArray) = findprev(A, last(keys(A))) """ findprev(predicate::Function, A, i) Find the previous index before or including `i` of an element of `A` for which `predicate` returns `true`, or `nothing` if not found. Indices are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> A = [4, 6, 1, 2] 4-element Vector{Int64}: 4 6 1 2 julia> findprev(isodd, A, 1) # returns nothing, but not printed in the REPL julia> findprev(isodd, A, 3) 3 julia> A = [4 6; 1 2] 2ร—2 Matrix{Int64}: 4 6 1 2 julia> findprev(isodd, A, CartesianIndex(1, 2)) CartesianIndex(2, 1) ``` """ function findprev(testf::Function, A, start) f = first(keys(A)) i = oftype(f, start) i < f && return nothing while true testf(A[i]) && return i i == f && break # prevind(A, f) can throw/underflow i = prevind(A, i) end return nothing end """ findlast(predicate::Function, A) Return the index or key of the last element of `A` for which `predicate` returns `true`. Return `nothing` if there is no such element. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> A = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> findlast(isodd, A) 3 julia> findlast(x -> x > 5, A) # returns nothing, but not printed in the REPL julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> findlast(isodd, A) CartesianIndex(2, 1) ``` """ function findlast(testf::Function, A) for (i, a) in Iterators.reverse(pairs(A)) testf(a) && return i end return nothing end # Needed for bootstrap, and allows defining only an optimized findprev method findlast(testf::Function, A::Union{AbstractArray, AbstractString}) = findprev(testf, A, last(keys(A))) """ findall(f::Function, A) Return a vector `I` of the indices or keys of `A` where `f(A[I])` returns `true`. If there are no such elements of `A`, return an empty array. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). # Examples ```jldoctest julia> x = [1, 3, 4] 3-element Vector{Int64}: 1 3 4 julia> findall(isodd, x) 2-element Vector{Int64}: 1 2 julia> A = [1 2 0; 3 4 0] 2ร—3 Matrix{Int64}: 1 2 0 3 4 0 julia> findall(isodd, A) 2-element Vector{CartesianIndex{2}}: CartesianIndex(1, 1) CartesianIndex(2, 1) julia> findall(!iszero, A) 4-element Vector{CartesianIndex{2}}: CartesianIndex(1, 1) CartesianIndex(2, 1) CartesianIndex(1, 2) CartesianIndex(2, 2) julia> d = Dict(:A => 10, :B => -1, :C => 0) Dict{Symbol, Int64} with 3 entries: :A => 10 :B => -1 :C => 0 julia> findall(x -> x >= 0, d) 2-element Vector{Symbol}: :A :C ``` """ function findall(testf::Function, A) T = eltype(keys(A)) gen = (first(p) for p in pairs(A) if testf(last(p))) isconcretetype(T) ? collect(T, gen) : collect(gen) end # Broadcasting is much faster for small testf, and computing # integer indices from logical index using findall has a negligible cost findall(testf::F, A::AbstractArray) where {F<:Function} = findall(testf.(A)) """ findall(A) Return a vector `I` of the `true` indices or keys of `A`. If there are no such elements of `A`, return an empty array. To search for other kinds of values, pass a predicate as the first argument. Indices or keys are of the same type as those returned by [`keys(A)`](@ref) and [`pairs(A)`](@ref). See also: [`findfirst`](@ref), [`searchsorted`](@ref). # Examples ```jldoctest julia> A = [true, false, false, true] 4-element Vector{Bool}: 1 0 0 1 julia> findall(A) 2-element Vector{Int64}: 1 4 julia> A = [true false; false true] 2ร—2 Matrix{Bool}: 1 0 0 1 julia> findall(A) 2-element Vector{CartesianIndex{2}}: CartesianIndex(1, 1) CartesianIndex(2, 2) julia> findall(falses(3)) Int64[] ``` """ function findall(A) collect(first(p) for p in pairs(A) if last(p)) end # Allocating result upfront is faster (possible only when collection can be iterated twice) function findall(A::AbstractArray{Bool}) n = count(A) I = Vector{eltype(keys(A))}(undef, n) cnt = 1 for (i,a) in pairs(A) if a I[cnt] = i cnt += 1 end end I end findall(x::Bool) = x ? [1] : Vector{Int}() findall(testf::Function, x::Number) = testf(x) ? [1] : Vector{Int}() findall(p::Fix2{typeof(in)}, x::Number) = x in p.x ? [1] : Vector{Int}() # similar to Matlab's ismember """ indexin(a, b) Return an array containing the first index in `b` for each value in `a` that is a member of `b`. The output array contains `nothing` wherever `a` is not a member of `b`. See also: [`sortperm`](@ref), [`findfirst`](@ref). # Examples ```jldoctest julia> a = ['a', 'b', 'c', 'b', 'd', 'a']; julia> b = ['a', 'b', 'c']; julia> indexin(a, b) 6-element Vector{Union{Nothing, Int64}}: 1 2 3 2 nothing 1 julia> indexin(b, a) 3-element Vector{Union{Nothing, Int64}}: 1 2 3 ``` """ function indexin(a, b::AbstractArray) inds = keys(b) bdict = Dict{eltype(b),eltype(inds)}() for (val, ind) in zip(b, inds) get!(bdict, val, ind) end return Union{eltype(inds), Nothing}[ get(bdict, i, nothing) for i in a ] end function _findin(a::Union{AbstractArray, Tuple}, b) ind = Vector{eltype(keys(a))}() bset = Set(b) @inbounds for (i,ai) in pairs(a) ai in bset && push!(ind, i) end ind end # If two collections are already sorted, _findin can be computed with # a single traversal of the two collections. This is much faster than # using a hash table (although it has the same complexity). function _sortedfindin(v::Union{AbstractArray, Tuple}, w) viter, witer = keys(v), eachindex(w) out = eltype(viter)[] vy, wy = iterate(viter), iterate(witer) if vy === nothing || wy === nothing return out end viteri, i = vy witerj, j = wy @inbounds begin vi, wj = v[viteri], w[witerj] while true if isless(vi, wj) vy = iterate(viter, i) if vy === nothing break end viteri, i = vy vi = v[viteri] elseif isless(wj, vi) wy = iterate(witer, j) if wy === nothing break end witerj, j = wy wj = w[witerj] else push!(out, viteri) vy = iterate(viter, i) if vy === nothing break end # We only increment the v iterator because v can have # repeated matches to a single value in w viteri, i = vy vi = v[viteri] end end end return out end function findall(pred::Fix2{typeof(in),<:Union{Array{<:Real},Real}}, x::Array{<:Real}) if issorted(x, Sort.Forward) && issorted(pred.x, Sort.Forward) return _sortedfindin(x, pred.x) else return _findin(x, pred.x) end end # issorted fails for some element types so the method above has to be restricted # to element with isless/< defined. findall(pred::Fix2{typeof(in)}, x::AbstractArray) = _findin(x, pred.x) findall(pred::Fix2{typeof(in)}, x::Tuple) = _findin(x, pred.x) # Copying subregions function indcopy(sz::Dims, I::Vector) n = length(I) s = sz[n] for i = n+1:length(sz) s *= sz[i] end dst = eltype(I)[_findin(I[i], i < n ? (1:sz[i]) : (1:s)) for i = 1:n] src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n] dst, src end function indcopy(sz::Dims, I::Tuple{Vararg{RangeIndex}}) n = length(I) s = sz[n] for i = n+1:length(sz) s *= sz[i] end dst::typeof(I) = ntuple(i-> _findin(I[i], i < n ? (1:sz[i]) : (1:s)), n)::typeof(I) src::typeof(I) = ntuple(i-> I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))], n)::typeof(I) dst, src end ## Filter ## """ filter(f, a) Return a copy of collection `a`, removing elements for which `f` is `false`. The function `f` is passed one argument. !!! compat "Julia 1.4" Support for `a` as a tuple requires at least Julia 1.4. See also: [`filter!`](@ref), [`Iterators.filter`](@ref). # Examples ```jldoctest julia> a = 1:10 1:10 julia> filter(isodd, a) 5-element Vector{Int64}: 1 3 5 7 9 ``` """ function filter(f, a::Array{T, N}) where {T, N} j = 1 b = Vector{T}(undef, length(a)) for ai in a @inbounds b[j] = ai j = ifelse(f(ai)::Bool, j+1, j) end resize!(b, j-1) sizehint!(b, length(b)) b end function filter(f, a::AbstractArray) (IndexStyle(a) != IndexLinear()) && return a[map(f, a)::AbstractArray{Bool}] j = 1 idxs = Vector{Int}(undef, length(a)) for idx in eachindex(a) @inbounds idxs[j] = idx ai = @inbounds a[idx] j = ifelse(f(ai)::Bool, j+1, j) end resize!(idxs, j-1) res = a[idxs] empty!(idxs) sizehint!(idxs, 0) return res end """ filter!(f, a) Update collection `a`, removing elements for which `f` is `false`. The function `f` is passed one argument. # Examples ```jldoctest julia> filter!(isodd, Vector(1:10)) 5-element Vector{Int64}: 1 3 5 7 9 ``` """ function filter!(f, a::AbstractVector) j = firstindex(a) for ai in a @inbounds a[j] = ai j = ifelse(f(ai)::Bool, nextind(a, j), j) end j > lastindex(a) && return a if a isa Vector resize!(a, j-1) sizehint!(a, j-1) else deleteat!(a, j:lastindex(a)) end return a end """ filter(f) Create a function that filters its arguments with function `f` using [`filter`](@ref), i.e. a function equivalent to `x -> filter(f, x)`. The returned function is of type `Base.Fix1{typeof(filter)}`, which can be used to implement specialized methods. # Examples ```jldoctest julia> (1, 2, Inf, 4, NaN, 6) |> filter(isfinite) (1, 2, 4, 6) julia> map(filter(iseven), [1:3, 2:4, 3:5]) 3-element Vector{Vector{Int64}}: [2] [2, 4] [4] ``` !!! compat "Julia 1.9" This method requires at least Julia 1.9. """ function filter(f) Fix1(filter, f) end """ keepat!(a::Vector, inds) keepat!(a::BitVector, inds) Remove the items at all the indices which are not given by `inds`, and return the modified `a`. Items which are kept are shifted to fill the resulting gaps. $(_DOCS_ALIASING_WARNING) `inds` must be an iterator of sorted and unique integer indices. See also [`deleteat!`](@ref). !!! compat "Julia 1.7" This function is available as of Julia 1.7. # Examples ```jldoctest julia> keepat!([6, 5, 4, 3, 2, 1], 1:2:5) 3-element Vector{Int64}: 6 4 2 ``` """ keepat!(a::Vector, inds) = _keepat!(a, inds) """ keepat!(a::Vector, m::AbstractVector{Bool}) keepat!(a::BitVector, m::AbstractVector{Bool}) The in-place version of logical indexing `a = a[m]`. That is, `keepat!(a, m)` on vectors of equal length `a` and `m` will remove all elements from `a` for which `m` at the corresponding index is `false`. # Examples ```jldoctest julia> a = [:a, :b, :c]; julia> keepat!(a, [true, false, true]) 2-element Vector{Symbol}: :a :c julia> a 2-element Vector{Symbol}: :a :c ``` """ keepat!(a::Vector, m::AbstractVector{Bool}) = _keepat!(a, m) # set-like operators for vectors # These are moderately efficient, preserve order, and remove dupes. _unique_filter!(pred::P, update!::U, state) where {P,U} = function (x) # P, U force specialization if pred(x, state) update!(state, x) true else false end end _grow_filter!(seen) = _unique_filter!(โˆ‰, push!, seen) _shrink_filter!(keep) = _unique_filter!(โˆˆ, pop!, keep) function _grow!(pred!, v::AbstractVector, itrs) filter!(pred!, v) # uniquify v for itr in itrs mapfilter(pred!, push!, itr, v) end return v end union!(v::AbstractVector{T}, itrs...) where {T} = _grow!(_grow_filter!(sizehint!(Set{T}(), length(v))), v, itrs) symdiff!(v::AbstractVector{T}, itrs...) where {T} = _grow!(_shrink_filter!(symdiff!(Set{T}(), v, itrs...)), v, itrs) function _shrink!(shrinker!::F, v::AbstractVector, itrs) where F seen = Set{eltype(v)}() filter!(_grow_filter!(seen), v) shrinker!(seen, itrs...) filter!(in(seen), v) end intersect!(v::AbstractVector, itrs...) = _shrink!(intersect!, v, itrs) setdiff!( v::AbstractVector, itrs...) = _shrink!(setdiff!, v, itrs) vectorfilter(T::Type, f, v) = T[x for x in v if f(x)] function _shrink(shrinker!::F, itr, itrs) where F T = promote_eltype(itr, itrs...) keep = shrinker!(Set{T}(itr), itrs...) vectorfilter(T, _shrink_filter!(keep), itr) end intersect(itr, itrs...) = _shrink(intersect!, itr, itrs) setdiff( itr, itrs...) = _shrink(setdiff!, itr, itrs) function intersect(v::AbstractVector, r::AbstractRange) T = promote_eltype(v, r) common = Iterators.filter(in(r), v) seen = Set{T}(common) return vectorfilter(T, _shrink_filter!(seen), common) end intersect(r::AbstractRange, v::AbstractVector) = intersect(v, r) T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/abstractarray.jl€ล# This file is a part of Julia. License is MIT: https://julialang.org/license ## Basic functions ## """ AbstractArray{T,N} Supertype for `N`-dimensional arrays (or array-like types) with elements of type `T`. [`Array`](@ref) and other types are subtypes of this. See the manual section on the [`AbstractArray` interface](@ref man-interface-array). See also: [`AbstractVector`](@ref), [`AbstractMatrix`](@ref), [`eltype`](@ref), [`ndims`](@ref). """ AbstractArray convert(::Type{T}, a::T) where {T<:AbstractArray} = a convert(::Type{AbstractArray{T}}, a::AbstractArray) where {T} = AbstractArray{T}(a)::AbstractArray{T} convert(::Type{AbstractArray{T,N}}, a::AbstractArray{<:Any,N}) where {T,N} = AbstractArray{T,N}(a)::AbstractArray{T,N} """ size(A::AbstractArray, [dim]) Return a tuple containing the dimensions of `A`. Optionally you can specify a dimension to just get the length of that dimension. Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref) may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices). See also: [`length`](@ref), [`ndims`](@ref), [`eachindex`](@ref), [`sizeof`](@ref). # Examples ```jldoctest julia> A = fill(1, (2,3,4)); julia> size(A) (2, 3, 4) julia> size(A, 2) 3 ``` """ size(t::AbstractArray{T,N}, d) where {T,N} = d::Integer <= N ? size(t)[d] : 1 """ axes(A, d) Return the valid range of indices for array `A` along dimension `d`. See also [`size`](@ref), and the manual chapter on [arrays with custom indices](@ref man-custom-indices). # Examples ```jldoctest julia> A = fill(1, (5,6,7)); julia> axes(A, 2) Base.OneTo(6) julia> axes(A, 4) == 1:1 # all dimensions d > ndims(A) have size 1 true ``` # Usage note Each of the indices has to be an `AbstractUnitRange{<:Integer}`, but at the same time can be a type that uses custom indices. So, for example, if you need a subset, use generalized indexing constructs like `begin`/`end` or [`firstindex`](@ref)/[`lastindex`](@ref): ```julia ix = axes(v, 1) ix[2:end] # will work for eg Vector, but may fail in general ix[(begin+1):end] # works for generalized indexes ``` """ function axes(A::AbstractArray{T,N}, d) where {T,N} @inline d::Integer <= N ? axes(A)[d] : OneTo(1) end """ axes(A) Return the tuple of valid indices for array `A`. See also: [`size`](@ref), [`keys`](@ref), [`eachindex`](@ref). # Examples ```jldoctest julia> A = fill(1, (5,6,7)); julia> axes(A) (Base.OneTo(5), Base.OneTo(6), Base.OneTo(7)) ``` """ function axes(A) @inline map(oneto, size(A)) end """ has_offset_axes(A) has_offset_axes(A, B, ...) Return `true` if the indices of `A` start with something other than 1 along any axis. If multiple arguments are passed, equivalent to `has_offset_axes(A) || has_offset_axes(B) || ...`. See also [`require_one_based_indexing`](@ref). """ has_offset_axes() = false has_offset_axes(A) = _any_tuple(x->Int(first(x))::Int != 1, false, axes(A)...) has_offset_axes(A::AbstractVector) = Int(firstindex(A))::Int != 1 # improve performance of a common case (ranges) has_offset_axes(::Colon) = false has_offset_axes(::Array) = false # note: this could call `any` directly if the compiler can infer it. We don't use _any_tuple # here because it stops full elision in some cases (#49332) and we don't need handling of # `missing` (has_offset_axes(A) always returns a Bool) has_offset_axes(A, As...) = has_offset_axes(A) || has_offset_axes(As...) """ require_one_based_indexing(A::AbstractArray) require_one_based_indexing(A,B...) Throw an `ArgumentError` if the indices of any argument start with something other than `1` along any axis. See also [`has_offset_axes`](@ref). !!! compat "Julia 1.2" This function requires at least Julia 1.2. """ require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) # Performance optimization: get rid of a branch on `d` in `axes(A, d)` # for d=1. 1d arrays are heavily used, and the first dimension comes up # in other applications. axes1(A::AbstractArray{<:Any,0}) = OneTo(1) axes1(A::AbstractArray) = (@inline; axes(A)[1]) axes1(iter) = oneto(length(iter)) """ keys(a::AbstractArray) Return an efficient array describing all valid indices for `a` arranged in the shape of `a` itself. The keys of 1-dimensional arrays (vectors) are integers, whereas all other N-dimensional arrays use [`CartesianIndex`](@ref) to describe their locations. Often the special array types [`LinearIndices`](@ref) and [`CartesianIndices`](@ref) are used to efficiently represent these arrays of integers and `CartesianIndex`es, respectively. Note that the `keys` of an array might not be the most efficient index type; for maximum performance use [`eachindex`](@ref) instead. # Examples ```jldoctest julia> keys([4, 5, 6]) 3-element LinearIndices{1, Tuple{Base.OneTo{Int64}}}: 1 2 3 julia> keys([4 5; 6 7]) CartesianIndices((2, 2)) ``` """ keys(a::AbstractArray) = CartesianIndices(axes(a)) keys(a::AbstractVector) = LinearIndices(a) """ keytype(T::Type{<:AbstractArray}) keytype(A::AbstractArray) Return the key type of an array. This is equal to the [`eltype`](@ref) of the result of `keys(...)`, and is provided mainly for compatibility with the dictionary interface. # Examples ```jldoctest julia> keytype([1, 2, 3]) == Int true julia> keytype([1 2; 3 4]) CartesianIndex{2} ``` !!! compat "Julia 1.2" For arrays, this function requires at least Julia 1.2. """ keytype(a::AbstractArray) = keytype(typeof(a)) keytype(::Type{Union{}}, slurp...) = eltype(Union{}) keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} keytype(A::Type{<:AbstractVector}) = Int valtype(a::AbstractArray) = valtype(typeof(a)) valtype(::Type{Union{}}, slurp...) = eltype(Union{}) """ valtype(T::Type{<:AbstractArray}) valtype(A::AbstractArray) Return the value type of an array. This is identical to [`eltype`](@ref) and is provided mainly for compatibility with the dictionary interface. # Examples ```jldoctest julia> valtype(["one", "two", "three"]) String ``` !!! compat "Julia 1.2" For arrays, this function requires at least Julia 1.2. """ valtype(A::Type{<:AbstractArray}) = eltype(A) prevind(::AbstractArray, i::Integer) = Int(i)-1 nextind(::AbstractArray, i::Integer) = Int(i)+1 """ eltype(type) Determine the type of the elements generated by iterating a collection of the given `type`. For dictionary types, this will be a `Pair{KeyType,ValType}`. The definition `eltype(x) = eltype(typeof(x))` is provided for convenience so that instances can be passed instead of types. However the form that accepts a type argument should be defined for new types. See also: [`keytype`](@ref), [`typeof`](@ref). # Examples ```jldoctest julia> eltype(fill(1f0, (2,2))) Float32 julia> eltype(fill(0x1, (2,2))) UInt8 ``` """ eltype(::Type) = Any eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements")) eltype(x) = eltype(typeof(x)) eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any """ elsize(type) Compute the memory stride in bytes between consecutive elements of [`eltype`](@ref) stored inside the given `type`, if the array elements are stored densely with a uniform linear stride. # Examples ```jldoctest julia> Base.elsize(rand(Float32, 10)) 4 ``` """ elsize(A::AbstractArray) = elsize(typeof(A)) """ ndims(A::AbstractArray) -> Integer Return the number of dimensions of `A`. See also: [`size`](@ref), [`axes`](@ref). # Examples ```jldoctest julia> A = fill(1, (3,4,5)); julia> ndims(A) 3 ``` """ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements")) """ length(collection) -> Integer Return the number of elements in the collection. Use [`lastindex`](@ref) to get the last valid index of an indexable collection. See also: [`size`](@ref), [`ndims`](@ref), [`eachindex`](@ref). # Examples ```jldoctest julia> length(1:5) 5 julia> length([1, 2, 3, 4]) 4 julia> length([1 2; 3 4]) 4 ``` """ length """ length(A::AbstractArray) Return the number of elements in the array, defaults to `prod(size(A))`. # Examples ```jldoctest julia> length([1, 2, 3, 4]) 4 julia> length([1 2; 3 4]) 4 ``` """ length(t::AbstractArray) = (@inline; prod(size(t))) # `eachindex` is mostly an optimization of `keys` eachindex(itrs...) = keys(itrs...) # eachindex iterates over all indices. IndexCartesian definitions are later. eachindex(A::AbstractVector) = (@inline(); axes1(A)) @noinline function throw_eachindex_mismatch_indices(::IndexLinear, inds...) throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(inds, ", ", " and "))")) end @noinline function throw_eachindex_mismatch_indices(::IndexCartesian, inds...) throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(inds, ", ", " and "))")) end """ eachindex(A...) eachindex(::IndexStyle, A::AbstractArray...) Create an iterable object for visiting each index of an `AbstractArray` `A` in an efficient manner. For array types that have opted into fast linear indexing (like `Array`), this is simply the range `1:length(A)` if they use 1-based indexing. For array types that have not opted into fast linear indexing, a specialized Cartesian range is typically returned to efficiently index into the array with indices specified for every dimension. In general `eachindex` accepts arbitrary iterables, including strings and dictionaries, and returns an iterator object supporting arbitrary index types (e.g. unevenly spaced or non-integer indices). If `A` is `AbstractArray` it is possible to explicitly specify the style of the indices that should be returned by `eachindex` by passing a value having `IndexStyle` type as its first argument (typically `IndexLinear()` if linear indices are required or `IndexCartesian()` if Cartesian range is wanted). If you supply more than one `AbstractArray` argument, `eachindex` will create an iterable object that is fast for all arguments (typically a [`UnitRange`](@ref) if all inputs have fast linear indexing, a [`CartesianIndices`](@ref) otherwise). If the arrays have different sizes and/or dimensionalities, a `DimensionMismatch` exception will be thrown. See also [`pairs`](@ref)`(A)` to iterate over indices and values together, and [`axes`](@ref)`(A, 2)` for valid indices along one dimension. # Examples ```jldoctest julia> A = [10 20; 30 40]; julia> for i in eachindex(A) # linear indexing println("A[", i, "] == ", A[i]) end A[1] == 10 A[2] == 30 A[3] == 20 A[4] == 40 julia> for i in eachindex(view(A, 1:2, 1:1)) # Cartesian indexing println(i) end CartesianIndex(1, 1) CartesianIndex(2, 1) ``` """ eachindex(A::AbstractArray) = (@inline(); eachindex(IndexStyle(A), A)) function eachindex(A::AbstractArray, B::AbstractArray) @inline eachindex(IndexStyle(A,B), A, B) end function eachindex(A::AbstractArray, B::AbstractArray...) @inline eachindex(IndexStyle(A,B...), A, B...) end eachindex(::IndexLinear, A::AbstractArray) = (@inline; oneto(length(A))) eachindex(::IndexLinear, A::AbstractVector) = (@inline; axes1(A)) function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) @inline indsA = eachindex(IndexLinear(), A) _all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) || throw_eachindex_mismatch_indices(IndexLinear(), eachindex(A), eachindex.(B)...) indsA end function _all_match_first(f::F, inds, A, B...) where F<:Function @inline (inds == f(A)) & _all_match_first(f, inds, B...) end _all_match_first(f::F, inds) where F<:Function = true # keys with an IndexStyle keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) """ lastindex(collection) -> Integer lastindex(collection, d) -> Integer Return the last index of `collection`. If `d` is given, return the last index of `collection` along dimension `d`. The syntaxes `A[end]` and `A[end, end]` lower to `A[lastindex(A)]` and `A[lastindex(A, 1), lastindex(A, 2)]`, respectively. See also: [`axes`](@ref), [`firstindex`](@ref), [`eachindex`](@ref), [`prevind`](@ref). # Examples ```jldoctest julia> lastindex([1,2,4]) 3 julia> lastindex(rand(3,4,5), 2) 4 ``` """ lastindex(a::AbstractArray) = (@inline; last(eachindex(IndexLinear(), a))) lastindex(a, d) = (@inline; last(axes(a, d))) """ firstindex(collection) -> Integer firstindex(collection, d) -> Integer Return the first index of `collection`. If `d` is given, return the first index of `collection` along dimension `d`. The syntaxes `A[begin]` and `A[1, begin]` lower to `A[firstindex(A)]` and `A[1, firstindex(A, 2)]`, respectively. See also: [`first`](@ref), [`axes`](@ref), [`lastindex`](@ref), [`nextind`](@ref). # Examples ```jldoctest julia> firstindex([1,2,4]) 1 julia> firstindex(rand(3,4,5), 2) 1 ``` """ firstindex(a::AbstractArray) = (@inline; first(eachindex(IndexLinear(), a))) firstindex(a, d) = (@inline; first(axes(a, d))) first(a::AbstractArray) = a[first(eachindex(a))] """ first(coll) Get the first element of an iterable collection. Return the start point of an [`AbstractRange`](@ref) even if it is empty. See also: [`only`](@ref), [`firstindex`](@ref), [`last`](@ref). # Examples ```jldoctest julia> first(2:2:10) 2 julia> first([1; 2; 3; 4]) 1 ``` """ function first(itr) x = iterate(itr) x === nothing && throw(ArgumentError("collection must be non-empty")) x[1] end """ first(itr, n::Integer) Get the first `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not long enough. See also: [`startswith`](@ref), [`Iterators.take`](@ref). !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> first(["foo", "bar", "qux"], 2) 2-element Vector{String}: "foo" "bar" julia> first(1:6, 10) 1:6 julia> first(Bool[], 1) Bool[] ``` """ first(itr, n::Integer) = collect(Iterators.take(itr, n)) # Faster method for vectors function first(v::AbstractVector, n::Integer) n < 0 && throw(ArgumentError("Number of elements must be nonnegative")) v[range(begin, length=min(n, checked_length(v)))] end """ last(coll) Get the last element of an ordered collection, if it can be computed in O(1) time. This is accomplished by calling [`lastindex`](@ref) to get the last index. Return the end point of an [`AbstractRange`](@ref) even if it is empty. See also [`first`](@ref), [`endswith`](@ref). # Examples ```jldoctest julia> last(1:2:10) 9 julia> last([1; 2; 3; 4]) 4 ``` """ last(a) = a[end] """ last(itr, n::Integer) Get the last `n` elements of the iterable collection `itr`, or fewer elements if `itr` is not long enough. !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> last(["foo", "bar", "qux"], 2) 2-element Vector{String}: "bar" "qux" julia> last(1:6, 10) 1:6 julia> last(Float64[], 1) Float64[] ``` """ last(itr, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(itr), n))) # Faster method for arrays function last(v::AbstractVector, n::Integer) n < 0 && throw(ArgumentError("Number of elements must be nonnegative")) v[range(stop=lastindex(v), length=min(n, checked_length(v)))] end """ strides(A) Return a tuple of the memory strides in each dimension. See also: [`stride`](@ref). # Examples ```jldoctest julia> A = fill(1, (3,4,5)); julia> strides(A) (1, 3, 12) ``` """ function strides end """ stride(A, k::Integer) Return the distance in memory (in number of elements) between adjacent elements in dimension `k`. See also: [`strides`](@ref). # Examples ```jldoctest julia> A = fill(1, (3,4,5)); julia> stride(A,2) 3 julia> stride(A,3) 12 ``` """ function stride(A::AbstractArray, k::Integer) st = strides(A) k โ‰ค ndims(A) && return st[k] ndims(A) == 0 && return 1 sz = size(A) s = st[1] * sz[1] for i in 2:ndims(A) s += st[i] * sz[i] end return s end @inline size_to_strides(s, d, sz...) = (s, size_to_strides(s * d, sz...)...) size_to_strides(s, d) = (s,) size_to_strides(s) = () function isstored(A::AbstractArray{<:Any,N}, I::Vararg{Integer,N}) where {N} @boundscheck checkbounds(A, I...) return true end # used to compute "end" for last index function trailingsize(A, n) s = 1 for i=n:ndims(A) s *= size(A,i) end return s end function trailingsize(inds::Indices, n) s = 1 for i=n:length(inds) s *= length(inds[i]) end return s end # This version is type-stable even if inds is heterogeneous function trailingsize(inds::Indices) @inline prod(map(length, inds)) end ## Bounds checking ## # The overall hierarchy is # `checkbounds(A, I...)` -> # `checkbounds(Bool, A, I...)` -> # `checkbounds_indices(Bool, IA, I)`, which recursively calls # `checkindex` for each dimension # # See the "boundscheck" devdocs for more information. # # Note this hierarchy has been designed to reduce the likelihood of # method ambiguities. We try to make `checkbounds` the place to # specialize on array type, and try to avoid specializations on index # types; conversely, `checkindex` is intended to be specialized only # on index type (especially, its last argument). """ checkbounds(Bool, A, I...) Return `true` if the specified indices `I` are in bounds for the given array `A`. Subtypes of `AbstractArray` should specialize this method if they need to provide custom bounds checking behaviors; however, in many cases one can rely on `A`'s indices and [`checkindex`](@ref). See also [`checkindex`](@ref). # Examples ```jldoctest julia> A = rand(3, 3); julia> checkbounds(Bool, A, 2) true julia> checkbounds(Bool, A, 3, 4) false julia> checkbounds(Bool, A, 1:3) true julia> checkbounds(Bool, A, 1:3, 2:4) false ``` """ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @inline checkbounds_indices(Bool, axes(A), I) end # Linear indexing is explicitly allowed when there is only one (non-cartesian) index function checkbounds(::Type{Bool}, A::AbstractArray, i) @inline checkindex(Bool, eachindex(IndexLinear(), A), i) end # As a special extension, allow using logical arrays that match the source array exactly function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{Bool,N}) where N @inline axes(A) == axes(I) end """ checkbounds(A, I...) Throw an error if the specified indices `I` are not in bounds for the given array `A`. """ function checkbounds(A::AbstractArray, I...) @inline checkbounds(Bool, A, I...) || throw_boundserror(A, I) nothing end """ checkbounds_indices(Bool, IA, I) Return `true` if the "requested" indices in the tuple `I` fall within the bounds of the "permitted" indices specified by the tuple `IA`. This function recursively consumes elements of these tuples, usually in a 1-for-1 fashion, checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & checkbounds_indices(Bool, IA, I) Note that [`checkindex`](@ref) is being used to perform the actual bounds-check for a single dimension of the array. There are two important exceptions to the 1-1 rule: linear indexing and CartesianIndex{N}, both of which may "consume" more than one element of `IA`. See also [`checkbounds`](@ref). """ function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) @inline checkindex(Bool, IA[1], I[1])::Bool & checkbounds_indices(Bool, tail(IA), tail(I)) end function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) @inline checkindex(Bool, OneTo(1), I[1])::Bool & checkbounds_indices(Bool, (), tail(I)) end checkbounds_indices(::Type{Bool}, IA::Tuple, ::Tuple{}) = (@inline; all(x->length(x)==1, IA)) checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) # check along a single dimension """ checkindex(Bool, inds::AbstractUnitRange, index) Return `true` if the given `index` is within the bounds of `inds`. Custom types that would like to behave as indices for all arrays can extend this method in order to provide a specialized bounds checking implementation. See also [`checkbounds`](@ref). # Examples ```jldoctest julia> checkindex(Bool, 1:20, 8) true julia> checkindex(Bool, 1:20, 21) false ``` """ checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) checkindex(::Type{Bool}, inds::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i) checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds)) checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange) @_propagate_inbounds_meta isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) end checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == axes1(I) checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool}) = false function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) @inline b = true for i in I b &= checkindex(Bool, inds, i) end b end # See also specializations in multidimensional ## Constructors ## # default arguments to similar() """ similar(array, [element_type=eltype(array)], [dims=size(array)]) Create an uninitialized mutable array with the given element type and size, based upon the given source array. The second and third arguments are both optional, defaulting to the given array's `eltype` and `size`. The dimensions may be specified either as a single tuple argument or as a series of integer arguments. Custom AbstractArray subtypes may choose which specific array type is best-suited to return for the given element type and dimensionality. If they do not specialize this method, the default is an `Array{element_type}(undef, dims...)`. For example, `similar(1:10, 1, 4)` returns an uninitialized `Array{Int,2}` since ranges are neither mutable nor support 2 dimensions: ```julia-repl julia> similar(1:10, 1, 4) 1ร—4 Matrix{Int64}: 4419743872 4374413872 4419743888 0 ``` Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two elements since `BitArray`s are both mutable and can support 1-dimensional arrays: ```julia-repl julia> similar(trues(10,10), 2) 2-element BitVector: 0 0 ``` Since `BitArray`s can only store elements of type [`Bool`](@ref), however, if you request a different element type it will create a regular `Array` instead: ```julia-repl julia> similar(falses(10), Float64, 2, 4) 2ร—4 Matrix{Float64}: 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 ``` See also: [`undef`](@ref), [`isassigned`](@ref). """ similar(a::AbstractArray{T}) where {T} = similar(a, T) similar(a::AbstractArray, ::Type{T}) where {T} = similar(a, T, to_shape(axes(a))) similar(a::AbstractArray{T}, dims::Tuple) where {T} = similar(a, T, to_shape(dims)) similar(a::AbstractArray{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) similar(a::AbstractArray, ::Type{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) # Similar supports specifying dims as either Integers or AbstractUnitRanges or any mixed combination # thereof. Ideally, we'd just convert Integers to OneTos and then call a canonical method with the axes, # but we don't want to require all AbstractArray subtypes to dispatch on Base.OneTo. So instead we # define this method to convert supported axes to Ints, with the expectation that an offset array # package will define a method with dims::Tuple{Union{Integer, UnitRange}, Vararg{Union{Integer, UnitRange}}} similar(a::AbstractArray, ::Type{T}, dims::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T} = similar(a, T, to_shape(dims)) similar(a::AbstractArray, ::Type{T}, dims::Tuple{Integer, Vararg{Integer}}) where {T} = similar(a, T, to_shape(dims)) # similar creates an Array by default similar(a::AbstractArray, ::Type{T}, dims::Dims{N}) where {T,N} = Array{T,N}(undef, dims) to_shape(::Tuple{}) = () to_shape(dims::Dims) = dims to_shape(dims::DimsOrInds) = map(to_shape, dims)::DimsOrInds # each dimension to_shape(i::Int) = i to_shape(i::Integer) = Int(i) to_shape(r::OneTo) = Int(last(r)) to_shape(r::AbstractUnitRange) = r """ similar(storagetype, axes) Create an uninitialized mutable array analogous to that specified by `storagetype`, but with `axes` specified by the last argument. **Examples**: similar(Array{Int}, axes(A)) creates an array that "acts like" an `Array{Int}` (and might indeed be backed by one), but which is indexed identically to `A`. If `A` has conventional indexing, this will be identical to `Array{Int}(undef, size(A))`, but if `A` has unconventional indexing then the indices of the result will match `A`. similar(BitArray, (axes(A, 2),)) would create a 1-dimensional logical array whose indices match those of the columns of `A`. """ similar(::Type{T}, dims::DimOrInd...) where {T<:AbstractArray} = similar(T, dims) similar(::Type{T}, shape::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T<:AbstractArray} = similar(T, to_shape(shape)) similar(::Type{T}, dims::Dims) where {T<:AbstractArray} = T(undef, dims) """ empty(v::AbstractVector, [eltype]) Create an empty vector similar to `v`, optionally changing the `eltype`. See also: [`empty!`](@ref), [`isempty`](@ref), [`isassigned`](@ref). # Examples ```jldoctest julia> empty([1.0, 2.0, 3.0]) Float64[] julia> empty([1.0, 2.0, 3.0], String) String[] ``` """ empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() # like empty, but should return a mutable collection, a Vector by default emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() emptymutable(itr, ::Type{U}) where {U} = Vector{U}() """ copy!(dst, src) -> dst In-place [`copy`](@ref) of `src` into `dst`, discarding any pre-existing elements in `dst`. If `dst` and `src` are of the same type, `dst == src` should hold after the call. If `dst` and `src` are multidimensional arrays, they must have equal [`axes`](@ref). $(_DOCS_ALIASING_WARNING) See also [`copyto!`](@ref). !!! compat "Julia 1.1" This method requires at least Julia 1.1. In Julia 1.0 this method is available from the `Future` standard library as `Future.copy!`. """ function copy!(dst::AbstractVector, src::AbstractVector) firstindex(dst) == firstindex(src) || throw(ArgumentError( "vectors must have the same offset for copy! (consider using `copyto!`)")) if length(dst) != length(src) resize!(dst, length(src)) end copyto!(dst, src) end function copy!(dst::AbstractArray, src::AbstractArray) axes(dst) == axes(src) || throw(ArgumentError( "arrays must have the same axes for copy! (consider using `copyto!`)")) copyto!(dst, src) end ## from general iterable to any array # This is `Experimental.@max_methods 1 function copyto! end`, which is not # defined at this point in bootstrap. typeof(function copyto! end).name.max_methods = UInt8(1) function copyto!(dest::AbstractArray, src) destiter = eachindex(dest) y = iterate(destiter) for x in src y === nothing && throw(ArgumentError("destination has fewer elements than required")) dest[y[1]] = x y = iterate(destiter, y[2]) end return dest end function copyto!(dest::AbstractArray, dstart::Integer, src) i = Int(dstart) if haslength(src) && length(dest) > 0 @boundscheck checkbounds(dest, i:(i + length(src) - 1)) for x in src @inbounds dest[i] = x i += 1 end else for x in src dest[i] = x i += 1 end end return dest end # copy from an some iterable object into an AbstractArray function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer) if (sstart < 1) throw(ArgumentError(LazyString("source start offset (",sstart,") is < 1"))) end y = iterate(src) for j = 1:(sstart-1) if y === nothing throw(ArgumentError(LazyString( "source has fewer elements than required, ", "expected at least ", sstart,", got ", j-1))) end y = iterate(src, y[2]) end if y === nothing throw(ArgumentError(LazyString( "source has fewer elements than required, ", "expected at least ",sstart," got ", sstart-1))) end i = Int(dstart) while y !== nothing val, st = y dest[i] = val i += 1 y = iterate(src, st) end return dest end # this method must be separate from the above since src might not have a length function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer) n < 0 && throw(ArgumentError(LazyString("tried to copy n=",n, ", elements, but n should be nonnegative"))) n == 0 && return dest dmax = dstart + n - 1 inds = LinearIndices(dest) if (dstart โˆ‰ inds || dmax โˆ‰ inds) | (sstart < 1) sstart < 1 && throw(ArgumentError(LazyString("source start offset (", sstart,") is < 1"))) throw(BoundsError(dest, dstart:dmax)) end y = iterate(src) for j = 1:(sstart-1) if y === nothing throw(ArgumentError(LazyString( "source has fewer elements than required, ", "expected at least ",sstart,", got ",j-1))) end y = iterate(src, y[2]) end i = Int(dstart) while i <= dmax && y !== nothing val, st = y @inbounds dest[i] = val y = iterate(src, st) i += 1 end i <= dmax && throw(BoundsError(dest, i)) return dest end ## copy between abstract arrays - generally more efficient ## since a single index variable can be used. """ copyto!(dest::AbstractArray, src) -> dest Copy all elements from collection `src` to array `dest`, whose length must be greater than or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten, the other elements are left untouched. See also [`copy!`](@ref Base.copy!), [`copy`](@ref). # Examples ```jldoctest julia> x = [1., 0., 3., 0., 5.]; julia> y = zeros(7); julia> copyto!(y, x); julia> y 7-element Vector{Float64}: 1.0 0.0 3.0 0.0 5.0 0.0 0.0 ``` """ function copyto!(dest::AbstractArray, src::AbstractArray) isempty(src) && return dest if dest isa BitArray # avoid ambiguities with other copyto!(::AbstractArray, ::SourceArray) methods return _copyto_bitarray!(dest, src) end srcโ€ฒ = unalias(dest, src) copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(srcโ€ฒ), srcโ€ฒ) end function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) isempty(src) && return dest srcโ€ฒ = unalias(dest, src) copyto_unaliased!(deststyle, dest, srcstyle, srcโ€ฒ) end function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) isempty(src) && return dest destinds, srcinds = LinearIndices(dest), LinearIndices(src) idf, isf = first(destinds), first(srcinds) ฮ”i = idf - isf (checkbounds(Bool, destinds, isf+ฮ”i) & checkbounds(Bool, destinds, last(srcinds)+ฮ”i)) || throw(BoundsError(dest, srcinds)) if deststyle isa IndexLinear if srcstyle isa IndexLinear # Single-index implementation @inbounds for i in srcinds dest[i + ฮ”i] = src[i] end else # Dual-index implementation i = idf - 1 @inbounds for a in src dest[i+=1] = a end end else iterdest, itersrc = eachindex(dest), eachindex(src) if iterdest == itersrc # Shared-iterator implementation for I in iterdest @inbounds dest[I] = src[I] end else # Dual-iterator implementation ret = iterate(iterdest) @inbounds for a in src idx, state = ret::NTuple{2,Any} dest[idx] = a ret = iterate(iterdest, state) end end end return dest end function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray) copyto!(dest, dstart, src, first(LinearIndices(src)), length(src)) end function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) srcinds = LinearIndices(src) checkbounds(Bool, srcinds, sstart) || throw(BoundsError(src, sstart)) copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1) end function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer, n::Integer) n == 0 && return dest n < 0 && throw(ArgumentError(LazyString("tried to copy n=", n," elements, but n should be nonnegative"))) destinds, srcinds = LinearIndices(dest), LinearIndices(src) (checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1)) (checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1)) srcโ€ฒ = unalias(dest, src) @inbounds for i = 0:n-1 dest[dstart+i] = srcโ€ฒ[sstart+i] end return dest end function copy(a::AbstractArray) @_propagate_inbounds_meta copymutable(a) end function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S} if length(ir_dest) != length(ir_src) throw(ArgumentError(LazyString("source and destination must have same size (got ", length(ir_src)," and ",length(ir_dest),")"))) end if length(jr_dest) != length(jr_src) throw(ArgumentError(LazyString("source and destination must have same size (got ", length(jr_src)," and ",length(jr_dest),")"))) end @boundscheck checkbounds(B, ir_dest, jr_dest) @boundscheck checkbounds(A, ir_src, jr_src) Aโ€ฒ = unalias(B, A) jdest = first(jr_dest) for jsrc in jr_src idest = first(ir_dest) for isrc in ir_src @inbounds B[idest,jdest] = Aโ€ฒ[isrc,jsrc] idest += step(ir_dest) end jdest += step(jr_dest) end return B end @noinline _checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs")) function copyto_axcheck!(dest, src) _checkaxs(axes(dest), axes(src)) copyto!(dest, src) end """ copymutable(a) Make a mutable copy of an array or iterable `a`. For `a::Array`, this is equivalent to `copy(a)`, but for other array types it may differ depending on the type of `similar(a)`. For generic iterables this is equivalent to `collect(a)`. # Examples ```jldoctest julia> tup = (1, 2, 3) (1, 2, 3) julia> Base.copymutable(tup) 3-element Vector{Int64}: 1 2 3 ``` """ function copymutable(a::AbstractArray) @_propagate_inbounds_meta copyto!(similar(a), a) end copymutable(itr) = collect(itr) zero(x::AbstractArray{T}) where {T} = fill!(similar(x, typeof(zero(T))), zero(T)) ## iteration support for arrays by iterating over `eachindex` in the array ## # Allows fast iteration by default for both IndexLinear and IndexCartesian arrays # While the definitions for IndexLinear are all simple enough to inline on their # own, IndexCartesian's CartesianIndices is more complicated and requires explicit # inlining. function iterate(A::AbstractArray, state=(eachindex(A),)) y = iterate(state...) y === nothing && return nothing A[y[1]], (state[1], tail(y)...) end isempty(a::AbstractArray) = (length(a) == 0) ## range conversions ## map(::Type{T}, r::StepRange) where {T<:Real} = T(r.start):T(r.step):T(last(r)) map(::Type{T}, r::UnitRange) where {T<:Real} = T(r.start):T(last(r)) map(::Type{T}, r::StepRangeLen) where {T<:AbstractFloat} = convert(StepRangeLen{T}, r) function map(::Type{T}, r::LinRange) where T<:AbstractFloat LinRange(T(r.start), T(r.stop), length(r)) end ## unsafe/pointer conversions ## # note: the following type definitions don't mean any AbstractArray is convertible to # a data Ref. they just map the array element type to the pointer type for # convenience in cases that work. pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x) function pointer(x::AbstractArray{T}, i::Integer) where T @inline unsafe_convert(Ptr{T}, x) + Int(_memory_offset(x, i))::Int end # The distance from pointer(x) to the element at x[I...] in bytes _memory_offset(x::DenseArray, I::Vararg{Any,N}) where {N} = (_to_linear_index(x, I...) - first(LinearIndices(x)))*elsize(x) function _memory_offset(x::AbstractArray, I::Vararg{Any,N}) where {N} J = _to_subscript_indices(x, I...) return sum(map((i, s, o)->s*(i-o), J, strides(x), Tuple(first(CartesianIndices(x)))))*elsize(x) end ## Approach: # We only define one fallback method on getindex for all argument types. # That dispatches to an (inlined) internal _getindex function, where the goal is # to transform the indices such that we can call the only getindex method that # we require the type A{T,N} <: AbstractArray{T,N} to define; either: # getindex(::A, ::Int) # if IndexStyle(A) == IndexLinear() OR # getindex(::A{T,N}, ::Vararg{Int, N}) where {T,N} # if IndexCartesian() # If the subtype hasn't defined the required method, it falls back to the # _getindex function again where an error is thrown to prevent stack overflows. """ getindex(A, inds...) Return a subset of array `A` as specified by `inds`, where each `ind` may be, for example, an `Int`, an [`AbstractRange`](@ref), or a [`Vector`](@ref). See the manual section on [array indexing](@ref man-array-indexing) for details. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> getindex(A, 1) 1 julia> getindex(A, [2, 1]) 2-element Vector{Int64}: 3 1 julia> getindex(A, 2:4) 3-element Vector{Int64}: 3 2 4 ``` """ function getindex(A::AbstractArray, I...) @_propagate_inbounds_meta error_if_canonical_getindex(IndexStyle(A), A, I...) _getindex(IndexStyle(A), A, to_indices(A, I)...) end # To avoid invalidations from multidimensional.jl: getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) @propagate_inbounds getindex(A::Array, i1::Integer, I::Integer...) = A[to_indices(A, (i1, I...))...] function unsafe_getindex(A::AbstractArray, I...) @inline @inbounds r = getindex(A, I...) r end struct CanonicalIndexError <: Exception func::String type::Any CanonicalIndexError(func::String, @nospecialize(type)) = new(func, type) end error_if_canonical_getindex(::IndexLinear, A::AbstractArray, ::Int) = throw(CanonicalIndexError("getindex", typeof(A))) error_if_canonical_getindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = throw(CanonicalIndexError("getindex", typeof(A))) error_if_canonical_getindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing ## Internal definitions _getindex(::IndexStyle, A::AbstractArray, I...) = error("getindex for $(typeof(A)) with types $(typeof(I)) is not supported") ## IndexLinear Scalar indexing: canonical method is one Int _getindex(::IndexLinear, A::AbstractVector, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) # ambiguity resolution in case packages specialize this (to be avoided if at all possible, but see Interpolations.jl) _getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M @inline @boundscheck checkbounds(A, I...) # generally _to_linear_index requires bounds checking @inbounds r = getindex(A, _to_linear_index(A, I...)) r end _to_linear_index(A::AbstractArray, i::Integer) = i _to_linear_index(A::AbstractVector, i::Integer, I::Integer...) = i _to_linear_index(A::AbstractArray) = first(LinearIndices(A)) _to_linear_index(A::AbstractArray, I::Integer...) = (@inline; _sub2ind(A, I...)) ## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M @inline @boundscheck checkbounds(A, I...) # generally _to_subscript_indices requires bounds checking @inbounds r = getindex(A, _to_subscript_indices(A, I...)...) r end function _getindex(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N} @_propagate_inbounds_meta getindex(A, I...) end _to_subscript_indices(A::AbstractArray, i::Integer) = (@inline; _unsafe_ind2sub(A, i)) _to_subscript_indices(A::AbstractArray{T,N}) where {T,N} = (@inline; fill_to_length((), 1, Val(N))) _to_subscript_indices(A::AbstractArray{T,0}) where {T} = () _to_subscript_indices(A::AbstractArray{T,0}, i::Integer) where {T} = () _to_subscript_indices(A::AbstractArray{T,0}, I::Integer...) where {T} = () function _to_subscript_indices(A::AbstractArray{T,N}, I::Integer...) where {T,N} @inline J, Jrem = IteratorsMD.split(I, Val(N)) _to_subscript_indices(A, J, Jrem) end _to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) = __to_subscript_indices(A, axes(A), J, Jrem) function __to_subscript_indices(A::AbstractArray, ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{}) @inline (J..., map(first, tail(_remaining_size(J, axes(A))))...) end _to_subscript_indices(A, J::Tuple, Jrem::Tuple) = J # already bounds-checked, safe to drop _to_subscript_indices(A::AbstractArray{T,N}, I::Vararg{Int,N}) where {T,N} = I _remaining_size(::Tuple{Any}, t::Tuple) = t _remaining_size(h::Tuple, t::Tuple) = (@inline; _remaining_size(tail(h), tail(t))) _unsafe_ind2sub(::Tuple{}, i) = () # _ind2sub may throw(BoundsError()) in this case _unsafe_ind2sub(sz, i) = (@inline; _ind2sub(sz, i)) ## Setindex! is defined similarly. We first dispatch to an internal _setindex! # function that allows dispatch on array storage """ setindex!(A, X, inds...) A[inds...] = X Store values from array `X` within some subset of `A` as specified by `inds`. The syntax `A[inds...] = X` is equivalent to `(setindex!(A, X, inds...); X)`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = zeros(2,2); julia> setindex!(A, [10, 20], [1, 2]); julia> A[[3, 4]] = [30, 40]; julia> A 2ร—2 Matrix{Float64}: 10.0 30.0 20.0 40.0 ``` """ function setindex!(A::AbstractArray, v, I...) @_propagate_inbounds_meta error_if_canonical_setindex(IndexStyle(A), A, I...) _setindex!(IndexStyle(A), A, v, to_indices(A, I)...) end function unsafe_setindex!(A::AbstractArray, v, I...) @inline @inbounds r = setindex!(A, v, I...) r end error_if_canonical_setindex(::IndexLinear, A::AbstractArray, ::Int) = throw(CanonicalIndexError("setindex!", typeof(A))) error_if_canonical_setindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = throw(CanonicalIndexError("setindex!", typeof(A))) error_if_canonical_setindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing ## Internal definitions _setindex!(::IndexStyle, A::AbstractArray, v, I...) = error("setindex! for $(typeof(A)) with types $(typeof(I)) is not supported") ## IndexLinear Scalar indexing _setindex!(::IndexLinear, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) function _setindex!(::IndexLinear, A::AbstractArray, v, I::Vararg{Int,M}) where M @inline @boundscheck checkbounds(A, I...) @inbounds r = setindex!(A, v, _to_linear_index(A, I...)) r end # IndexCartesian Scalar indexing function _setindex!(::IndexCartesian, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N} @_propagate_inbounds_meta setindex!(A, v, I...) end function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) where M @inline @boundscheck checkbounds(A, I...) @inbounds r = setindex!(A, v, _to_subscript_indices(A, I...)...) r end """ parent(A) Return the underlying parent object of the view. This parent of objects of types `SubArray`, `SubString`, `ReshapedArray` or `LinearAlgebra.Transpose` is what was passed as an argument to `view`, `reshape`, `transpose`, etc. during object creation. If the input is not a wrapped object, return the input itself. If the input is wrapped multiple times, only the outermost wrapper will be removed. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> V = view(A, 1:2, :) 2ร—2 view(::Matrix{Int64}, 1:2, :) with eltype Int64: 1 2 3 4 julia> parent(V) 2ร—2 Matrix{Int64}: 1 2 3 4 ``` """ function parent end parent(a::AbstractArray) = a ## rudimentary aliasing detection ## """ Base.unalias(dest, A) Return either `A` or a copy of `A` in a rough effort to prevent modifications to `dest` from affecting the returned object. No guarantees are provided. Custom arrays that wrap or use fields containing arrays that might alias against other external objects should provide a [`Base.dataids`](@ref) implementation. This function must return an object of exactly the same type as `A` for performance and type stability. Mutable custom arrays for which [`copy(A)`](@ref) is not `typeof(A)` should provide a [`Base.unaliascopy`](@ref) implementation. See also [`Base.mightalias`](@ref). """ unalias(dest, A::AbstractArray) = mightalias(dest, A) ? unaliascopy(A) : A unalias(dest, A::AbstractRange) = A unalias(dest, A) = A """ Base.unaliascopy(A) Make a preventative copy of `A` in an operation where `A` [`Base.mightalias`](@ref) against another array in order to preserve consistent semantics as that other array is mutated. This must return an object of the same type as `A` to preserve optimal performance in the much more common case where aliasing does not occur. By default, `unaliascopy(A::AbstractArray)` will attempt to use [`copy(A)`](@ref), but in cases where `copy(A)` is not a `typeof(A)`, then the array should provide a custom implementation of `Base.unaliascopy(A)`. """ unaliascopy(A::Array) = copy(A) unaliascopy(A::AbstractArray)::typeof(A) = (@noinline; _unaliascopy(A, copy(A))) _unaliascopy(A::T, C::T) where {T} = C _unaliascopy(A, C) = throw(ArgumentError(""" an array of type `$(typename(typeof(A)).wrapper)` shares memory with another argument and must make a preventative copy of itself in order to maintain consistent semantics, but `copy(::$(typeof(A)))` returns a new array of type `$(typeof(C))`. To fix, implement: `Base.unaliascopy(A::$(typename(typeof(A)).wrapper))::typeof(A)`""")) unaliascopy(A) = A """ Base.mightalias(A::AbstractArray, B::AbstractArray) Perform a conservative test to check if arrays `A` and `B` might share the same memory. By default, this simply checks if either of the arrays reference the same memory regions, as identified by their [`Base.dataids`](@ref). """ mightalias(A::AbstractArray, B::AbstractArray) = !isbits(A) && !isbits(B) && !_isdisjoint(dataids(A), dataids(B)) mightalias(x, y) = false _isdisjoint(as::Tuple{}, bs::Tuple{}) = true _isdisjoint(as::Tuple{}, bs::Tuple{UInt}) = true _isdisjoint(as::Tuple{}, bs::Tuple) = true _isdisjoint(as::Tuple{UInt}, bs::Tuple{}) = true _isdisjoint(as::Tuple{UInt}, bs::Tuple{UInt}) = as[1] != bs[1] _isdisjoint(as::Tuple{UInt}, bs::Tuple) = !(as[1] in bs) _isdisjoint(as::Tuple, bs::Tuple{}) = true _isdisjoint(as::Tuple, bs::Tuple{UInt}) = !(bs[1] in as) _isdisjoint(as::Tuple, bs::Tuple) = !(as[1] in bs) && _isdisjoint(tail(as), bs) """ Base.dataids(A::AbstractArray) Return a tuple of `UInt`s that represent the mutable data segments of an array. Custom arrays that would like to opt-in to aliasing detection of their component parts can specialize this method to return the concatenation of the `dataids` of their component parts. A typical definition for an array that wraps a parent is `Base.dataids(C::CustomArray) = dataids(C.parent)`. """ dataids(A::AbstractArray) = (UInt(objectid(A)),) dataids(A::Array) = (UInt(pointer(A)),) dataids(::AbstractRange) = () dataids(x) = () ## get (getindex with a default value) ## RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{AbstractRange, AbstractVector{Int}}}}, AbstractVector{UnitRange{Int}}, AbstractVector{AbstractRange{Int}}, AbstractVector{A}} get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default get(A::AbstractArray, I::Tuple{}, default) = checkbounds(Bool, A) ? A[] : default get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default get(f::Callable, A::AbstractArray, i::Integer) = checkbounds(Bool, A, i) ? A[i] : f() get(f::Callable, A::AbstractArray, I::Tuple{}) = checkbounds(Bool, A) ? A[] : f() get(f::Callable, A::AbstractArray, I::Dims) = checkbounds(Bool, A, I...) ? A[I...] : f() function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T # 1d is not linear indexing ind = findall(in(axes1(A)), I) X[ind] = A[I[ind]] Xind = axes1(X) X[first(Xind):first(ind)-1] = default X[last(ind)+1:last(Xind)] = default X end function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T # Linear indexing ind = findall(in(1:length(A)), I) X[ind] = A[I[ind]] fill!(view(X, 1:first(ind)-1), default) fill!(view(X, last(ind)+1:length(X)), default) X end get(A::AbstractArray, I::AbstractRange, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) function get!(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) where T fill!(X, default) dst, src = indcopy(size(A), I) X[dst...] = A[src...] X end get(A::AbstractArray, I::RangeVecIntList, default) = get!(similar(A, typeof(default), index_shape(I...)), A, I, default) ## structured matrix methods ## replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s replace_in_print_matrix(A::AbstractVector,i::Integer,j::Integer,s::AbstractString) = s ## Concatenation ## eltypeof(x) = typeof(x) eltypeof(x::AbstractArray) = eltype(x) promote_eltypeof() = error() promote_eltypeof(v1) = eltypeof(v1) promote_eltypeof(v1, vs...) = promote_type(eltypeof(v1), promote_eltypeof(vs...)) promote_eltypeof(v1::T, vs::T...) where {T} = eltypeof(v1) promote_eltypeof(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T promote_eltype() = error() promote_eltype(v1) = eltype(v1) promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) promote_eltype(v1::T, vs::T...) where {T} = eltype(T) promote_eltype(v1::AbstractArray{T}, vs::AbstractArray{T}...) where {T} = T #TODO: ERROR CHECK _cat(catdim::Int) = Vector{Any}() typed_vcat(::Type{T}) where {T} = Vector{T}() typed_hcat(::Type{T}) where {T} = Vector{T}() ## cat: special cases vcat(X::T...) where {T} = T[ X[i] for i=1:length(X) ] vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ] hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ] hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ] vcat(X::Number...) = hvcat_fill!(Vector{promote_typeof(X...)}(undef, length(X)), X) hcat(X::Number...) = hvcat_fill!(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X) typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Vector{T}(undef, length(X)), X) typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill!(Matrix{T}(undef, 1,length(X)), X) vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...) vcat(V::AbstractVector{T}...) where {T} = typed_vcat(T, V...) # FIXME: this alias would better be Union{AbstractVector{T}, Tuple{Vararg{T}}} # and method signatures should do AbstractVecOrTuple{<:T} when they want covariance, # but that solution currently fails (see #27188 and #27224) AbstractVecOrTuple{T} = Union{AbstractVector{<:T}, Tuple{Vararg{T}}} _typed_vcat_similar(V, ::Type{T}, n) where T = similar(V[1], T, n) _typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T = _typed_vcat!(_typed_vcat_similar(V, T, sum(map(length, V))), V) function _typed_vcat!(a::AbstractVector{T}, V::AbstractVecOrTuple{AbstractVector}) where T pos = 1 for k=1:Int(length(V))::Int Vk = V[k] p1 = pos + Int(length(Vk))::Int - 1 a[pos:p1] = Vk pos = p1+1 end a end typed_hcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_hcat(T, A) # Catch indexing errors like v[i +1] (instead of v[i+1] or v[i + 1]), where indexing is # interpreted as a typed concatenation. (issue #49676) typed_hcat(::AbstractArray, other...) = throw(ArgumentError("It is unclear whether you \ intend to perform an indexing operation or typed concatenation. If you intend to \ perform indexing (v[1 + 2]), adjust spacing or insert missing operator to clarify. \ If you intend to perform typed concatenation (T[1 2]), ensure that T is a type.")) hcat(A::AbstractVecOrMat...) = typed_hcat(promote_eltype(A...), A...) hcat(A::AbstractVecOrMat{T}...) where {T} = typed_hcat(T, A...) function _typed_hcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T nargs = length(A) nrows = size(A[1], 1) ncols = 0 dense = true for j = 1:nargs Aj = A[j] if size(Aj, 1) != nrows throw(DimensionMismatch("number of rows of each array must match (got $(map(x->size(x,1), A)))")) end dense &= isa(Aj,Array) nd = ndims(Aj) ncols += (nd==2 ? size(Aj,2) : 1) end B = similar(A[1], T, nrows, ncols) pos = 1 if dense for k=1:nargs Ak = A[k] n = length(Ak) copyto!(B, pos, Ak, 1, n) pos += n end else for k=1:nargs Ak = A[k] p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1 B[:, pos:p1] = Ak pos = p1+1 end end return B end vcat(A::AbstractVecOrMat...) = typed_vcat(promote_eltype(A...), A...) vcat(A::AbstractVecOrMat{T}...) where {T} = typed_vcat(T, A...) function _typed_vcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T nargs = length(A) nrows = sum(a->size(a, 1), A)::Int ncols = size(A[1], 2) for j = 2:nargs if size(A[j], 2) != ncols throw(DimensionMismatch("number of columns of each array must match (got $(map(x->size(x,2), A)))")) end end B = similar(A[1], T, nrows, ncols) pos = 1 for k=1:nargs Ak = A[k] p1 = pos+size(Ak,1)::Int-1 B[pos:p1, :] = Ak pos = p1+1 end return B end typed_vcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_vcat(T, A) reduce(::typeof(vcat), A::AbstractVector{<:AbstractVecOrMat}) = _typed_vcat(mapreduce(eltype, promote_type, A), A) reduce(::typeof(hcat), A::AbstractVector{<:AbstractVecOrMat}) = _typed_hcat(mapreduce(eltype, promote_type, A), A) ## cat: general case # helper functions cat_size(A) = (1,) cat_size(A::AbstractArray) = size(A) cat_size(A, d) = 1 cat_size(A::AbstractArray, d) = size(A, d) cat_length(::Any) = 1 cat_length(a::AbstractArray) = length(a) cat_ndims(a) = 0 cat_ndims(a::AbstractArray) = ndims(a) cat_indices(A, d) = OneTo(1) cat_indices(A::AbstractArray, d) = axes(A, d) cat_similar(A, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape) cat_similar(A, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...) cat_similar(A::Array, ::Type{T}, shape::Tuple) where T = Array{T}(undef, shape) cat_similar(A::Array, ::Type{T}, shape::Vector) where T = Array{T}(undef, shape...) cat_similar(A::AbstractArray, T::Type, shape::Tuple) = similar(A, T, shape) cat_similar(A::AbstractArray, T::Type, shape::Vector) = similar(A, T, shape...) # These are for backwards compatibility (even though internal) cat_shape(dims, shape::Tuple{Vararg{Int}}) = shape function cat_shape(dims, shapes::Tuple) out_shape = () for s in shapes out_shape = _cshp(1, dims, out_shape, s) end return out_shape end # The new way to compute the shape (more inferable than combining cat_size & cat_shape, due to Varargs + issue#36454) cat_size_shape(dims) = ntuple(zero, Val(length(dims))) @inline cat_size_shape(dims, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, (), cat_size(X)), tail...) _cat_size_shape(dims, shape) = shape @inline _cat_size_shape(dims, shape, X, tail...) = _cat_size_shape(dims, _cshp(1, dims, shape, cat_size(X)), tail...) _cshp(ndim::Int, ::Tuple{}, ::Tuple{}, ::Tuple{}) = () _cshp(ndim::Int, ::Tuple{}, ::Tuple{}, nshape) = nshape _cshp(ndim::Int, dims, ::Tuple{}, ::Tuple{}) = ntuple(Returns(1), Val(length(dims))) @inline _cshp(ndim::Int, dims, shape, ::Tuple{}) = (shape[1] + dims[1], _cshp(ndim + 1, tail(dims), tail(shape), ())...) @inline _cshp(ndim::Int, dims, ::Tuple{}, nshape) = (nshape[1], _cshp(ndim + 1, tail(dims), (), tail(nshape))...) @inline function _cshp(ndim::Int, ::Tuple{}, shape, ::Tuple{}) _cs(ndim, shape[1], 1) (1, _cshp(ndim + 1, (), tail(shape), ())...) end @inline function _cshp(ndim::Int, ::Tuple{}, shape, nshape) next = _cs(ndim, shape[1], nshape[1]) (next, _cshp(ndim + 1, (), tail(shape), tail(nshape))...) end @inline function _cshp(ndim::Int, dims, shape, nshape) a = shape[1] b = nshape[1] next = dims[1] ? a + b : _cs(ndim, a, b) (next, _cshp(ndim + 1, tail(dims), tail(shape), tail(nshape))...) end _cs(d, a, b) = (a == b ? a : throw(DimensionMismatch( "mismatch in dimension $d (expected $a got $b)"))) dims2cat(::Val{dims}) where dims = dims2cat(dims) function dims2cat(dims) if any(โ‰ค(0), dims) throw(ArgumentError("All cat dimensions must be positive integers, but got $dims")) end ntuple(in(dims), maximum(dims)) end _cat(dims, X...) = _cat_t(dims, promote_eltypeof(X...), X...) @inline function _cat_t(dims, ::Type{T}, X...) where {T} catdims = dims2cat(dims) shape = cat_size_shape(catdims, X...) A = cat_similar(X[1], T, shape) if count(!iszero, catdims)::Int > 1 fill!(A, zero(T)) end return __cat(A, shape, catdims, X...) end # this version of `cat_t` is not very kind for inference and so its usage should be avoided, # nevertheless it is here just for compat after https://github.com/JuliaLang/julia/pull/45028 @inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...) # Why isn't this called `__cat!`? __cat(A, shape, catdims, X...) = __cat_offset!(A, shape, catdims, ntuple(zero, length(shape)), X...) function __cat_offset!(A, shape, catdims, offsets, x, X...) # splitting the "work" on x from X... may reduce latency (fewer costly specializations) newoffsets = __cat_offset1!(A, shape, catdims, offsets, x) return __cat_offset!(A, shape, catdims, newoffsets, X...) end __cat_offset!(A, shape, catdims, offsets) = A function __cat_offset1!(A, shape, catdims, offsets, x) inds = ntuple(length(offsets)) do i (i <= length(catdims) && catdims[i]) ? offsets[i] .+ cat_indices(x, i) : 1:shape[i] end _copy_or_fill!(A, inds, x) newoffsets = ntuple(length(offsets)) do i (i <= length(catdims) && catdims[i]) ? offsets[i] + cat_size(x, i) : offsets[i] end return newoffsets end _copy_or_fill!(A, inds, x) = fill!(view(A, inds...), x) _copy_or_fill!(A, inds, x::AbstractArray) = (A[inds...] = x) """ vcat(A...) Concatenate arrays or numbers vertically. Equivalent to [`cat`](@ref)`(A...; dims=1)`, and to the syntax `[a; b; c]`. To concatenate a large vector of arrays, `reduce(vcat, A)` calls an efficient method when `A isa AbstractVector{<:AbstractVecOrMat}`, rather than working pairwise. See also [`hcat`](@ref), [`Iterators.flatten`](@ref), [`stack`](@ref). # Examples ```jldoctest julia> v = vcat([1,2], [3,4]) 4-element Vector{Int64}: 1 2 3 4 julia> v == vcat(1, 2, [3,4]) # accepts numbers true julia> v == [1; 2; [3,4]] # syntax for the same operation true julia> summary(ComplexF64[1; 2; [3,4]]) # syntax for supplying the element type "4-element Vector{ComplexF64}" julia> vcat(range(1, 2, length=3)) # collects lazy ranges 3-element Vector{Float64}: 1.0 1.5 2.0 julia> two = ([10, 20, 30]', Float64[4 5 6; 7 8 9]) # row vector and a matrix ([10 20 30], [4.0 5.0 6.0; 7.0 8.0 9.0]) julia> vcat(two...) 3ร—3 Matrix{Float64}: 10.0 20.0 30.0 4.0 5.0 6.0 7.0 8.0 9.0 julia> vs = [[1, 2], [3, 4], [5, 6]]; julia> reduce(vcat, vs) # more efficient than vcat(vs...) 6-element Vector{Int64}: 1 2 3 4 5 6 julia> ans == collect(Iterators.flatten(vs)) true ``` """ vcat(X...) = cat(X...; dims=Val(1)) """ hcat(A...) Concatenate arrays or numbers horizontally. Equivalent to [`cat`](@ref)`(A...; dims=2)`, and to the syntax `[a b c]` or `[a;; b;; c]`. For a large vector of arrays, `reduce(hcat, A)` calls an efficient method when `A isa AbstractVector{<:AbstractVecOrMat}`. For a vector of vectors, this can also be written [`stack`](@ref)`(A)`. See also [`vcat`](@ref), [`hvcat`](@ref). # Examples ```jldoctest julia> hcat([1,2], [3,4], [5,6]) 2ร—3 Matrix{Int64}: 1 3 5 2 4 6 julia> hcat(1, 2, [30 40], [5, 6, 7]') # accepts numbers 1ร—7 Matrix{Int64}: 1 2 30 40 5 6 7 julia> ans == [1 2 [30 40] [5, 6, 7]'] # syntax for the same operation true julia> Float32[1 2 [30 40] [5, 6, 7]'] # syntax for supplying the eltype 1ร—7 Matrix{Float32}: 1.0 2.0 30.0 40.0 5.0 6.0 7.0 julia> ms = [zeros(2,2), [1 2; 3 4], [50 60; 70 80]]; julia> reduce(hcat, ms) # more efficient than hcat(ms...) 2ร—6 Matrix{Float64}: 0.0 0.0 1.0 2.0 50.0 60.0 0.0 0.0 3.0 4.0 70.0 80.0 julia> stack(ms) |> summary # disagrees on a vector of matrices "2ร—2ร—3 Array{Float64, 3}" julia> hcat(Int[], Int[], Int[]) # empty vectors, each of size (0,) 0ร—3 Matrix{Int64} julia> hcat([1.1, 9.9], Matrix(undef, 2, 0)) # hcat with empty 2ร—0 Matrix 2ร—1 Matrix{Any}: 1.1 9.9 ``` """ hcat(X...) = cat(X...; dims=Val(2)) typed_vcat(::Type{T}, X...) where T = _cat_t(Val(1), T, X...) typed_hcat(::Type{T}, X...) where T = _cat_t(Val(2), T, X...) """ cat(A...; dims) Concatenate the input arrays along the dimensions specified in `dims`. Along a dimension `d in dims`, the size of the output array is `sum(size(a,d) for a in A)`. Along other dimensions, all input arrays should have the same size, which will also be the size of the output array along those dimensions. If `dims` is a single number, the different arrays are tightly packed along that dimension. If `dims` is an iterable containing several dimensions, the positions along these dimensions are increased simultaneously for each input array, filling with zero elsewhere. This allows one to construct block-diagonal matrices as `cat(matrices...; dims=(1,2))`, and their higher-dimensional analogues. The special case `dims=1` is [`vcat`](@ref), and `dims=2` is [`hcat`](@ref). See also [`hvcat`](@ref), [`hvncat`](@ref), [`stack`](@ref), [`repeat`](@ref). The keyword also accepts `Val(dims)`. !!! compat "Julia 1.8" For multiple dimensions `dims = Val(::Tuple)` was added in Julia 1.8. # Examples ```jldoctest julia> cat([1 2; 3 4], [pi, pi], fill(10, 2,3,1); dims=2) # same as hcat 2ร—6ร—1 Array{Float64, 3}: [:, :, 1] = 1.0 2.0 3.14159 10.0 10.0 10.0 3.0 4.0 3.14159 10.0 10.0 10.0 julia> cat(true, trues(2,2), trues(4)', dims=(1,2)) # block-diagonal 4ร—7 Matrix{Bool}: 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 1 1 julia> cat(1, [2], [3;;]; dims=Val(2)) 1ร—3 Matrix{Int64}: 1 2 3 ``` """ @inline cat(A...; dims) = _cat(dims, A...) # `@constprop :aggressive` allows `catdims` to be propagated as constant improving return type inference @constprop :aggressive _cat(catdims, A::AbstractArray{T}...) where {T} = _cat_t(catdims, T, A...) # The specializations for 1 and 2 inputs are important # especially when running with --inline=no, see #11158 vcat(A::AbstractArray) = cat(A; dims=Val(1)) vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1)) vcat(A::AbstractArray...) = cat(A...; dims=Val(1)) vcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(1)) hcat(A::AbstractArray) = cat(A; dims=Val(2)) hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2)) hcat(A::AbstractArray...) = cat(A...; dims=Val(2)) hcat(A::Union{AbstractArray,Number}...) = cat(A...; dims=Val(2)) typed_vcat(T::Type, A::AbstractArray) = _cat_t(Val(1), T, A) typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(1), T, A, B) typed_vcat(T::Type, A::AbstractArray...) = _cat_t(Val(1), T, A...) typed_hcat(T::Type, A::AbstractArray) = _cat_t(Val(2), T, A) typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = _cat_t(Val(2), T, A, B) typed_hcat(T::Type, A::AbstractArray...) = _cat_t(Val(2), T, A...) # 2d horizontal and vertical concatenation # these are produced in lowering if splatting occurs inside hvcat hvcat_rows(rows::Tuple...) = hvcat(map(length, rows), (rows...)...) typed_hvcat_rows(T::Type, rows::Tuple...) = typed_hvcat(T, map(length, rows), (rows...)...) function hvcat(nbc::Int, as...) # nbc = # of block columns n = length(as) mod(n,nbc) != 0 && throw(ArgumentError("number of arrays $n is not a multiple of the requested number of block columns $nbc")) nbr = div(n,nbc) hvcat(ntuple(Returns(nbc), nbr), as...) end """ hvcat(blocks_per_row::Union{Tuple{Vararg{Int}}, Int}, values...) Horizontal and vertical concatenation in one call. This function is called for block matrix syntax. The first argument specifies the number of arguments to concatenate in each block row. If the first argument is a single integer `n`, then all block rows are assumed to have `n` block columns. # Examples ```jldoctest julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 (1, 2, 3, 4, 5, 6) julia> [a b c; d e f] 2ร—3 Matrix{Int64}: 1 2 3 4 5 6 julia> hvcat((3,3), a,b,c,d,e,f) 2ร—3 Matrix{Int64}: 1 2 3 4 5 6 julia> [a b; c d; e f] 3ร—2 Matrix{Int64}: 1 2 3 4 5 6 julia> hvcat((2,2,2), a,b,c,d,e,f) 3ร—2 Matrix{Int64}: 1 2 3 4 5 6 julia> hvcat((2,2,2), a,b,c,d,e,f) == hvcat(2, a,b,c,d,e,f) true ``` """ hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray...) = typed_hvcat(promote_eltype(xs...), rows, xs...) hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractArray{T}...) where {T} = typed_hvcat(T, rows, xs...) function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as::AbstractVecOrMat...) where T nbr = length(rows) # number of block rows nc = 0 for i=1:rows[1] nc += size(as[i],2) end nr = 0 a = 1 for i = 1:nbr nr += size(as[a],1) a += rows[i] end out = similar(as[1], T, nr, nc) a = 1 r = 1 for i = 1:nbr c = 1 szi = size(as[a],1) for j = 1:rows[i] Aj = as[a+j-1] szj = size(Aj,2) if size(Aj,1) != szi throw(DimensionMismatch("mismatched height in block row $(i) (expected $szi, got $(size(Aj,1)))")) end if c-1+szj > nc throw(DimensionMismatch("block row $(i) has mismatched number of columns (expected $nc, got $(c-1+szj))")) end out[r:r-1+szi, c:c-1+szj] = Aj c += szj end if c != nc+1 throw(DimensionMismatch("block row $(i) has mismatched number of columns (expected $nc, got $(c-1))")) end r += szi a += rows[i] end out end hvcat(rows::Tuple{Vararg{Int}}) = [] typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}) where {T} = Vector{T}() function hvcat(rows::Tuple{Vararg{Int}}, xs::T...) where T<:Number nr = length(rows) nc = rows[1] a = Matrix{T}(undef, nr, nc) if length(a) != length(xs) throw(ArgumentError("argument count does not match specified shape (expected $(length(a)), got $(length(xs)))")) end k = 1 @inbounds for i=1:nr if nc != rows[i] throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) end for j=1:nc a[i,j] = xs[k] k += 1 end end a end function hvcat_fill!(a::Array, xs::Tuple) nr, nc = size(a,1), size(a,2) len = length(xs) if nr*nc != len throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))")) end k = 1 for i=1:nr @inbounds for j=1:nc a[i,j] = xs[k] k += 1 end end a end hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...) hvcat(rows::Tuple{Vararg{Int}}, xs...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) # the following method is needed to provide a more specific one compared to LinearAlgebra/uniformscaling.jl hvcat(rows::Tuple{Vararg{Int}}, xs::Union{AbstractArray,Number}...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) where T nr = length(rows) nc = rows[1] for i = 2:nr if nc != rows[i] throw(DimensionMismatch("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) end end hvcat_fill!(Matrix{T}(undef, nr, nc), xs) end function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as...) where T nbr = length(rows) # number of block rows rs = Vector{Any}(undef, nbr) a = 1 for i = 1:nbr rs[i] = typed_hcat(T, as[a:a-1+rows[i]]...) a += rows[i] end T[rs...;] end ## N-dimensional concatenation ## """ hvncat(dim::Int, row_first, values...) hvncat(dims::Tuple{Vararg{Int}}, row_first, values...) hvncat(shape::Tuple{Vararg{Tuple}}, row_first, values...) Horizontal, vertical, and n-dimensional concatenation of many `values` in one call. This function is called for block matrix syntax. The first argument either specifies the shape of the concatenation, similar to `hvcat`, as a tuple of tuples, or the dimensions that specify the key number of elements along each axis, and is used to determine the output dimensions. The `dims` form is more performant, and is used by default when the concatenation operation has the same number of elements along each axis (e.g., [a b; c d;;; e f ; g h]). The `shape` form is used when the number of elements along each axis is unbalanced (e.g., [a b ; c]). Unbalanced syntax needs additional validation overhead. The `dim` form is an optimization for concatenation along just one dimension. `row_first` indicates how `values` are ordered. The meaning of the first and second elements of `shape` are also swapped based on `row_first`. # Examples ```jldoctest julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 (1, 2, 3, 4, 5, 6) julia> [a b c;;; d e f] 1ร—3ร—2 Array{Int64, 3}: [:, :, 1] = 1 2 3 [:, :, 2] = 4 5 6 julia> hvncat((2,1,3), false, a,b,c,d,e,f) 2ร—1ร—3 Array{Int64, 3}: [:, :, 1] = 1 2 [:, :, 2] = 3 4 [:, :, 3] = 5 6 julia> [a b;;; c d;;; e f] 1ร—2ร—3 Array{Int64, 3}: [:, :, 1] = 1 2 [:, :, 2] = 3 4 [:, :, 3] = 5 6 julia> hvncat(((3, 3), (3, 3), (6,)), true, a, b, c, d, e, f) 1ร—3ร—2 Array{Int64, 3}: [:, :, 1] = 1 2 3 [:, :, 2] = 4 5 6 ``` # Examples for construction of the arguments ``` [a b c ; d e f ;;; g h i ; j k l ;;; m n o ; p q r ;;; s t u ; v w x] โ‡’ dims = (2, 3, 4) [a b ; c ;;; d ;;;;] ___ _ _ 2 1 1 = elements in each row (2, 1, 1) _______ _ 3 1 = elements in each column (3, 1) _____________ 4 = elements in each 3d slice (4,) _____________ 4 = elements in each 4d slice (4,) โ‡’ shape = ((2, 1, 1), (3, 1), (4,), (4,)) with `row_first` = true ``` """ hvncat(dimsshape::Tuple, row_first::Bool, xs...) = _hvncat(dimsshape, row_first, xs...) hvncat(dim::Int, xs...) = _hvncat(dim, true, xs...) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool) = _typed_hvncat(Any, dimsshape, row_first) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs...) = _typed_hvncat(promote_eltypeof(xs...), dimsshape, row_first, xs...) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::T...) where T<:Number = _typed_hvncat(T, dimsshape, row_first, xs...) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::Number...) = _typed_hvncat(promote_typeof(xs...), dimsshape, row_first, xs...) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray...) = _typed_hvncat(promote_eltype(xs...), dimsshape, row_first, xs...) _hvncat(dimsshape::Union{Tuple, Int}, row_first::Bool, xs::AbstractArray{T}...) where T = _typed_hvncat(T, dimsshape, row_first, xs...) typed_hvncat(T::Type, dimsshape::Tuple, row_first::Bool, xs...) = _typed_hvncat(T, dimsshape, row_first, xs...) typed_hvncat(T::Type, dim::Int, xs...) = _typed_hvncat(T, Val(dim), xs...) # 1-dimensional hvncat methods _typed_hvncat(::Type, ::Val{0}) = _typed_hvncat_0d_only_one() _typed_hvncat(T::Type, ::Val{0}, x) = fill(convert(T, x)) _typed_hvncat(T::Type, ::Val{0}, x::Number) = fill(convert(T, x)) _typed_hvncat(T::Type, ::Val{0}, x::AbstractArray) = convert.(T, x) _typed_hvncat(::Type, ::Val{0}, ::Any...) = _typed_hvncat_0d_only_one() _typed_hvncat(::Type, ::Val{0}, ::Number...) = _typed_hvncat_0d_only_one() _typed_hvncat(::Type, ::Val{0}, ::AbstractArray...) = _typed_hvncat_0d_only_one() _typed_hvncat_0d_only_one() = throw(ArgumentError("a 0-dimensional array may only contain exactly one element")) # `@constprop :aggressive` here to form constant `Val(dim)` type to get type stability @constprop :aggressive _typed_hvncat(T::Type, dim::Int, ::Bool, xs...) = _typed_hvncat(T, Val(dim), xs...) # catches from _hvncat type promoters function _typed_hvncat(::Type{T}, ::Val{N}) where {T, N} N < 0 && throw(ArgumentError("concatenation dimension must be nonnegative")) return Array{T, N}(undef, ntuple(x -> 0, Val(N))) end function _typed_hvncat(T::Type, ::Val{N}, xs::Number...) where N N < 0 && throw(ArgumentError("concatenation dimension must be nonnegative")) A = cat_similar(xs[1], T, (ntuple(x -> 1, Val(N - 1))..., length(xs))) hvncat_fill!(A, false, xs) return A end function _typed_hvncat(::Type{T}, ::Val{N}, as::AbstractArray...) where {T, N} # optimization for arrays that can be concatenated by copying them linearly into the destination # conditions: the elements must all have 1-length dimensions above N length(as) > 0 || throw(ArgumentError("must have at least one element")) N < 0 && throw(ArgumentError("concatenation dimension must be nonnegative")) for a โˆˆ as ndims(a) <= N || all(x -> size(a, x) == 1, (N + 1):ndims(a)) || return _typed_hvncat(T, (ntuple(x -> 1, Val(N - 1))..., length(as), 1), false, as...) # the extra 1 is to avoid an infinite cycle end nd = N Ndim = 0 for i โˆˆ eachindex(as) Ndim += cat_size(as[i], N) nd = max(nd, cat_ndims(as[i])) for d โˆˆ 1:N - 1 cat_size(as[1], d) == cat_size(as[i], d) || throw(DimensionMismatch("mismatched size along axis $d in element $i")) end end A = cat_similar(as[1], T, (ntuple(d -> size(as[1], d), N - 1)..., Ndim, ntuple(x -> 1, nd - N)...)) k = 1 for a โˆˆ as for i โˆˆ eachindex(a) A[k] = a[i] k += 1 end end return A end function _typed_hvncat(::Type{T}, ::Val{N}, as...) where {T, N} length(as) > 0 || throw(ArgumentError("must have at least one element")) N < 0 && throw(ArgumentError("concatenation dimension must be nonnegative")) nd = N Ndim = 0 for i โˆˆ eachindex(as) Ndim += cat_size(as[i], N) nd = max(nd, cat_ndims(as[i])) for d โˆˆ 1:N-1 cat_size(as[i], d) == 1 || throw(DimensionMismatch("all dimensions of element $i other than $N must be of length 1")) end end A = Array{T, nd}(undef, ntuple(x -> 1, Val(N - 1))..., Ndim, ntuple(x -> 1, nd - N)...) k = 1 for a โˆˆ as if a isa AbstractArray lena = length(a) copyto!(A, k, a, 1, lena) k += lena else A[k] = a k += 1 end end return A end # 0-dimensional cases for balanced and unbalanced hvncat method _typed_hvncat(T::Type, ::Tuple{}, ::Bool, x...) = _typed_hvncat(T, Val(0), x...) _typed_hvncat(T::Type, ::Tuple{}, ::Bool, x::Number...) = _typed_hvncat(T, Val(0), x...) # balanced dimensions hvncat methods _typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as...) = _typed_hvncat_1d(T, dims[1], Val(false), as...) _typed_hvncat(T::Type, dims::Tuple{Int}, ::Bool, as::Number...) = _typed_hvncat_1d(T, dims[1], Val(false), as...) function _typed_hvncat_1d(::Type{T}, ds::Int, ::Val{row_first}, as...) where {T, row_first} lengthas = length(as) ds > 0 || throw(ArgumentError("`dimsshape` argument must consist of positive integers")) lengthas == ds || throw(ArgumentError("number of elements does not match `dimshape` argument; expected $ds, got $lengthas")) if row_first return _typed_hvncat(T, Val(2), as...) else return _typed_hvncat(T, Val(1), as...) end end function _typed_hvncat(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, xs::Number...) where {T, N} all(>(0), dims) || throw(ArgumentError("`dims` argument must contain positive integers")) A = Array{T, N}(undef, dims...) lengtha = length(A) # Necessary to store result because throw blocks are being deoptimized right now, which leads to excessive allocations lengthx = length(xs) # Cuts from 3 allocations to 1. if lengtha != lengthx throw(ArgumentError("argument count does not match specified shape (expected $lengtha, got $lengthx)")) end hvncat_fill!(A, row_first, xs) return A end function hvncat_fill!(A::Array, row_first::Bool, xs::Tuple) nr, nc = size(A, 1), size(A, 2) na = prod(size(A)[3:end]) len = length(xs) nrc = nr * nc if nrc * na != len throw(ArgumentError("argument count $(len) does not match specified shape $(size(A))")) end # putting these in separate functions leads to unnecessary allocations if row_first k = 1 for d โˆˆ 1:na dd = nrc * (d - 1) for i โˆˆ 1:nr Ai = dd + i for j โˆˆ 1:nc @inbounds A[Ai] = xs[k] k += 1 Ai += nr end end end else for k โˆˆ eachindex(xs) @inbounds A[k] = xs[k] end end end function _typed_hvncat(T::Type, dims::NTuple{N, Int}, row_first::Bool, as...) where {N} # function barrier after calculating the max is necessary for high performance nd = max(maximum(cat_ndims(a) for a โˆˆ as), N) return _typed_hvncat_dims(T, (dims..., ntuple(x -> 1, nd - N)...), row_first, as) end function _typed_hvncat_dims(::Type{T}, dims::NTuple{N, Int}, row_first::Bool, as::Tuple) where {T, N} length(as) > 0 || throw(ArgumentError("must have at least one element")) all(>(0), dims) || throw(ArgumentError("`dims` argument must contain positive integers")) d1 = row_first ? 2 : 1 d2 = row_first ? 1 : 2 outdims = zeros(Int, N) # validate shapes for lowest level of concatenation d = findfirst(>(1), dims) if d !== nothing # all dims are 1 if row_first && d < 3 d = d == 1 ? 2 : 1 end nblocks = length(as) รท dims[d] for b โˆˆ 1:nblocks offset = ((b - 1) * dims[d]) startelementi = offset + 1 for i โˆˆ offset .+ (2:dims[d]) for dd โˆˆ 1:N dd == d && continue if cat_size(as[startelementi], dd) != cat_size(as[i], dd) throw(DimensionMismatch("incompatible shape in element $i")) end end end end end # discover number of rows or columns for i โˆˆ 1:dims[d1] outdims[d1] += cat_size(as[i], d1) end currentdims = zeros(Int, N) blockcount = 0 elementcount = 0 for i โˆˆ eachindex(as) elementcount += cat_length(as[i]) currentdims[d1] += cat_size(as[i], d1) if currentdims[d1] == outdims[d1] currentdims[d1] = 0 for d โˆˆ (d2, 3:N...) currentdims[d] += cat_size(as[i], d) if outdims[d] == 0 # unfixed dimension blockcount += 1 if blockcount == dims[d] outdims[d] = currentdims[d] currentdims[d] = 0 blockcount = 0 else break end else # fixed dimension if currentdims[d] == outdims[d] # end of dimension currentdims[d] = 0 elseif currentdims[d] < outdims[d] # dimension in progress break else # exceeded dimension throw(DimensionMismatch("argument $i has too many elements along axis $d")) end end end elseif currentdims[d1] > outdims[d1] # exceeded dimension throw(DimensionMismatch("argument $i has too many elements along axis $d1")) end end outlen = prod(outdims) elementcount == outlen || throw(DimensionMismatch("mismatched number of elements; expected $(outlen), got $(elementcount)")) # copy into final array A = cat_similar(as[1], T, outdims) # @assert all(==(0), currentdims) outdims .= 0 hvncat_fill!(A, currentdims, outdims, d1, d2, as) return A end # unbalanced dimensions hvncat methods function _typed_hvncat(T::Type, shape::Tuple{Tuple}, row_first::Bool, xs...) length(shape[1]) > 0 || throw(ArgumentError("each level of `shape` argument must have at least one value")) return _typed_hvncat_1d(T, shape[1][1], Val(row_first), xs...) end function _typed_hvncat(T::Type, shape::NTuple{N, Tuple}, row_first::Bool, as...) where {N} # function barrier after calculating the max is necessary for high performance nd = max(maximum(cat_ndims(a) for a โˆˆ as), N) return _typed_hvncat_shape(T, (shape..., ntuple(x -> shape[end], nd - N)...), row_first, as) end function _typed_hvncat_shape(::Type{T}, shape::NTuple{N, Tuple}, row_first, as::Tuple) where {T, N} length(as) > 0 || throw(ArgumentError("must have at least one element")) all(>(0), tuple((shape...)...)) || throw(ArgumentError("`shape` argument must consist of positive integers")) d1 = row_first ? 2 : 1 d2 = row_first ? 1 : 2 shapev = collect(shape) # saves allocations later all(!isempty, shapev) || throw(ArgumentError("each level of `shape` argument must have at least one value")) length(shapev[end]) == 1 || throw(ArgumentError("last level of shape must contain only one integer")) shapelength = shapev[end][1] lengthas = length(as) shapelength == lengthas || throw(ArgumentError("number of elements does not match shape; expected $(shapelength), got $lengthas)")) # discover dimensions nd = max(N, cat_ndims(as[1])) outdims = fill(-1, nd) currentdims = zeros(Int, nd) blockcounts = zeros(Int, nd) shapepos = ones(Int, nd) elementcount = 0 for i โˆˆ eachindex(as) elementcount += cat_length(as[i]) wasstartblock = false for d โˆˆ 1:N ad = (d < 3 && row_first) ? (d == 1 ? 2 : 1) : d dsize = cat_size(as[i], ad) blockcounts[d] += 1 if d == 1 || i == 1 || wasstartblock currentdims[d] += dsize elseif dsize != cat_size(as[i - 1], ad) throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \ expected $(cat_size(as[i - 1], ad)), got $dsize")) end wasstartblock = blockcounts[d] == 1 # remember for next dimension isendblock = blockcounts[d] == shapev[d][shapepos[d]] if isendblock if outdims[d] == -1 outdims[d] = currentdims[d] elseif outdims[d] != currentdims[d] throw(DimensionMismatch("argument $i has a mismatched number of elements along axis $ad; \ expected $(abs(outdims[d] - (currentdims[d] - dsize))), got $dsize")) end currentdims[d] = 0 blockcounts[d] = 0 shapepos[d] += 1 d > 1 && (blockcounts[d - 1] == 0 || throw(DimensionMismatch("shape in level $d is inconsistent; level counts must nest \ evenly into each other"))) end end end outlen = prod(outdims) elementcount == outlen || throw(ArgumentError("mismatched number of elements; expected $(outlen), got $(elementcount)")) if row_first outdims[1], outdims[2] = outdims[2], outdims[1] end # @assert all(==(0), currentdims) # @assert all(==(0), blockcounts) # copy into final array A = cat_similar(as[1], T, outdims) hvncat_fill!(A, currentdims, blockcounts, d1, d2, as) return A end function hvncat_fill!(A::AbstractArray{T, N}, scratch1::Vector{Int}, scratch2::Vector{Int}, d1::Int, d2::Int, as::Tuple) where {T, N} N > 1 || throw(ArgumentError("dimensions of the destination array must be at least 2")) length(scratch1) == length(scratch2) == N || throw(ArgumentError("scratch vectors must have as many elements as the destination array has dimensions")) 0 < d1 < 3 && 0 < d2 < 3 && d1 != d2 || throw(ArgumentError("d1 and d2 must be either 1 or 2, exclusive.")) outdims = size(A) offsets = scratch1 inneroffsets = scratch2 for a โˆˆ as if isa(a, AbstractArray) for ai โˆˆ a @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N) A[Ai] = ai @inbounds for j โˆˆ 1:N inneroffsets[j] += 1 inneroffsets[j] < cat_size(a, j) && break inneroffsets[j] = 0 end end else @inbounds Ai = hvncat_calcindex(offsets, inneroffsets, outdims, N) A[Ai] = a end @inbounds for j โˆˆ (d1, d2, 3:N...) offsets[j] += cat_size(a, j) offsets[j] < outdims[j] && break offsets[j] = 0 end end end @propagate_inbounds function hvncat_calcindex(offsets::Vector{Int}, inneroffsets::Vector{Int}, outdims::Tuple{Vararg{Int}}, nd::Int) Ai = inneroffsets[1] + offsets[1] + 1 for j โˆˆ 2:nd increment = inneroffsets[j] + offsets[j] for k โˆˆ 1:j-1 increment *= outdims[k] end Ai += increment end Ai end """ stack(iter; [dims]) Combine a collection of arrays (or other iterable objects) of equal size into one larger array, by arranging them along one or more new dimensions. By default the axes of the elements are placed first, giving `size(result) = (size(first(iter))..., size(iter)...)`. This has the same order of elements as [`Iterators.flatten`](@ref)`(iter)`. With keyword `dims::Integer`, instead the `i`th element of `iter` becomes the slice [`selectdim`](@ref)`(result, dims, i)`, so that `size(result, dims) == length(iter)`. In this case `stack` reverses the action of [`eachslice`](@ref) with the same `dims`. The various [`cat`](@ref) functions also combine arrays. However, these all extend the arrays' existing (possibly trivial) dimensions, rather than placing the arrays along new dimensions. They also accept arrays as separate arguments, rather than a single collection. !!! compat "Julia 1.9" This function requires at least Julia 1.9. # Examples ```jldoctest julia> vecs = (1:2, [30, 40], Float32[500, 600]); julia> mat = stack(vecs) 2ร—3 Matrix{Float32}: 1.0 30.0 500.0 2.0 40.0 600.0 julia> mat == hcat(vecs...) == reduce(hcat, collect(vecs)) true julia> vec(mat) == vcat(vecs...) == reduce(vcat, collect(vecs)) true julia> stack(zip(1:4, 10:99)) # accepts any iterators of iterators 2ร—4 Matrix{Int64}: 1 2 3 4 10 11 12 13 julia> vec(ans) == collect(Iterators.flatten(zip(1:4, 10:99))) true julia> stack(vecs; dims=1) # unlike any cat function, 1st axis of vecs[1] is 2nd axis of result 3ร—2 Matrix{Float32}: 1.0 2.0 30.0 40.0 500.0 600.0 julia> x = rand(3,4); julia> x == stack(eachcol(x)) == stack(eachrow(x), dims=1) # inverse of eachslice true ``` Higher-dimensional examples: ```jldoctest julia> A = rand(5, 7, 11); julia> E = eachslice(A, dims=2); # a vector of matrices julia> (element = size(first(E)), container = size(E)) (element = (5, 11), container = (7,)) julia> stack(E) |> size (5, 11, 7) julia> stack(E) == stack(E; dims=3) == cat(E...; dims=3) true julia> A == stack(E; dims=2) true julia> M = (fill(10i+j, 2, 3) for i in 1:5, j in 1:7); julia> (element = size(first(M)), container = size(M)) (element = (2, 3), container = (5, 7)) julia> stack(M) |> size # keeps all dimensions (2, 3, 5, 7) julia> stack(M; dims=1) |> size # vec(container) along dims=1 (35, 2, 3) julia> hvcat(5, M...) |> size # hvcat puts matrices next to each other (14, 15) ``` """ stack(iter; dims=:) = _stack(dims, iter) """ stack(f, args...; [dims]) Apply a function to each element of a collection, and `stack` the result. Or to several collections, [`zip`](@ref)ped together. The function should return arrays (or tuples, or other iterators) all of the same size. These become slices of the result, each separated along `dims` (if given) or by default along the last dimensions. See also [`mapslices`](@ref), [`eachcol`](@ref). # Examples ```jldoctest julia> stack(c -> (c, c-32), "julia") 2ร—5 Matrix{Char}: 'j' 'u' 'l' 'i' 'a' 'J' 'U' 'L' 'I' 'A' julia> stack(eachrow([1 2 3; 4 5 6]), (10, 100); dims=1) do row, n vcat(row, row .* n, row ./ n) end 2ร—9 Matrix{Float64}: 1.0 2.0 3.0 10.0 20.0 30.0 0.1 0.2 0.3 4.0 5.0 6.0 400.0 500.0 600.0 0.04 0.05 0.06 ``` """ stack(f, iter; dims=:) = _stack(dims, f(x) for x in iter) stack(f, xs, yzs...; dims=:) = _stack(dims, f(xy...) for xy in zip(xs, yzs...)) _stack(dims::Union{Integer, Colon}, iter) = _stack(dims, IteratorSize(iter), iter) _stack(dims, ::IteratorSize, iter) = _stack(dims, collect(iter)) function _stack(dims, ::Union{HasShape, HasLength}, iter) S = @default_eltype iter T = S != Union{} ? eltype(S) : Any # Union{} occurs for e.g. stack(1,2), postpone the error if isconcretetype(T) _typed_stack(dims, T, S, iter) else # Need to look inside, but shouldn't run an expensive iterator twice: array = iter isa Union{Tuple, AbstractArray} ? iter : collect(iter) isempty(array) && return _empty_stack(dims, T, S, iter) T2 = mapreduce(eltype, promote_type, array) _typed_stack(dims, T2, eltype(array), array) end end function _typed_stack(::Colon, ::Type{T}, ::Type{S}, A, Aax=_iterator_axes(A)) where {T, S} xit = iterate(A) nothing === xit && return _empty_stack(:, T, S, A) x1, _ = xit ax1 = _iterator_axes(x1) B = similar(_ensure_array(x1), T, ax1..., Aax...) off = firstindex(B) len = length(x1) while xit !== nothing x, state = xit _stack_size_check(x, ax1) copyto!(B, off, x) off += len xit = iterate(A, state) end B end _iterator_axes(x) = _iterator_axes(x, IteratorSize(x)) _iterator_axes(x, ::HasLength) = (OneTo(length(x)),) _iterator_axes(x, ::IteratorSize) = axes(x) # For some dims values, stack(A; dims) == stack(vec(A)), and the : path will be faster _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} = _typed_stack(dims, T, S, IteratorSize(S), A) _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasLength, A) where {T,S} = _typed_stack(dims, T, S, HasShape{1}(), A) function _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::HasShape{N}, A) where {T,S,N} if dims == N+1 _typed_stack(:, T, S, A, (_vec_axis(A),)) else _dim_stack(dims, T, S, A) end end _typed_stack(dims::Integer, ::Type{T}, ::Type{S}, ::IteratorSize, A) where {T,S} = _dim_stack(dims, T, S, A) _vec_axis(A, ax=_iterator_axes(A)) = length(ax) == 1 ? only(ax) : OneTo(prod(length, ax; init=1)) @constprop :aggressive function _dim_stack(dims::Integer, ::Type{T}, ::Type{S}, A) where {T,S} xit = Iterators.peel(A) nothing === xit && return _empty_stack(dims, T, S, A) x1, xrest = xit ax1 = _iterator_axes(x1) N1 = length(ax1)+1 dims in 1:N1 || throw(ArgumentError(LazyString("cannot stack slices ndims(x) = ", N1-1, " along dims = ", dims))) newaxis = _vec_axis(A) outax = ntuple(d -> d==dims ? newaxis : ax1[d - (d>dims)], N1) B = similar(_ensure_array(x1), T, outax...) if dims == 1 _dim_stack!(Val(1), B, x1, xrest) elseif dims == 2 _dim_stack!(Val(2), B, x1, xrest) else _dim_stack!(Val(dims), B, x1, xrest) end B end function _dim_stack!(::Val{dims}, B::AbstractArray, x1, xrest) where {dims} before = ntuple(d -> Colon(), dims - 1) after = ntuple(d -> Colon(), ndims(B) - dims) i = firstindex(B, dims) copyto!(view(B, before..., i, after...), x1) for x in xrest _stack_size_check(x, _iterator_axes(x1)) i += 1 @inbounds copyto!(view(B, before..., i, after...), x) end end @inline function _stack_size_check(x, ax1::Tuple) if _iterator_axes(x) != ax1 uax1 = map(UnitRange, ax1) uaxN = map(UnitRange, axes(x)) throw(DimensionMismatch( LazyString("stack expects uniform slices, got axes(x) == ", uaxN, " while first had ", uax1))) end end _ensure_array(x::AbstractArray) = x _ensure_array(x) = 1:0 # passed to similar, makes stack's output an Array _empty_stack(_...) = throw(ArgumentError("`stack` on an empty collection is not allowed")) ## Reductions and accumulates ## function isequal(A::AbstractArray, B::AbstractArray) if A === B return true end if axes(A) != axes(B) return false end for (a, b) in zip(A, B) if !isequal(a, b) return false end end return true end function cmp(A::AbstractVector, B::AbstractVector) for (a, b) in zip(A, B) if !isequal(a, b) return isless(a, b) ? -1 : 1 end end return cmp(length(A), length(B)) end """ isless(A::AbstractVector, B::AbstractVector) Return `true` when `A` is less than `B` in lexicographic order. """ isless(A::AbstractVector, B::AbstractVector) = cmp(A, B) < 0 function (==)(A::AbstractArray, B::AbstractArray) if axes(A) != axes(B) return false end anymissing = false for (a, b) in zip(A, B) eq = (a == b) if ismissing(eq) anymissing = true elseif !eq return false end end return anymissing ? missing : true end # _sub2ind and _ind2sub # fallbacks function _sub2ind(A::AbstractArray, I...) @inline _sub2ind(axes(A), I...) end function _ind2sub(A::AbstractArray, ind) @inline _ind2sub(axes(A), ind) end # 0-dimensional arrays and indexing with [] _sub2ind(::Tuple{}) = 1 _sub2ind(::DimsInteger) = 1 _sub2ind(::Indices) = 1 _sub2ind(::Tuple{}, I::Integer...) = (@inline; _sub2ind_recurse((), 1, 1, I...)) # Generic cases _sub2ind(dims::DimsInteger, I::Integer...) = (@inline; _sub2ind_recurse(dims, 1, 1, I...)) _sub2ind(inds::Indices, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...)) # In 1d, there's a question of whether we're doing cartesian indexing # or linear indexing. Support only the former. _sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) _sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@inline; _sub2ind_recurse(inds, 1, 1, I...)) # only OneTo is safe _sub2ind(inds::Tuple{OneTo}, i::Integer) = i _sub2ind_recurse(::Any, L, ind) = ind function _sub2ind_recurse(::Tuple{}, L, ind, i::Integer, I::Integer...) @inline _sub2ind_recurse((), L, ind+(i-1)*L, I...) end function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...) @inline r1 = inds[1] _sub2ind_recurse(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) end nextL(L, l::Integer) = L*l nextL(L, r::AbstractUnitRange) = L*length(r) nextL(L, r::Slice) = L*length(r.indices) offsetin(i, l::Integer) = i-1 offsetin(i, r::AbstractUnitRange) = i-first(r) _ind2sub(::Tuple{}, ind::Integer) = (@inline; ind == 1 ? () : throw(BoundsError())) _ind2sub(dims::DimsInteger, ind::Integer) = (@inline; _ind2sub_recurse(dims, ind-1)) _ind2sub(inds::Indices, ind::Integer) = (@inline; _ind2sub_recurse(inds, ind-1)) _ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) _ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) _ind2sub_recurse(::Tuple{}, ind) = (ind+1,) function _ind2sub_recurse(indslast::NTuple{1}, ind) @inline (_lookup(ind, indslast[1]),) end function _ind2sub_recurse(inds, ind) @inline r1 = inds[1] indnext, f, l = _div(ind, r1) (ind-l*indnext+f, _ind2sub_recurse(tail(inds), indnext)...) end _lookup(ind, d::Integer) = ind+1 _lookup(ind, r::AbstractUnitRange) = ind+first(r) _div(ind, d::Integer) = div(ind, d), 1, d _div(ind, r::AbstractUnitRange) = (d = length(r); (div(ind, d), first(r), d)) # Vectorized forms function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) end _sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = _sub2ind_vecs(inds, I1, I...) _sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = _sub2ind_vecs(inds, I1, I...) function _sub2ind_vecs(inds, I::AbstractVector...) I1 = I[1] Iinds = axes1(I1) for j = 2:length(I) axes1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(axes1(I[j])))")) end Iout = similar(I1) _sub2ind!(Iout, inds, Iinds, I) Iout end function _sub2ind!(Iout, inds, Iinds, I) @noinline for i in Iinds # Iout[i] = _sub2ind(inds, map(Ij -> Ij[i], I)...) Iout[i] = sub2ind_vec(inds, i, I) end Iout end sub2ind_vec(inds, i, I) = (@inline; _sub2ind(inds, _sub2ind_vec(i, I...)...)) _sub2ind_vec(i, I1, I...) = (@inline; (I1[i], _sub2ind_vec(i, I...)...)) _sub2ind_vec(i) = () function _ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N M = length(ind) t = ntuple(n->similar(ind),Val(N)) for (i,idx) in pairs(IndexLinear(), ind) sub = _ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] end end t end ## iteration utilities ## """ foreach(f, c...) -> Nothing Call function `f` on each element of iterable `c`. For multiple iterable arguments, `f` is called elementwise, and iteration stops when any iterator is finished. `foreach` should be used instead of [`map`](@ref) when the results of `f` are not needed, for example in `foreach(println, array)`. # Examples ```jldoctest julia> tri = 1:3:7; res = Int[]; julia> foreach(x -> push!(res, x^2), tri) julia> res 3-element Vector{$(Int)}: 1 16 49 julia> foreach((x, y) -> println(x, " with ", y), tri, 'a':'z') 1 with a 4 with b 7 with c ``` """ foreach(f) = (f(); nothing) foreach(f, itr) = (for x in itr; f(x); end; nothing) foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing) ## map over arrays ## ## transform any set of dimensions ## dims specifies which dimensions will be transformed. for example ## dims==1:2 will call f on all slices A[:,:,...] """ mapslices(f, A; dims) Transform the given dimensions of array `A` by applying a function `f` on each slice of the form `A[..., :, ..., :, ...]`, with a colon at each `d` in `dims`. The results are concatenated along the remaining dimensions. For example, if `dims = [1,2]` and `A` is 4-dimensional, then `f` is called on `x = A[:,:,i,j]` for all `i` and `j`, and `f(x)` becomes `R[:,:,i,j]` in the result `R`. See also [`eachcol`](@ref) or [`eachslice`](@ref), used with [`map`](@ref) or [`stack`](@ref). # Examples ```jldoctest julia> A = reshape(1:30,(2,5,3)) 2ร—5ร—3 reshape(::UnitRange{$Int}, 2, 5, 3) with eltype $Int: [:, :, 1] = 1 3 5 7 9 2 4 6 8 10 [:, :, 2] = 11 13 15 17 19 12 14 16 18 20 [:, :, 3] = 21 23 25 27 29 22 24 26 28 30 julia> f(x::Matrix) = fill(x[1,1], 1,4); # returns a 1ร—4 matrix julia> B = mapslices(f, A, dims=(1,2)) 1ร—4ร—3 Array{$Int, 3}: [:, :, 1] = 1 1 1 1 [:, :, 2] = 11 11 11 11 [:, :, 3] = 21 21 21 21 julia> f2(x::AbstractMatrix) = fill(x[1,1], 1,4); julia> B == stack(f2, eachslice(A, dims=3)) true julia> g(x) = x[begin] // x[end-1]; # returns a number julia> mapslices(g, A, dims=[1,3]) 1ร—5ร—1 Array{Rational{$Int}, 3}: [:, :, 1] = 1//21 3//23 1//5 7//27 9//29 julia> map(g, eachslice(A, dims=2)) 5-element Vector{Rational{$Int}}: 1//21 3//23 1//5 7//27 9//29 julia> mapslices(sum, A; dims=(1,3)) == sum(A; dims=(1,3)) true ``` Notice that in `eachslice(A; dims=2)`, the specified dimension is the one *without* a colon in the slice. This is `view(A,:,i,:)`, whereas `mapslices(f, A; dims=(1,3))` uses `A[:,i,:]`. The function `f` may mutate values in the slice without affecting `A`. """ function mapslices(f, A::AbstractArray; dims) isempty(dims) && return map(f, A) for d in dims d isa Integer || throw(ArgumentError("mapslices: dimension must be an integer, got $d")) d >= 1 || throw(ArgumentError("mapslices: dimension must be โ‰ฅ 1, got $d")) # Indexing a matrix M[:,1,:] produces a 1-column matrix, but dims=(1,3) here # would otherwise ignore 3, and slice M[:,i]. Previously this gave error: # BoundsError: attempt to access 2-element Vector{Any} at index [3] d > ndims(A) && throw(ArgumentError("mapslices does not accept dimensions > ndims(A) = $(ndims(A)), got $d")) end dim_mask = ntuple(d -> d in dims, ndims(A)) # Apply the function to the first slice in order to determine the next steps idx1 = ntuple(d -> d in dims ? (:) : firstindex(A,d), ndims(A)) Aslice = A[idx1...] r1 = f(Aslice) res1 = if r1 isa AbstractArray && ndims(r1) > 0 n = sum(dim_mask) if ndims(r1) > n && any(ntuple(d -> size(r1,d+n)>1, ndims(r1)-n)) s = size(r1)[1:n] throw(DimensionMismatch("mapslices cannot assign slice f(x) of size $(size(r1)) into output of size $s")) end r1 else # If the result of f on a single slice is a scalar then we add singleton # dimensions. When adding the dimensions, we have to respect the # index type of the input array (e.g. in the case of OffsetArrays) _res1 = similar(Aslice, typeof(r1), reduced_indices(Aslice, 1:ndims(Aslice))) _res1[begin] = r1 _res1 end # Determine result size and allocate. We always pad ndims(res1) out to length(dims): din = Ref(0) Rsize = ntuple(ndims(A)) do d if d in dims axes(res1, din[] += 1) else axes(A,d) end end R = similar(res1, Rsize) # Determine iteration space. It will be convenient in the loop to mask N-dimensional # CartesianIndices, with some trivial dimensions: itershape = ntuple(d -> d in dims ? Base.OneTo(1) : axes(A,d), ndims(A)) indices = Iterators.drop(CartesianIndices(itershape), 1) # That skips the first element, which we already have: ridx = ntuple(d -> d in dims ? Slice(axes(R,d)) : firstindex(A,d), ndims(A)) concatenate_setindex!(R, res1, ridx...) # In some cases, we can re-use the first slice for a dramatic performance # increase. The slice itself must be mutable and the result cannot contain # any mutable containers. The following errs on the side of being overly # strict (#18570 & #21123). safe_for_reuse = isa(Aslice, StridedArray) && (isa(r1, Number) || (isa(r1, AbstractArray) && eltype(r1) <: Number)) _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse) return R end @noinline function _inner_mapslices!(R, indices, f, A, dim_mask, Aslice, safe_for_reuse) must_extend = any(dim_mask .& size(R) .> 1) if safe_for_reuse # when f returns an array, R[ridx...] = f(Aslice) line copies elements, # so we can reuse Aslice for I in indices idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I)) _unsafe_getindex!(Aslice, A, idx...) r = f(Aslice) if r isa AbstractArray || must_extend ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I)) R[ridx...] = r else ridx = ifelse.(dim_mask, first.(axes(R)), Tuple(I)) R[ridx...] = r end end else # we can't guarantee safety (#18524), so allocate new storage for each slice for I in indices idx = ifelse.(dim_mask, Slice.(axes(A)), Tuple(I)) ridx = ifelse.(dim_mask, Slice.(axes(R)), Tuple(I)) concatenate_setindex!(R, f(A[idx...]), ridx...) end end end concatenate_setindex!(R, v, I...) = (R[I...] .= (v,); R) concatenate_setindex!(R, X::AbstractArray, I...) = (R[I...] = X) ## 0 arguments map(f) = f() ## 1 argument function map!(f::F, dest::AbstractArray, A::AbstractArray) where F for (i,j) in zip(eachindex(dest),eachindex(A)) val = f(@inbounds A[j]) @inbounds dest[i] = val end return dest end # map on collections map(f, A::AbstractArray) = collect_similar(A, Generator(f,A)) mapany(f, A::AbstractArray) = map!(f, Vector{Any}(undef, length(A)), A) mapany(f, itr) = Any[f(x) for x in itr] """ map(f, c...) -> collection Transform collection `c` by applying `f` to each element. For multiple collection arguments, apply `f` elementwise, and stop when any of them is exhausted. See also [`map!`](@ref), [`foreach`](@ref), [`mapreduce`](@ref), [`mapslices`](@ref), [`zip`](@ref), [`Iterators.map`](@ref). # Examples ```jldoctest julia> map(x -> x * 2, [1, 2, 3]) 3-element Vector{Int64}: 2 4 6 julia> map(+, [1, 2, 3], [10, 20, 30, 400, 5000]) 3-element Vector{Int64}: 11 22 33 ``` """ map(f, A) = collect(Generator(f,A)) # default to returning an Array for `map` on general iterators map(f, ::AbstractDict) = error("map is not defined on dictionaries") map(f, ::AbstractSet) = error("map is not defined on sets") ## 2 argument function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B)) @inbounds a, b = A[j], B[k] val = f(a, b) @inbounds dest[i] = val end return dest end ## N argument @inline ith_all(i, ::Tuple{}) = () function ith_all(i, as) @_propagate_inbounds_meta return (as[1][i], ith_all(i, tail(as))...) end function map_n!(f::F, dest::AbstractArray, As) where F idxs1 = LinearIndices(As[1]) @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) for i = idxs1 @inbounds I = ith_all(i, As) val = f(I...) @inbounds dest[i] = val end return dest end """ map!(function, destination, collection...) Like [`map`](@ref), but stores the result in `destination` rather than a new collection. `destination` must be at least as large as the smallest collection. $(_DOCS_ALIASING_WARNING) See also: [`map`](@ref), [`foreach`](@ref), [`zip`](@ref), [`copyto!`](@ref). # Examples ```jldoctest julia> a = zeros(3); julia> map!(x -> x * 2, a, [1, 2, 3]); julia> a 3-element Vector{Float64}: 2.0 4.0 6.0 julia> map!(+, zeros(Int, 5), 100:999, 1:3) 5-element Vector{$(Int)}: 101 103 105 0 0 ``` """ function map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F} isempty(As) && throw(ArgumentError( """map! requires at least one "source" argument""")) map_n!(f, dest, As) end """ map(f, A::AbstractArray...) -> N-array When acting on multi-dimensional arrays of the same [`ndims`](@ref), they must all have the same [`axes`](@ref), and the answer will too. See also [`broadcast`](@ref), which allows mismatched sizes. # Examples ``` julia> map(//, [1 2; 3 4], [4 3; 2 1]) 2ร—2 Matrix{Rational{$Int}}: 1//4 2//3 3//2 4//1 julia> map(+, [1 2; 3 4], zeros(2,1)) ERROR: DimensionMismatch julia> map(+, [1 2; 3 4], [1,10,100,1000], zeros(3,1)) # iterates until 3rd is exhausted 3-element Vector{Float64}: 2.0 13.0 102.0 ``` """ map(f, iters...) = collect(Generator(f, iters...)) # multi-item push!, pushfirst! (built on top of type-specific 1-item version) # (note: must not cause a dispatch loop when 1-item case is not defined) push!(A, a, b) = push!(push!(A, a), b) push!(A, a, b, c...) = push!(push!(A, a, b), c...) pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a) pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) ## hashing AbstractArray ## const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5 function hash(A::AbstractArray, h::UInt) h += hash_abstractarray_seed # Axes are themselves AbstractArrays, so hashing them directly would stack overflow # Instead hash the tuple of firsts and lasts along each dimension h = hash(map(first, axes(A)), h) h = hash(map(last, axes(A)), h) # For short arrays, it's not worth doing anything complicated if length(A) < 8192 for x in A h = hash(x, h) end return h end # Goal: Hash approximately log(N) entries with a higher density of hashed elements # weighted towards the end and special consideration for repeated values. Colliding # hashes will often subsequently be compared by equality -- and equality between arrays # works elementwise forwards and is short-circuiting. This means that a collision # between arrays that differ by elements at the beginning is cheaper than one where the # difference is towards the end. Furthermore, choosing `log(N)` arbitrary entries from a # sparse array will likely only choose the same element repeatedly (zero in this case). # To achieve this, we work backwards, starting by hashing the last element of the # array. After hashing each element, we skip `fibskip` elements, where `fibskip` # is pulled from the Fibonacci sequence -- Fibonacci was chosen as a simple # ~O(log(N)) algorithm that ensures we don't hit a common divisor of a dimension # and only end up hashing one slice of the array (as might happen with powers of # two). Finally, we find the next distinct value from the one we just hashed. # This is a little tricky since skipping an integer number of values inherently works # with linear indices, but `findprev` uses `keys`. Hoist out the conversion "maps": ks = keys(A) key_to_linear = LinearIndices(ks) # Index into this map to compute the linear index linear_to_key = vec(ks) # And vice-versa # Start at the last index keyidx = last(ks) linidx = key_to_linear[keyidx] fibskip = prevfibskip = oneunit(linidx) first_linear = first(LinearIndices(linear_to_key)) n = 0 while true n += 1 # Hash the element elt = A[keyidx] h = hash(keyidx=>elt, h) # Skip backwards a Fibonacci number of indices -- this is a linear index operation linidx = key_to_linear[keyidx] linidx < fibskip + first_linear && break linidx -= fibskip keyidx = linear_to_key[linidx] # Only increase the Fibonacci skip once every N iterations. This was chosen # to be big enough that all elements of small arrays get hashed while # obscenely large arrays are still tractable. With a choice of N=4096, an # entirely-distinct 8000-element array will have ~75% of its elements hashed, # with every other element hashed in the first half of the array. At the same # time, hashing a `typemax(Int64)`-length Float64 range takes about a second. if rem(n, 4096) == 0 fibskip, prevfibskip = fibskip + prevfibskip, fibskip end # Find a key index with a value distinct from `elt` -- might be `keyidx` itself keyidx = findprev(!isequal(elt), A, keyidx) keyidx === nothing && break end return h end # The semantics of `collect` are weird. Better to write our own function rest(a::AbstractArray{T}, state...) where {T} v = Vector{T}(undef, 0) # assume only very few items are taken from the front sizehint!(v, length(a)) return foldl(push!, Iterators.rest(a, state...), init=v) end ## keepat! ## # NOTE: since these use `@inbounds`, they are actually only intended for Vector and BitVector function _keepat!(a::AbstractVector, inds) local prev i = firstindex(a) for k in inds if @isdefined(prev) prev < k || throw(ArgumentError("indices must be unique and sorted")) end ak = a[k] # must happen even when i==k for bounds checking if i != k @inbounds a[i] = ak # k > i, so a[i] is inbounds end prev = k i = nextind(a, i) end deleteat!(a, i:lastindex(a)) return a end function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) length(m) == length(a) || throw(BoundsError(a, m)) j = firstindex(a) for i in eachindex(a, m) @inbounds begin if m[i] i == j || (a[j] = a[i]) j = nextind(a, j) end end end deleteat!(a, j:lastindex(a)) end ## 1-d circshift ## function circshift!(a::AbstractVector, shift::Integer) n = length(a) n == 0 && return shift = mod(shift, n) shift == 0 && return l = lastindex(a) reverse!(a, firstindex(a), l-shift) reverse!(a, l-shift+1, lastindex(a)) reverse!(a) return a end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/subarray.jlRR# This file is a part of Julia. License is MIT: https://julialang.org/license abstract type AbstractCartesianIndex{N} end # This is a hacky forward declaration for CartesianIndex const ViewIndex = Union{Real, AbstractArray} const ScalarIndex = Real """ SubArray{T,N,P,I,L} <: AbstractArray{T,N} `N`-dimensional view into a parent array (of type `P`) with an element type `T`, restricted by a tuple of indices (of type `I`). `L` is true for types that support fast linear indexing, and `false` otherwise. Construct `SubArray`s using the [`view`](@ref) function. """ struct SubArray{T,N,P,I,L} <: AbstractArray{T,N} parent::P indices::I offset1::Int # for linear indexing and pointer, only valid when L==true stride1::Int # used only for linear indexing function SubArray{T,N,P,I,L}(parent, indices, offset1, stride1) where {T,N,P,I,L} @inline check_parent_index_match(parent, indices) new(parent, indices, offset1, stride1) end end # Compute the linear indexability of the indices, and combine it with the linear indexing of the parent function SubArray(parent::AbstractArray, indices::Tuple) @inline SubArray(IndexStyle(viewindexing(indices), IndexStyle(parent)), parent, ensure_indexable(indices), index_dimsum(indices...)) end function SubArray(::IndexCartesian, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} @inline SubArray{eltype(P), N, P, I, false}(parent, indices, 0, 0) end function SubArray(::IndexLinear, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} @inline # Compute the stride and offset stride1 = compute_stride1(parent, indices) SubArray{eltype(P), N, P, I, true}(parent, indices, compute_offset1(parent, stride1, indices), stride1) end check_parent_index_match(parent, indices) = check_parent_index_match(parent, index_ndims(indices...)) check_parent_index_match(parent::AbstractArray{T,N}, ::NTuple{N, Bool}) where {T,N} = nothing check_parent_index_match(parent, ::NTuple{N, Bool}) where {N} = throw(ArgumentError("number of indices ($N) must match the parent dimensionality ($(ndims(parent)))")) # This computes the linear indexing compatibility for a given tuple of indices viewindexing(I::Tuple{}) = IndexLinear() # Leading scalar indices simply increase the stride viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; viewindexing(tail(I))) # Slices may begin a section which may be followed by any number of Slices viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@inline; viewindexing(tail(I))) # A UnitRange can follow Slices, but only if all other indices are scalar viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear() viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate # In general, ranges are only fast if all other indices are scalar viewindexing(I::Tuple{AbstractRange, Vararg{ScalarIndex}}) = IndexLinear() # All other index combinations are slow viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() # Of course, all other array types are slow viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() # Simple utilities size(V::SubArray) = (@inline; map(length, axes(V))) similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) sizeof(V::SubArray) = length(V) * sizeof(eltype(V)) sizeof(V::SubArray{<:Any,<:Any,<:Array}) = length(V) * elsize(V.parent) function Base.copy(V::SubArray) v = V.parent[V.indices...] ndims(V) == 0 || return v x = similar(V) # ensure proper type of x x[] = v return x end parent(V::SubArray) = V.parent parentindices(V::SubArray) = V.indices """ parentindices(A) Return the indices in the [`parent`](@ref) which correspond to the view `A`. # Examples ```jldoctest julia> A = [1 2; 3 4]; julia> V = view(A, 1, :) 2-element view(::Matrix{Int64}, 1, :) with eltype Int64: 1 2 julia> parentindices(V) (1, Base.Slice(Base.OneTo(2))) ``` """ function parentindices end parentindices(a::AbstractArray) = map(oneto, size(a)) ## Aliasing detection dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...) _splatmap(f, ::Tuple{}) = () _splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...) unaliascopy(A::SubArray) = typeof(A)(unaliascopy(A.parent), map(unaliascopy, A.indices), A.offset1, A.stride1) # When the parent is an Array we can trim the size down a bit. In the future this # could possibly be extended to any mutable array. function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Vararg{Union{Real,AbstractRange,Array}}},LD} dest = Array{T}(undef, index_lengths(V.indices...)) copyto!(dest, V) SubArray{T,N,A,I,LD}(dest, map(_trimmedindex, V.indices), 0, Int(LD)) end # Transform indices to be "dense" _trimmedindex(i::Real) = oftype(i, 1) _trimmedindex(i::AbstractUnitRange) = oftype(i, oneto(length(i))) _trimmedindex(i::AbstractArray) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i))) ## SubArray creation # We always assume that the dimensionality of the parent matches the number of # indices that end up getting passed to it, so we store the parent as a # ReshapedArray view if necessary. The trouble is that arrays of `CartesianIndex` # can make the number of effective indices not equal to length(I). _maybe_reshape_parent(A::AbstractArray, ::NTuple{1, Bool}) = reshape(A, Val(1)) _maybe_reshape_parent(A::AbstractArray{<:Any,1}, ::NTuple{1, Bool}) = reshape(A, Val(1)) _maybe_reshape_parent(A::AbstractArray{<:Any,N}, ::NTuple{N, Bool}) where {N} = A _maybe_reshape_parent(A::AbstractArray, ::NTuple{N, Bool}) where {N} = reshape(A, Val(N)) # The trailing singleton indices could be eliminated after bounds checking. rm_singleton_indices(ndims::Tuple, J1, Js...) = (J1, rm_singleton_indices(IteratorsMD._splitrest(ndims, index_ndims(J1)), Js...)...) rm_singleton_indices(::Tuple{}, ::ScalarIndex, Js...) = rm_singleton_indices((), Js...) rm_singleton_indices(::Tuple) = () """ view(A, inds...) Like [`getindex`](@ref), but returns a lightweight array that lazily references (or is effectively a _view_ into) the parent array `A` at the given index or indices `inds` instead of eagerly extracting elements or constructing a copied subset. Calling [`getindex`](@ref) or [`setindex!`](@ref) on the returned value (often a [`SubArray`](@ref)) computes the indices to access or modify the parent array on the fly. The behavior is undefined if the shape of the parent array is changed after `view` is called because there is no bound check for the parent array; e.g., it may cause a segmentation fault. Some immutable parent arrays (like ranges) may choose to simply recompute a new array in some circumstances instead of returning a `SubArray` if doing so is efficient and provides compatible semantics. !!! compat "Julia 1.6" In Julia 1.6 or later, `view` can be called on an `AbstractString`, returning a `SubString`. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> b = view(A, :, 1) 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: 1 3 julia> fill!(b, 0) 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: 0 0 julia> A # Note A has changed even though we modified b 2ร—2 Matrix{Int64}: 0 2 0 4 julia> view(2:5, 2:3) # returns a range as type is immutable 3:4 ``` """ function view(A::AbstractArray, I::Vararg{Any,M}) where {M} @inline J = map(i->unalias(A,i), to_indices(A, I)) @boundscheck checkbounds(A, J...) Jโ€ฒ = rm_singleton_indices(ntuple(Returns(true), Val(ndims(A))), J...) unsafe_view(_maybe_reshape_parent(A, index_ndims(Jโ€ฒ...)), Jโ€ฒ...) end # Ranges implement getindex to return recomputed ranges; use that for views, too (when possible) function view(r1::OneTo, r2::OneTo) @_propagate_inbounds_meta getindex(r1, r2) end function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer}) @_propagate_inbounds_meta getindex(r1, r2) end function view(r1::AbstractUnitRange, r2::StepRange{<:Integer}) @_propagate_inbounds_meta getindex(r1, r2) end function view(r1::StepRange, r2::AbstractRange{<:Integer}) @_propagate_inbounds_meta getindex(r1, r2) end function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer}) @_propagate_inbounds_meta getindex(r1, r2) end function view(r1::LinRange, r2::OrdinalRange{<:Integer}) @_propagate_inbounds_meta getindex(r1, r2) end # getindex(r::AbstractRange, ::Colon) returns a copy of the range, and we may do the same for a view function view(r1::AbstractRange, c::Colon) @_propagate_inbounds_meta getindex(r1, c) end function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N} @inline SubArray(A, I) end # When we take the view of a view, it's often possible to "reindex" the parent # view's indices such that we can "pop" the parent view and keep just one layer # of indirection. But we can't always do this because arrays of `CartesianIndex` # might span multiple parent indices, making the reindex calculation very hard. # So we use _maybe_reindex to figure out if there are any arrays of # `CartesianIndex`, and if so, we punt and keep two layers of indirection. unsafe_view(V::SubArray, I::Vararg{ViewIndex,N}) where {N} = (@inline; _maybe_reindex(V, I)) _maybe_reindex(V, I) = (@inline; _maybe_reindex(V, I, I)) _maybe_reindex(V, I, ::Tuple{AbstractArray{<:AbstractCartesianIndex}, Vararg{Any}}) = (@inline; SubArray(V, I)) # But allow arrays of CartesianIndex{1}; they behave just like arrays of Ints _maybe_reindex(V, I, A::Tuple{AbstractArray{<:AbstractCartesianIndex{1}}, Vararg{Any}}) = (@inline; _maybe_reindex(V, I, tail(A))) _maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@inline; _maybe_reindex(V, I, tail(A))) function _maybe_reindex(V, I, ::Tuple{}) @inline @inbounds idxs = to_indices(V.parent, reindex(V.indices, I)) SubArray(V.parent, idxs) end ## Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]] # # Recursively look through the heads of the parent- and sub-indices, considering # the following cases: # * Parent index is array -> re-index that with one or more sub-indices (one per dimension) # * Parent index is Colon -> just use the sub-index as provided # * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is AbstractZeroDimArray{T} = AbstractArray{T, 0} reindex(::Tuple{}, ::Tuple{}) = () # Skip dropped scalars, so simply peel them off the parent indices and continue reindex(idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) = (@_propagate_inbounds_meta; (idxs[1], reindex(tail(idxs), subidxs)...)) # Slices simply pass their subindices straight through reindex(idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = (@_propagate_inbounds_meta; (subidxs[1], reindex(tail(idxs), tail(subidxs))...)) # Re-index into parent vectors with one subindex reindex(idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = (@_propagate_inbounds_meta; (idxs[1][subidxs[1]], reindex(tail(idxs), tail(subidxs))...)) # Parent matrices are re-indexed with two sub-indices reindex(idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) = (@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]], reindex(tail(idxs), tail(tail(subidxs)))...)) # In general, we index N-dimensional parent arrays with N indices @generated function reindex(idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) where {T,N} if length(subidxs.parameters) >= N subs = [:(subidxs[$d]) for d in 1:N] tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)] :(@_propagate_inbounds_meta; (idxs[1][$(subs...)], reindex(tail(idxs), ($(tail...),))...)) else :(throw(ArgumentError("cannot re-index SubArray with fewer indices than dimensions\nThis should not occur; please submit a bug report."))) end end # In general, we simply re-index the parent indices by the provided ones SlowSubArray{T,N,P,I} = SubArray{T,N,P,I,false} function getindex(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N} @inline @boundscheck checkbounds(V, I...) @inbounds r = V.parent[reindex(V.indices, I)...] r end # But SubArrays with fast linear indexing pre-compute a stride and offset FastSubArray{T,N,P,I} = SubArray{T,N,P,I,true} function getindex(V::FastSubArray, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds r = V.parent[V.offset1 + V.stride1*i] r end # We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange, # or if all the indices are scalars, i.e. the view is for a single value only FastContiguousSubArray{T,N,P,I<:Union{Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}}, Tuple{Vararg{ScalarIndex}}}} = SubArray{T,N,P,I,true} function getindex(V::FastContiguousSubArray, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds r = V.parent[V.offset1 + i] r end # For vector views with linear indexing, we disambiguate to favor the stride/offset # computation as that'll generally be faster than (or just as fast as) re-indexing into a range. function getindex(V::FastSubArray{<:Any, 1}, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds r = V.parent[V.offset1 + V.stride1*i] r end function getindex(V::FastContiguousSubArray{<:Any, 1}, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds r = V.parent[V.offset1 + i] r end # Indexed assignment follows the same pattern as `getindex` above function setindex!(V::SubArray{T,N}, x, I::Vararg{Int,N}) where {T,N} @inline @boundscheck checkbounds(V, I...) @inbounds V.parent[reindex(V.indices, I)...] = x V end function setindex!(V::FastSubArray, x, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds V.parent[V.offset1 + V.stride1*i] = x V end function setindex!(V::FastContiguousSubArray, x, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds V.parent[V.offset1 + i] = x V end function setindex!(V::FastSubArray{<:Any, 1}, x, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds V.parent[V.offset1 + V.stride1*i] = x V end function setindex!(V::FastContiguousSubArray{<:Any, 1}, x, i::Int) @inline @boundscheck checkbounds(V, i) @inbounds V.parent[V.offset1 + i] = x V end function isassigned(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N} @inline @boundscheck checkbounds(Bool, V, I...) || return false @inbounds r = isassigned(V.parent, reindex(V.indices, I)...) r end function isassigned(V::FastSubArray, i::Int) @inline @boundscheck checkbounds(Bool, V, i) || return false @inbounds r = isassigned(V.parent, V.offset1 + V.stride1*i) r end function isassigned(V::FastContiguousSubArray, i::Int) @inline @boundscheck checkbounds(Bool, V, i) || return false @inbounds r = isassigned(V.parent, V.offset1 + i) r end function isassigned(V::FastSubArray{<:Any, 1}, i::Int) @inline @boundscheck checkbounds(Bool, V, i) || return false @inbounds r = isassigned(V.parent, V.offset1 + V.stride1*i) r end function isassigned(V::FastContiguousSubArray{<:Any, 1}, i::Int) @inline @boundscheck checkbounds(Bool, V, i) || return false @inbounds r = isassigned(V.parent, V.offset1 + i) r end IndexStyle(::Type{<:FastSubArray}) = IndexLinear() IndexStyle(::Type{<:SubArray}) = IndexCartesian() # Strides are the distance in memory between adjacent elements in a given dimension # which we determine from the strides of the parent strides(V::SubArray) = substrides(strides(V.parent), V.indices) substrides(strds::Tuple{}, ::Tuple{}) = () substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,) substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...) substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...) substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end] compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} = (@inline; compute_stride1(1, fill_to_length(axes(parent), OneTo(1), Val(N)), I)) compute_stride1(s, inds, I::Tuple{}) = s compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; compute_stride1(s*length(inds[1]), tail(inds), tail(I))) compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P) iscontiguous(A::SubArray) = iscontiguous(typeof(A)) iscontiguous(::Type{<:SubArray}) = false iscontiguous(::Type{<:FastContiguousSubArray}) = true first_index(V::FastSubArray) = V.offset1 + V.stride1 # cached for fast linear SubArrays function first_index(V::SubArray) P, I = parent(V), V.indices s1 = compute_stride1(P, I) s1 + compute_offset1(P, s1, I) end # Computing the first index simply steps through the indices, accumulating the # sum of index each multiplied by the parent's stride. # The running sum is `f`; the cumulative stride product is `s`. # If the parent is a vector, then we offset the parent's own indices with parameters of I compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange}) = (@inline; first(I[1]) - stride1*first(axes1(I[1]))) # If the result is one-dimensional and it's a Colon, then linear # indexing uses the indices along the given dimension. # If the result is one-dimensional and it's a range, then linear # indexing might be offset if the index itself is offset # Otherwise linear indexing always matches the parent. compute_offset1(parent, stride1::Integer, I::Tuple) = (@inline; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I)) compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) = (@inline; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case compute_offset1(parent, stride1::Integer, dims, inds::Tuple{AbstractRange}, I::Tuple) = (@inline; compute_linindex(parent, I) - stride1*first(axes1(inds[1]))) # potentially index-offsetting case compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) = (@inline; compute_linindex(parent, I) - stride1) function compute_linindex(parent, I::NTuple{N,Any}) where N @inline IP = fill_to_length(axes(parent), OneTo(1), Val(N)) compute_linindex(first(LinearIndices(parent)), 1, IP, I) end function compute_linindex(f, s, IP::Tuple, I::Tuple{ScalarIndex, Vararg{Any}}) @inline ฮ”i = I[1]-first(IP[1]) compute_linindex(f + ฮ”i*s, s*length(IP[1]), tail(IP), tail(I)) end function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}}) @inline ฮ”i = first(I[1])-first(IP[1]) compute_linindex(f + ฮ”i*s, s*length(IP[1]), tail(IP), tail(I)) end compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f find_extended_dims(dim, ::ScalarIndex, I...) = (@inline; find_extended_dims(dim + 1, I...)) find_extended_dims(dim, i1, I...) = (@inline; (dim, find_extended_dims(dim + 1, I...)...)) find_extended_dims(dim) = () find_extended_inds(::ScalarIndex, I...) = (@inline; find_extended_inds(I...)) find_extended_inds(i1, I...) = (@inline; (i1, find_extended_inds(I...)...)) find_extended_inds() = () function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...) end pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N} index = first_index(V) strds = strides(V) for d = 1:N index += (is[d]-1)*strds[d] end return pointer(V.parent, index) end # indices are taken from the range/vector # Since bounds-checking is performance-critical and uses # indices, it's worth optimizing these implementations thoroughly axes(S::SubArray) = (@inline; _indices_sub(S.indices...)) _indices_sub(::Real, I...) = (@inline; _indices_sub(I...)) _indices_sub() = () function _indices_sub(i1::AbstractArray, I...) @inline (axes(i1)..., _indices_sub(I...)...) end has_offset_axes(S::SubArray) = has_offset_axes(S.indices...) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/views.jl!# This file is a part of Julia. License is MIT: https://julialang.org/license """ replace_ref_begin_end!(ex) Recursively replace occurrences of the symbols `:begin` and `:end` in a "ref" expression (i.e. `A[...]`) `ex` with the appropriate function calls (`firstindex` or `lastindex`). Replacement uses the closest enclosing ref, so A[B[end]] should transform to A[B[lastindex(B)]] """ replace_ref_begin_end!(ex) = replace_ref_begin_end_!(ex, nothing)[1] # replace_ref_begin_end_!(ex,withex) returns (new ex, whether withex was used) function replace_ref_begin_end_!(ex, withex) used_withex = false if isa(ex,Symbol) if ex === :begin withex === nothing && error("Invalid use of begin") return withex[1], true elseif ex === :end withex === nothing && error("Invalid use of end") return withex[2], true end elseif isa(ex,Expr) if ex.head === :ref ex.args[1], used_withex = replace_ref_begin_end_!(ex.args[1], withex) S = isa(ex.args[1],Symbol) ? ex.args[1]::Symbol : gensym(:S) # temp var to cache ex.args[1] if needed used_S = false # whether we actually need S # new :ref, so redefine withex nargs = length(ex.args)-1 if nargs == 0 return ex, used_withex elseif nargs == 1 # replace with lastindex(S) ex.args[2], used_S = replace_ref_begin_end_!(ex.args[2], (:($firstindex($S)),:($lastindex($S)))) else n = 1 J = lastindex(ex.args) for j = 2:J exj, used = replace_ref_begin_end_!(ex.args[j], (:($firstindex($S,$n)),:($lastindex($S,$n)))) used_S |= used ex.args[j] = exj if isa(exj,Expr) && exj.head === :... # splatted object exjs = exj.args[1] n = :($n + length($exjs)) elseif isa(n, Expr) # previous expression splatted n = :($n + 1) else # an integer n += 1 end end end if used_S && S !== ex.args[1] S0 = ex.args[1] ex.args[1] = S ex = Expr(:let, :($S = $S0), ex) end else # recursive search for i = eachindex(ex.args) ex.args[i], used = replace_ref_begin_end_!(ex.args[i], withex) used_withex |= used end end end ex, used_withex end """ @view A[inds...] Transform the indexing expression `A[inds...]` into the equivalent [`view`](@ref) call. This can only be applied directly to a single indexing expression and is particularly helpful for expressions that include the special `begin` or `end` indexing syntaxes like `A[begin, 2:end-1]` (as those are not supported by the normal [`view`](@ref) function). Note that `@view` cannot be used as the target of a regular assignment (e.g., `@view(A[1, 2:end]) = ...`), nor would the un-decorated [indexed assignment](@ref man-indexed-assignment) (`A[1, 2:end] = ...`) or broadcasted indexed assignment (`A[1, 2:end] .= ...`) make a copy. It can be useful, however, for _updating_ broadcasted assignments like `@view(A[1, 2:end]) .+= 1` because this is a simple syntax for `@view(A[1, 2:end]) .= @view(A[1, 2:end]) + 1`, and the indexing expression on the right-hand side would otherwise make a copy without the `@view`. See also [`@views`](@ref) to switch an entire block of code to use views for non-scalar indexing. !!! compat "Julia 1.5" Using `begin` in an indexing expression to refer to the first index requires at least Julia 1.5. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> b = @view A[:, 1] 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: 1 3 julia> fill!(b, 0) 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: 0 0 julia> A 2ร—2 Matrix{Int64}: 0 2 0 4 ``` """ macro view(ex) if Meta.isexpr(ex, :ref) ex = replace_ref_begin_end!(ex) if Meta.isexpr(ex, :ref) ex = Expr(:call, view, ex.args...) else # ex replaced by let ...; foo[...]; end if !(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[2], :ref)) error("invalid expression") end ex.args[2] = Expr(:call, view, ex.args[2].args...) end Expr(:&&, true, esc(ex)) else throw(ArgumentError("Invalid use of @view macro: argument must be a reference expression A[...].")) end end ############################################################################ # @views macro code: # maybeview is like getindex, but returns a view for slicing operations # (while remaining equivalent to getindex for scalar indices and non-array types) @propagate_inbounds maybeview(A, args...) = getindex(A, args...) @propagate_inbounds maybeview(A::AbstractArray, args...) = view(A, args...) @propagate_inbounds maybeview(A::AbstractArray, args::Union{Number,AbstractCartesianIndex}...) = getindex(A, args...) @propagate_inbounds maybeview(A) = getindex(A) @propagate_inbounds maybeview(A::AbstractArray) = getindex(A) # _views implements the transformation for the @views macro. # @views calls esc(_views(...)) to work around #20241, # so any function calls we insert (to maybeview, or to # firstindex and lastindex in replace_ref_begin_end!) must be interpolated # as values rather than as symbols to ensure that they are called # from Base rather than from the caller's scope. _views(x) = x function _views(ex::Expr) if ex.head in (:(=), :(.=)) # don't use view for ref on the lhs of an assignment, # but still use views for the args of the ref: arg1 = ex.args[1] Expr(ex.head, Meta.isexpr(arg1, :ref) ? Expr(:ref, mapany(_views, (arg1::Expr).args)...) : _views(arg1), _views(ex.args[2])) elseif ex.head === :ref Expr(:call, maybeview, mapany(_views, ex.args)...)::Expr else h = string(ex.head) # don't use view on the lhs of an op-assignment a[i...] += ... if last(h) == '=' && Meta.isexpr(ex.args[1], :ref) lhs = ex.args[1]::Expr # temp vars to avoid recomputing a and i, # which will be assigned in a let block: a = gensym(:a) i = let lhs=lhs # #15276 [gensym(:i) for k = 1:length(lhs.args)-1] end # for splatted indices like a[i, j...], we need to # splat the corresponding temp var. I = similar(i, Any) for k = 1:length(i) argk1 = lhs.args[k+1] if Meta.isexpr(argk1, :...) I[k] = Expr(:..., i[k]) lhs.args[k+1] = (argk1::Expr).args[1] # unsplat else I[k] = i[k] end end Expr(:let, Expr(:block, :($a = $(_views(lhs.args[1]))), Any[:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...), Expr(first(h) == '.' ? :(.=) : :(=), :($a[$(I...)]), Expr(:call, Symbol(h[1:end-1]), :($maybeview($a, $(I...))), mapany(_views, ex.args[2:end])...))) else exprarray(ex.head, mapany(_views, ex.args)) end end end """ @views expression Convert every array-slicing operation in the given expression (which may be a `begin`/`end` block, loop, function, etc.) to return a view. Scalar indices, non-array types, and explicit [`getindex`](@ref) calls (as opposed to `array[...]`) are unaffected. Similarly, `@views` converts string slices into [`SubString`](@ref) views. !!! note The `@views` macro only affects `array[...]` expressions that appear explicitly in the given `expression`, not array slicing that occurs in functions called by that code. !!! compat "Julia 1.5" Using `begin` in an indexing expression to refer to the first index requires at least Julia 1.5. # Examples ```jldoctest julia> A = zeros(3, 3); julia> @views for row in 1:3 b = A[row, :] b[:] .= row end julia> A 3ร—3 Matrix{Float64}: 1.0 1.0 1.0 2.0 2.0 2.0 3.0 3.0 3.0 ``` """ macro views(x) esc(_views(replace_ref_begin_end!(x))) end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/baseext.jlz # This file is a part of Julia. License is MIT: https://julialang.org/license # extensions to Core types to add features in Base """ VecElement{T} A wrapper type that holds a single value of type `T`. When used in the context of an `NTuple{N, VecElement{T}} where {T, N}` object, it provides a hint to the runtime system to align that struct to be more amenable to vectorization optimization opportunities. In `ccall`, such an NTuple in the type signature will also use the vector register ABI, rather than the usual struct ABI. """ VecElement # hook up VecElement constructor to Base.convert VecElement{T}(arg) where {T} = VecElement{T}(convert(T, arg)) convert(::Type{T}, arg::T) where {T<:VecElement} = arg convert(::Type{T}, arg) where {T<:VecElement} = T(arg)::T # ## dims-type-converting Array constructors for convenience # type and dimensionality specified, accepting dims as series of Integers Vector{T}(::UndefInitializer, m::Integer) where {T} = Vector{T}(undef, Int(m)) Matrix{T}(::UndefInitializer, m::Integer, n::Integer) where {T} = Matrix{T}(undef, Int(m), Int(n)) Array{T,N}(::UndefInitializer, d::Vararg{Integer,N}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) # type but not dimensionality specified, accepting dims as series of Integers Array{T}(::UndefInitializer, m::Integer) where {T} = Array{T,1}(undef, Int(m)) Array{T}(::UndefInitializer, m::Integer, n::Integer) where {T} = Array{T,2}(undef, Int(m), Int(n)) Array{T}(::UndefInitializer, m::Integer, n::Integer, o::Integer) where {T} = Array{T,3}(undef, Int(m), Int(n), Int(o)) Array{T}(::UndefInitializer, d::Integer...) where {T} = Array{T}(undef, convert(Tuple{Vararg{Int}}, d)) # dimensionality but not type specified, accepting dims as series of Integers Vector(::UndefInitializer, m::Integer) = Vector{Any}(undef, Int(m)) Matrix(::UndefInitializer, m::Integer, n::Integer) = Matrix{Any}(undef, Int(m), Int(n)) # Dimensions as a single tuple Array{T}(::UndefInitializer, d::NTuple{N,Integer}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) Array{T,N}(::UndefInitializer, d::NTuple{N,Integer}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) # empty vector constructor Vector() = Vector{Any}(undef, 0) # Array constructors for nothing and missing # type and dimensionality specified Array{T,N}(::Nothing, d...) where {T,N} = fill!(Array{T,N}(undef, d...), nothing) Array{T,N}(::Missing, d...) where {T,N} = fill!(Array{T,N}(undef, d...), missing) # type but not dimensionality specified Array{T}(::Nothing, d...) where {T} = fill!(Array{T}(undef, d...), nothing) Array{T}(::Missing, d...) where {T} = fill!(Array{T}(undef, d...), missing) M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ntuple.jlฌ # This file is a part of Julia. License is MIT: https://julialang.org/license # `ntuple`, for constructing tuples of a given length """ ntuple(f::Function, n::Integer) Create a tuple of length `n`, computing each element as `f(i)`, where `i` is the index of the element. # Examples ```jldoctest julia> ntuple(i -> 2*i, 4) (2, 4, 6, 8) ``` """ @inline function ntuple(f::F, n::Integer) where F # marked inline since this benefits from constant propagation of `n` t = n == 0 ? () : n == 1 ? (f(1),) : n == 2 ? (f(1), f(2)) : n == 3 ? (f(1), f(2), f(3)) : n == 4 ? (f(1), f(2), f(3), f(4)) : n == 5 ? (f(1), f(2), f(3), f(4), f(5)) : n == 6 ? (f(1), f(2), f(3), f(4), f(5), f(6)) : n == 7 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7)) : n == 8 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8)) : n == 9 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9)) : n == 10 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9), f(10)) : _ntuple(f, n) return t end function _ntuple(f::F, n) where F @noinline (n >= 0) || throw(ArgumentError(LazyString("tuple length should be โ‰ฅ 0, got ", n))) ([f(i) for i = 1:n]...,) end function ntupleany(f, n) @noinline (n >= 0) || throw(ArgumentError(LazyString("tuple length should be โ‰ฅ 0, got ", n))) (Any[f(i) for i = 1:n]...,) end # inferable ntuple (enough for bootstrapping) ntuple(f, ::Val{0}) = () ntuple(f, ::Val{1}) = (@inline; (f(1),)) ntuple(f, ::Val{2}) = (@inline; (f(1), f(2))) ntuple(f, ::Val{3}) = (@inline; (f(1), f(2), f(3))) """ ntuple(f, ::Val{N}) Create a tuple of length `N`, computing each element as `f(i)`, where `i` is the index of the element. By taking a `Val(N)` argument, it is possible that this version of ntuple may generate more efficient code than the version taking the length as an integer. But `ntuple(f, N)` is preferable to `ntuple(f, Val(N))` in cases where `N` cannot be determined at compile time. # Examples ```jldoctest julia> ntuple(i -> 2*i, Val(4)) (2, 4, 6, 8) ``` """ @inline function ntuple(f::F, ::Val{N}) where {F,N} N::Int (N >= 0) || throw(ArgumentError(LazyString("tuple length should be โ‰ฅ 0, got ", N))) if @generated :(@ntuple $N i -> f(i)) else Tuple(f(i) for i = 1:N) end end @inline function fill_to_length(t::Tuple, val, ::Val{_N}) where {_N} M = length(t) N = _N::Int M > N && throw(ArgumentError(LazyString("input tuple of length ", M, ", requested ", N))) if @generated quote (t..., $(fill(:val, (_N::Int) - length(t.parameters))...)) end else (t..., fill(val, N-M)...) end end S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/abstractdict.jlŽ?# This file is a part of Julia. License is MIT: https://julialang.org/license # generic operations on dictionaries """ KeyError(key) An indexing operation into an `AbstractDict` (`Dict`) or `Set` like object tried to access or delete a non-existent element. """ struct KeyError <: Exception key end const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ haskey(d::AbstractDict, k) = in(k, keys(d)) function in(p::Pair, a::AbstractDict, valcmp=(==)) v = get(a, p.first, secret_table_token) if v !== secret_table_token return valcmp(v, p.second) end return false end function in(p, a::AbstractDict) error("""AbstractDict collections only contain Pairs; Either look for e.g. A=>B instead, or use the `keys` or `values` function if you are looking for a key or value respectively.""") end function summary(io::IO, t::AbstractDict) showarg(io, t, true) if Base.IteratorSize(t) isa HasLength n = length(t) print(io, " with ", n, (n==1 ? " entry" : " entries")) else print(io, "(...)") end end struct KeySet{K, T <: AbstractDict{K}} <: AbstractSet{K} dict::T end struct ValueIterator{T<:AbstractDict} dict::T end function summary(io::IO, iter::T) where {T<:Union{KeySet,ValueIterator}} print(io, T.name.name, " for a ") summary(io, iter.dict) end show(io::IO, iter::Union{KeySet,ValueIterator}) = show_vector(io, iter) length(v::Union{KeySet,ValueIterator}) = length(v.dict) isempty(v::Union{KeySet,ValueIterator}) = isempty(v.dict) _tt2(::Type{Pair{A,B}}) where {A,B} = B eltype(::Type{ValueIterator{D}}) where {D} = _tt2(eltype(D)) function iterate(v::Union{KeySet,ValueIterator}, state...) y = iterate(v.dict, state...) y === nothing && return nothing return (y[1][isa(v, KeySet) ? 1 : 2], y[2]) end copy(v::KeySet) = copymutable(v) in(k, v::KeySet) = get(v.dict, k, secret_table_token) !== secret_table_token """ keys(iterator) For an iterator or collection that has keys and values (e.g. arrays and dictionaries), return an iterator over the keys. """ function keys end """ keys(a::AbstractDict) Return an iterator over all keys in a dictionary. `collect(keys(a))` returns an array of keys. When the keys are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. But `keys(a)` and `values(a)` both iterate `a` and return the elements in the same order. # Examples ```jldoctest julia> D = Dict('a'=>2, 'b'=>3) Dict{Char, Int64} with 2 entries: 'a' => 2 'b' => 3 julia> collect(keys(D)) 2-element Vector{Char}: 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) ``` """ keys(a::AbstractDict) = KeySet(a) """ values(a::AbstractDict) Return an iterator over all values in a collection. `collect(values(a))` returns an array of values. When the values are stored internally in a hash table, as is the case for `Dict`, the order in which they are returned may vary. But `keys(a)` and `values(a)` both iterate `a` and return the elements in the same order. # Examples ```jldoctest julia> D = Dict('a'=>2, 'b'=>3) Dict{Char, Int64} with 2 entries: 'a' => 2 'b' => 3 julia> collect(values(D)) 2-element Vector{Int64}: 2 3 ``` """ values(a::AbstractDict) = ValueIterator(a) """ pairs(collection) Return an iterator over `key => value` pairs for any collection that maps a set of keys to a set of values. This includes arrays, where the keys are the array indices. # Examples ```jldoctest julia> a = Dict(zip(["a", "b", "c"], [1, 2, 3])) Dict{String, Int64} with 3 entries: "c" => 3 "b" => 2 "a" => 1 julia> pairs(a) Dict{String, Int64} with 3 entries: "c" => 3 "b" => 2 "a" => 1 julia> foreach(println, pairs(["a", "b", "c"])) 1 => "a" 2 => "b" 3 => "c" julia> (;a=1, b=2, c=3) |> pairs |> collect 3-element Vector{Pair{Symbol, Int64}}: :a => 1 :b => 2 :c => 3 julia> (;a=1, b=2, c=3) |> collect 3-element Vector{Int64}: 1 2 3 ``` """ pairs(collection) = Generator(=>, keys(collection), values(collection)) pairs(a::AbstractDict) = a """ empty(a::AbstractDict, [index_type=keytype(a)], [value_type=valtype(a)]) Create an empty `AbstractDict` container which can accept indices of type `index_type` and values of type `value_type`. The second and third arguments are optional and default to the input's `keytype` and `valtype`, respectively. (If only one of the two types is specified, it is assumed to be the `value_type`, and the `index_type` we default to `keytype(a)`). Custom `AbstractDict` subtypes may choose which specific dictionary type is best suited to return for the given index and value types, by specializing on the three-argument signature. The default is to return an empty `Dict`. """ empty(a::AbstractDict) = empty(a, keytype(a), valtype(a)) empty(a::AbstractDict, ::Type{V}) where {V} = empty(a, keytype(a), V) # Note: this is the form which makes sense for `Vector`. copy(a::AbstractDict) = merge!(empty(a), a) function copy!(dst::AbstractDict, src::AbstractDict) dst === src && return dst merge!(empty!(dst), src) end """ merge!(d::AbstractDict, others::AbstractDict...) Update collection with pairs from the other collections. See also [`merge`](@ref). # Examples ```jldoctest julia> d1 = Dict(1 => 2, 3 => 4); julia> d2 = Dict(1 => 4, 4 => 5); julia> merge!(d1, d2); julia> d1 Dict{Int64, Int64} with 3 entries: 4 => 5 3 => 4 1 => 4 ``` """ function merge!(d::AbstractDict, others::AbstractDict...) for other in others if haslength(d) && haslength(other) sizehint!(d, length(d) + length(other)) end for (k,v) in other d[k] = v end end return d end """ mergewith!(combine, d::AbstractDict, others::AbstractDict...) -> d mergewith!(combine) merge!(combine, d::AbstractDict, others::AbstractDict...) -> d Update collection with pairs from the other collections. Values with the same key will be combined using the combiner function. The curried form `mergewith!(combine)` returns the function `(args...) -> mergewith!(combine, args...)`. Method `merge!(combine::Union{Function,Type}, args...)` as an alias of `mergewith!(combine, args...)` is still available for backward compatibility. !!! compat "Julia 1.5" `mergewith!` requires Julia 1.5 or later. # Examples ```jldoctest julia> d1 = Dict(1 => 2, 3 => 4); julia> d2 = Dict(1 => 4, 4 => 5); julia> mergewith!(+, d1, d2); julia> d1 Dict{Int64, Int64} with 3 entries: 4 => 5 3 => 4 1 => 6 julia> mergewith!(-, d1, d1); julia> d1 Dict{Int64, Int64} with 3 entries: 4 => 0 3 => 0 1 => 0 julia> foldl(mergewith!(+), [d1, d2]; init=Dict{Int64, Int64}()) Dict{Int64, Int64} with 3 entries: 4 => 5 3 => 0 1 => 4 ``` """ function mergewith!(combine, d::AbstractDict, others::AbstractDict...) foldl(mergewith!(combine), others; init = d) end function mergewith!(combine, d1::AbstractDict, d2::AbstractDict) for (k, v) in d2 d1[k] = haskey(d1, k) ? combine(d1[k], v) : v end return d1 end mergewith!(combine) = (args...) -> mergewith!(combine, args...) merge!(combine::Callable, args...) = mergewith!(combine, args...) """ keytype(type) Get the key type of a dictionary type. Behaves similarly to [`eltype`](@ref). # Examples ```jldoctest julia> keytype(Dict(Int32(1) => "foo")) Int32 ``` """ keytype(::Type{<:AbstractDict{K,V}}) where {K,V} = K keytype(a::AbstractDict) = keytype(typeof(a)) """ valtype(type) Get the value type of a dictionary type. Behaves similarly to [`eltype`](@ref). # Examples ```jldoctest julia> valtype(Dict(Int32(1) => "foo")) String ``` """ valtype(::Type{<:AbstractDict{K,V}}) where {K,V} = V valtype(a::AbstractDict) = valtype(typeof(a)) """ merge(d::AbstractDict, others::AbstractDict...) Construct a merged collection from the given collections. If necessary, the types of the resulting collection will be promoted to accommodate the types of the merged collections. If the same key is present in another collection, the value for that key will be the value it has in the last collection listed. See also [`mergewith`](@ref) for custom handling of values with the same key. # Examples ```jldoctest julia> a = Dict("foo" => 0.0, "bar" => 42.0) Dict{String, Float64} with 2 entries: "bar" => 42.0 "foo" => 0.0 julia> b = Dict("baz" => 17, "bar" => 4711) Dict{String, Int64} with 2 entries: "bar" => 4711 "baz" => 17 julia> merge(a, b) Dict{String, Float64} with 3 entries: "bar" => 4711.0 "baz" => 17.0 "foo" => 0.0 julia> merge(b, a) Dict{String, Float64} with 3 entries: "bar" => 42.0 "baz" => 17.0 "foo" => 0.0 ``` """ merge(d::AbstractDict, others::AbstractDict...) = merge!(_typeddict(d, others...), others...) """ mergewith(combine, d::AbstractDict, others::AbstractDict...) mergewith(combine) merge(combine, d::AbstractDict, others::AbstractDict...) Construct a merged collection from the given collections. If necessary, the types of the resulting collection will be promoted to accommodate the types of the merged collections. Values with the same key will be combined using the combiner function. The curried form `mergewith(combine)` returns the function `(args...) -> mergewith(combine, args...)`. Method `merge(combine::Union{Function,Type}, args...)` as an alias of `mergewith(combine, args...)` is still available for backward compatibility. !!! compat "Julia 1.5" `mergewith` requires Julia 1.5 or later. # Examples ```jldoctest julia> a = Dict("foo" => 0.0, "bar" => 42.0) Dict{String, Float64} with 2 entries: "bar" => 42.0 "foo" => 0.0 julia> b = Dict("baz" => 17, "bar" => 4711) Dict{String, Int64} with 2 entries: "bar" => 4711 "baz" => 17 julia> mergewith(+, a, b) Dict{String, Float64} with 3 entries: "bar" => 4753.0 "baz" => 17.0 "foo" => 0.0 julia> ans == mergewith(+)(a, b) true ``` """ mergewith(combine, d::AbstractDict, others::AbstractDict...) = mergewith!(combine, _typeddict(d, others...), others...) mergewith(combine) = (args...) -> mergewith(combine, args...) merge(combine::Callable, d::AbstractDict, others::AbstractDict...) = merge!(combine, _typeddict(d, others...), others...) promoteK(K) = K promoteV(V) = V promoteK(K, d, ds...) = promoteK(promote_type(K, keytype(d)), ds...) promoteV(V, d, ds...) = promoteV(promote_type(V, valtype(d)), ds...) function _typeddict(d::AbstractDict, others::AbstractDict...) K = promoteK(keytype(d), others...) V = promoteV(valtype(d), others...) Dict{K,V}(d) end """ filter!(f, d::AbstractDict) Update `d`, removing elements for which `f` is `false`. The function `f` is passed `key=>value` pairs. # Example ```jldoctest julia> d = Dict(1=>"a", 2=>"b", 3=>"c") Dict{Int64, String} with 3 entries: 2 => "b" 3 => "c" 1 => "a" julia> filter!(p->isodd(p.first), d) Dict{Int64, String} with 2 entries: 3 => "c" 1 => "a" ``` """ function filter!(f, d::AbstractDict) badkeys = Vector{keytype(d)}() for pair in d # don't delete!(d, k) here, since dictionary types # may not support mutation during iteration f(pair) || push!(badkeys, pair.first) end for k in badkeys delete!(d, k) end return d end function filter_in_one_pass!(f, d::AbstractDict) for pair in d if !f(pair) delete!(d, pair.first) end end return d end """ filter(f, d::AbstractDict) Return a copy of `d`, removing elements for which `f` is `false`. The function `f` is passed `key=>value` pairs. # Examples ```jldoctest julia> d = Dict(1=>"a", 2=>"b") Dict{Int64, String} with 2 entries: 2 => "b" 1 => "a" julia> filter(p->isodd(p.first), d) Dict{Int64, String} with 1 entry: 1 => "a" ``` """ function filter(f, d::AbstractDict) # don't just do filter!(f, copy(d)): avoid making a whole copy of d df = empty(d) for pair in d if f(pair) df[pair.first] = pair.second end end return df end function eltype(::Type{<:AbstractDict{K,V}}) where {K,V} if @isdefined(K) if @isdefined(V) return Pair{K,V} else return Pair{K} end elseif @isdefined(V) return Pair{k,V} where k else return Pair end end function isequal(l::AbstractDict, r::AbstractDict) l === r && return true if isa(l,IdDict) != isa(r,IdDict) return false end if length(l) != length(r) return false end for pair in l if !in(pair, r, isequal) return false end end true end function ==(l::AbstractDict, r::AbstractDict) if isa(l,IdDict) != isa(r,IdDict) return false end length(l) != length(r) && return false anymissing = false for pair in l isin = in(pair, r) if ismissing(isin) anymissing = true elseif !isin return false end end return anymissing ? missing : true end # Fallback implementation sizehint!(d::AbstractDict, n) = d const hasha_seed = UInt === UInt64 ? 0x6d35bb51952d5539 : 0x952d5539 function hash(a::AbstractDict, h::UInt) hv = hasha_seed for (k,v) in a hv โŠป= hash(k, hash(v)) end hash(hv, h) end function getindex(t::AbstractDict{<:Any,V}, key) where V v = get(t, key, secret_table_token) if v === secret_table_token throw(KeyError(key)) end return v::V end # t[k1,k2,ks...] is syntactic sugar for t[(k1,k2,ks...)]. (Note # that we need to avoid dispatch loops if setindex!(t,v,k) is not defined.) getindex(t::AbstractDict, k1, k2, ks...) = getindex(t, tuple(k1,k2,ks...)) setindex!(t::AbstractDict, v, k1, k2, ks...) = setindex!(t, v, tuple(k1,k2,ks...)) get!(t::AbstractDict, key, default) = get!(() -> default, t, key) function get!(default::Callable, t::AbstractDict{K,V}, key) where K where V key = convert(K, key) if haskey(t, key) return t[key] else return t[key] = convert(V, default()) end end push!(t::AbstractDict, p::Pair) = setindex!(t, p.second, p.first) # AbstractDicts are convertible convert(::Type{T}, x::T) where {T<:AbstractDict} = x function convert(::Type{T}, x::AbstractDict) where T<:AbstractDict h = T(x)::T if length(h) != length(x) error("key collision during dictionary conversion") end return h end # hashing objects by identity _tablesz(x::T) where T <: Integer = x < 16 ? T(16) : one(T)<<(top_set_bit(x-one(T))) TP{K,V} = Union{Type{Tuple{K,V}},Type{Pair{K,V}}} dict_with_eltype(DT_apply, kv, ::TP{K,V}) where {K,V} = DT_apply(K, V)(kv) dict_with_eltype(DT_apply, kv::Generator, ::TP{K,V}) where {K,V} = DT_apply(K, V)(kv) dict_with_eltype(DT_apply, ::Type{Pair{K,V}}) where {K,V} = DT_apply(K, V)() dict_with_eltype(DT_apply, ::Type) = DT_apply(Any, Any)() dict_with_eltype(DT_apply::F, kv, t) where {F} = grow_to!(dict_with_eltype(DT_apply, @default_eltype(typeof(kv))), kv) function dict_with_eltype(DT_apply::F, kv::Generator, t) where F T = @default_eltype(kv) if T <: Union{Pair, Tuple{Any, Any}} && isconcretetype(T) return dict_with_eltype(DT_apply, kv, T) end return grow_to!(dict_with_eltype(DT_apply, T), kv) end """ map!(f, values(dict::AbstractDict)) Modifies `dict` by transforming each value from `val` to `f(val)`. Note that the type of `dict` cannot be changed: if `f(val)` is not an instance of the value type of `dict` then it will be converted to the value type if possible and otherwise raise an error. !!! compat "Julia 1.2" `map!(f, values(dict::AbstractDict))` requires Julia 1.2 or later. # Examples ```jldoctest julia> d = Dict(:a => 1, :b => 2) Dict{Symbol, Int64} with 2 entries: :a => 1 :b => 2 julia> map!(v -> v-1, values(d)) ValueIterator for a Dict{Symbol, Int64} with 2 entries. Values: 0 1 ``` """ function map!(f, iter::ValueIterator) # This is the naive fallback which requires hash evaluations # Contrary to the example Dict has an implementation which does not require hash evaluations dict = iter.dict for (key, val) in pairs(dict) dict[key] = f(val) end return iter end M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/iddict.jlA# This file is a part of Julia. License is MIT: https://julialang.org/license """ IdDict([itr]) `IdDict{K,V}()` constructs a hash table using [`objectid`](@ref) as hash and `===` as equality with keys of type `K` and values of type `V`. See [`Dict`](@ref) for further help. In the example below, The `Dict` keys are all `isequal` and therefore get hashed the same, so they get overwritten. The `IdDict` hashes by object-id, and thus preserves the 3 different keys. # Examples ```julia-repl julia> Dict(true => "yes", 1 => "no", 1.0 => "maybe") Dict{Real, String} with 1 entry: 1.0 => "maybe" julia> IdDict(true => "yes", 1 => "no", 1.0 => "maybe") IdDict{Any, String} with 3 entries: true => "yes" 1.0 => "maybe" 1 => "no" ``` """ mutable struct IdDict{K,V} <: AbstractDict{K,V} ht::Vector{Any} count::Int ndel::Int IdDict{K,V}() where {K, V} = new{K,V}(Vector{Any}(undef, 32), 0, 0) function IdDict{K,V}(itr) where {K, V} d = IdDict{K,V}() for (k,v) in itr; d[k] = v; end d end function IdDict{K,V}(pairs::Pair...) where {K, V} d = IdDict{K,V}() sizehint!(d, length(pairs)) for (k,v) in pairs; d[k] = v; end d end IdDict{K,V}(d::IdDict{K,V}) where {K, V} = new{K,V}(copy(d.ht), d.count, d.ndel) end IdDict() = IdDict{Any,Any}() IdDict(kv::Tuple{}) = IdDict() IdDict(ps::Pair{K,V}...) where {K,V} = IdDict{K,V}(ps) IdDict(ps::Pair{K}...) where {K} = IdDict{K,Any}(ps) IdDict(ps::(Pair{K,V} where K)...) where {V} = IdDict{Any,V}(ps) IdDict(ps::Pair...) = IdDict{Any,Any}(ps) function IdDict(kv) try dict_with_eltype((K, V) -> IdDict{K, V}, kv, eltype(kv)) catch if !applicable(iterate, kv) || !all(x->isa(x,Union{Tuple,Pair}),kv) throw(ArgumentError( "IdDict(kv): kv needs to be an iterator of tuples or pairs")) else rethrow() end end end empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}() function rehash!(d::IdDict, newsz = length(d.ht)%UInt) d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz) d end function sizehint!(d::IdDict, newsz) newsz = _tablesz(newsz*2) # *2 for keys and values in same array oldsz = length(d.ht) # grow at least 25% if newsz < (oldsz*5)>>2 return d end rehash!(d, newsz) end function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where {K, V} !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) if !(val isa V) # avoid a dynamic call val = convert(V, val)::V end if d.ndel >= ((3*length(d.ht))>>2) rehash!(d, max((length(d.ht)%UInt)>>1, 32)) d.ndel = 0 end inserted = RefValue{Cint}(0) d.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any, Ptr{Cint}), d.ht, key, val, inserted) d.count += inserted[] return d end function get(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, default) val === default ? default : val::V end function getindex(d::IdDict{K,V}, @nospecialize(key)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) val === secret_table_token && throw(KeyError(key)) return val::V end function pop!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} found = RefValue{Cint}(0) val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any, Ptr{Cint}), d.ht, key, default, found) if found[] === Cint(0) return default else d.count -= 1 d.ndel += 1 return val::V end end function pop!(d::IdDict{K,V}, @nospecialize(key)) where {K, V} val = pop!(d, key, secret_table_token) val === secret_table_token && throw(KeyError(key)) return val::V end function delete!(d::IdDict{K}, @nospecialize(key)) where K pop!(d, key, secret_table_token) d end function empty!(d::IdDict) resize!(d.ht, 32) ht = d.ht t = @_gc_preserve_begin ht memset(unsafe_convert(Ptr{Cvoid}, ht), 0, sizeof(ht)) @_gc_preserve_end t d.ndel = 0 d.count = 0 return d end _oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i)) function iterate(d::IdDict{K,V}, idx=0) where {K, V} idx = _oidd_nextind(d.ht, idx%UInt) idx == -1 && return nothing return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2) end length(d::IdDict) = d.count copy(d::IdDict) = typeof(d)(d) function get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token val = isa(default, V) ? default : convert(V, default)::V setindex!(d, val, key) return val else return val::V end end function get(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token return default() else return val::V end end function get!(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, secret_table_token) if val === secret_table_token val = default() if !isa(val, V) val = convert(V, val)::V end setindex!(d, val, key) return val else return val::V end end in(@nospecialize(k), v::KeySet{<:Any,<:IdDict}) = get(v.dict, k, secret_table_token) !== secret_table_token # For some AbstractDict types, it is safe to implement filter! # by deleting keys during iteration. filter!(f, d::IdDict) = filter_in_one_pass!(f, d) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/idset.jl # This file is a part of Julia. License is MIT: https://julialang.org/license # Like Set, but using IdDict mutable struct IdSet{T} <: AbstractSet{T} dict::IdDict{T,Nothing} IdSet{T}() where {T} = new(IdDict{T,Nothing}()) IdSet{T}(s::IdSet{T}) where {T} = new(copy(s.dict)) end IdSet{T}(itr) where {T} = union!(IdSet{T}(), itr) IdSet() = IdSet{Any}() copymutable(s::IdSet) = typeof(s)(s) emptymutable(s::IdSet{T}, ::Type{U}=T) where {T,U} = IdSet{U}() copy(s::IdSet) = typeof(s)(s) isempty(s::IdSet) = isempty(s.dict) length(s::IdSet) = length(s.dict) in(@nospecialize(x), s::IdSet) = haskey(s.dict, x) push!(s::IdSet, @nospecialize(x)) = (s.dict[x] = nothing; s) pop!(s::IdSet, @nospecialize(x)) = (pop!(s.dict, x); x) pop!(s::IdSet, @nospecialize(x), @nospecialize(default)) = (x in s ? pop!(s, x) : default) delete!(s::IdSet, @nospecialize(x)) = (delete!(s.dict, x); s) sizehint!(s::IdSet, newsz) = (sizehint!(s.dict, newsz); s) empty!(s::IdSet) = (empty!(s.dict); s) filter!(f, d::IdSet) = unsafe_filter!(f, d) function iterate(s::IdSet, state...) y = iterate(s.dict, state...) y === nothing && return nothing ((k, _), i) = y return (k, i) end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/iterators.jlลฐ# This file is a part of Julia. License is MIT: https://julialang.org/license """ Methods for working with Iterators. """ baremodule Iterators # small dance to make this work from Base or Intrinsics import ..@__MODULE__, ..parentmodule const Base = parentmodule(@__MODULE__) using .Base: @inline, Pair, Pairs, AbstractDict, IndexLinear, IndexStyle, AbstractVector, Vector, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, @isdefined, @boundscheck, @inbounds, Generator, AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom, (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape using Core: @doc if Base !== Core.Compiler using .Base: cld, fld, SubArray, view, resize!, IndexCartesian using .Base.Checked: checked_mul else # Checked.checked_mul is not available during bootstrapping: const checked_mul = * end import .Base: first, last, isempty, length, size, axes, ndims, eltype, IteratorSize, IteratorEltype, haskey, keys, values, pairs, getindex, setindex!, get, iterate, popfirst!, isdone, peek, intersect export enumerate, zip, rest, countfrom, take, drop, takewhile, dropwhile, cycle, repeated, product, flatten, flatmap if Base !== Core.Compiler export partition end """ Iterators.map(f, iterators...) Create a lazy mapping. This is another syntax for writing `(f(args...) for args in zip(iterators...))`. !!! compat "Julia 1.6" This function requires at least Julia 1.6. # Examples ```jldoctest julia> collect(Iterators.map(x -> x^2, 1:3)) 3-element Vector{Int64}: 1 4 9 ``` """ map(f, args...) = Base.Generator(f, args...) tail_if_any(::Tuple{}) = () tail_if_any(x::Tuple) = tail(x) _min_length(a, b, ::IsInfinite, ::IsInfinite) = min(length(a),length(b)) # inherit behaviour, error _min_length(a, b, A, ::IsInfinite) = length(a) _min_length(a, b, ::IsInfinite, B) = length(b) _min_length(a, b, A, B) = min(length(a),length(b)) _diff_length(a, b, A, ::IsInfinite) = 0 _diff_length(a, b, ::IsInfinite, ::IsInfinite) = 0 _diff_length(a, b, ::IsInfinite, B) = length(a) # inherit behaviour, error function _diff_length(a, b, A, B) m, n = length(a), length(b) return m > n ? m - n : zero(n - m) end and_iteratorsize(isz::T, ::T) where {T} = isz and_iteratorsize(::HasLength, ::HasShape) = HasLength() and_iteratorsize(::HasShape, ::HasLength) = HasLength() and_iteratorsize(a, b) = SizeUnknown() and_iteratoreltype(iel::T, ::T) where {T} = iel and_iteratoreltype(a, b) = EltypeUnknown() ## Reverse-order iteration for arrays and other collections. Collections ## should implement iterate etcetera if possible/practical. """ Iterators.reverse(itr) Given an iterator `itr`, then `reverse(itr)` is an iterator over the same collection but in the reverse order. This iterator is "lazy" in that it does not make a copy of the collection in order to reverse it; see [`Base.reverse`](@ref) for an eager implementation. (By default, this returns an `Iterators.Reverse` object wrapping `itr`, which is iterable if the corresponding [`iterate`](@ref) methods are defined, but some `itr` types may implement more specialized `Iterators.reverse` behaviors.) Not all iterator types `T` support reverse-order iteration. If `T` doesn't, then iterating over `Iterators.reverse(itr::T)` will throw a [`MethodError`](@ref) because of the missing `iterate` methods for `Iterators.Reverse{T}`. (To implement these methods, the original iterator `itr::T` can be obtained from an `r::Iterators.Reverse{T}` object by `r.itr`; more generally, one can use `Iterators.reverse(r)`.) # Examples ```jldoctest julia> foreach(println, Iterators.reverse(1:5)) 5 4 3 2 1 ``` """ reverse(itr) = Reverse(itr) struct Reverse{T} itr::T end eltype(::Type{Reverse{T}}) where {T} = eltype(T) length(r::Reverse) = length(r.itr) size(r::Reverse) = size(r.itr) IteratorSize(::Type{Reverse{T}}) where {T} = IteratorSize(T) IteratorEltype(::Type{Reverse{T}}) where {T} = IteratorEltype(T) last(r::Reverse) = first(r.itr) # the first shall be last # reverse-order array iterators: assumes more-specialized Reverse for eachindex @propagate_inbounds function iterate(A::Reverse{<:AbstractArray}, state=(reverse(eachindex(A.itr)),)) y = iterate(state...) y === nothing && return y idx, itrs = y (A.itr[idx], (state[1], itrs)) end # Fallback method of `iterate(::Reverse{T})` which assumes the collection has `getindex(::T) and `reverse(eachindex(::T))` # don't propagate inbounds for this just in case function iterate(A::Reverse, state=(reverse(eachindex(A.itr)),)) y = iterate(state...) y === nothing && return y idx, itrs = y (A.itr[idx], (state[1], itrs)) end reverse(R::AbstractRange) = Base.reverse(R) # copying ranges is cheap reverse(G::Generator) = Generator(G.f, reverse(G.iter)) reverse(r::Reverse) = r.itr reverse(x::Union{Number,AbstractChar}) = x reverse(p::Pair) = Base.reverse(p) # copying pairs is cheap iterate(r::Reverse{<:Union{Tuple, NamedTuple}}, i::Int = length(r.itr)) = i < 1 ? nothing : (r.itr[i], i-1) # enumerate struct Enumerate{I} itr::I end """ enumerate(iter) An iterator that yields `(i, x)` where `i` is a counter starting at 1, and `x` is the `i`th value from the given iterator. It's useful when you need not only the values `x` over which you are iterating, but also the number of iterations so far. Note that `i` may not be valid for indexing `iter`, or may index a different element. This will happen if `iter` has indices that do not start at 1, and may happen for strings, dictionaries, etc. See the `pairs(IndexLinear(), iter)` method if you want to ensure that `i` is an index. # Examples ```jldoctest julia> a = ["a", "b", "c"]; julia> for (index, value) in enumerate(a) println("\$index \$value") end 1 a 2 b 3 c julia> str = "naรฏve"; julia> for (i, val) in enumerate(str) print("i = ", i, ", val = ", val, ", ") try @show(str[i]) catch e println(e) end end i = 1, val = n, str[i] = 'n' i = 2, val = a, str[i] = 'a' i = 3, val = รฏ, str[i] = 'รฏ' i = 4, val = v, StringIndexError("naรฏve", 4) i = 5, val = e, str[i] = 'v' ``` """ enumerate(iter) = Enumerate(iter) length(e::Enumerate) = length(e.itr) size(e::Enumerate) = size(e.itr) @propagate_inbounds function iterate(e::Enumerate, state=(1,)) i, rest = state[1], tail(state) n = iterate(e.itr, rest...) n === nothing && return n (i, n[1]), (i+1, n[2]) end last(e::Enumerate) = (length(e.itr), e.itr[end]) eltype(::Type{Enumerate{I}}) where {I} = TupleOrBottom(Int, eltype(I)) IteratorSize(::Type{Enumerate{I}}) where {I} = IteratorSize(I) IteratorEltype(::Type{Enumerate{I}}) where {I} = IteratorEltype(I) @inline function iterate(r::Reverse{<:Enumerate}) ri = reverse(r.itr.itr) iterate(r, (length(ri), ri)) end @inline function iterate(r::Reverse{<:Enumerate}, state) i, ri, rest = state[1], state[2], tail(tail(state)) n = iterate(ri, rest...) n === nothing && return n (i, n[1]), (i-1, ri, n[2]) end """ pairs(IndexLinear(), A) pairs(IndexCartesian(), A) pairs(IndexStyle(A), A) An iterator that accesses each element of the array `A`, returning `i => x`, where `i` is the index for the element and `x = A[i]`. Identical to `pairs(A)`, except that the style of index can be selected. Also similar to `enumerate(A)`, except `i` will be a valid index for `A`, while `enumerate` always counts from 1 regardless of the indices of `A`. Specifying [`IndexLinear()`](@ref) ensures that `i` will be an integer; specifying [`IndexCartesian()`](@ref) ensures that `i` will be a [`Base.CartesianIndex`](@ref); specifying `IndexStyle(A)` chooses whichever has been defined as the native indexing style for array `A`. Mutation of the bounds of the underlying array will invalidate this iterator. # Examples ```jldoctest julia> A = ["a" "d"; "b" "e"; "c" "f"]; julia> for (index, value) in pairs(IndexStyle(A), A) println("\$index \$value") end 1 a 2 b 3 c 4 d 5 e 6 f julia> S = view(A, 1:2, :); julia> for (index, value) in pairs(IndexStyle(S), S) println("\$index \$value") end CartesianIndex(1, 1) a CartesianIndex(2, 1) b CartesianIndex(1, 2) d CartesianIndex(2, 2) e ``` See also [`IndexStyle`](@ref), [`axes`](@ref). """ pairs(::IndexLinear, A::AbstractArray) = Pairs(A, LinearIndices(A)) # preserve indexing capabilities for known indexable types # faster than zip(keys(a), values(a)) for arrays pairs(tuple::Tuple) = Pairs{Int}(tuple, keys(tuple)) pairs(nt::NamedTuple) = Pairs{Symbol}(nt, keys(nt)) pairs(v::Core.SimpleVector) = Pairs(v, LinearIndices(v)) pairs(A::AbstractVector) = pairs(IndexLinear(), A) # pairs(v::Pairs) = v # listed for reference, but already defined from being an AbstractDict if Base !== Core.Compiler pairs(::IndexCartesian, A::AbstractArray) = Pairs(A, Base.CartesianIndices(axes(A))) pairs(A::AbstractArray) = pairs(IndexCartesian(), A) end length(v::Pairs) = length(getfield(v, :itr)) axes(v::Pairs) = axes(getfield(v, :itr)) size(v::Pairs) = size(getfield(v, :itr)) Base.@eval @propagate_inbounds function _pairs_elt(p::Pairs{K, V}, idx) where {K, V} return $(Expr(:new, :(Pair{K, V}), :idx, :(getfield(p, :data)[idx]))) end @propagate_inbounds function iterate(p::Pairs{K, V}, state...) where {K, V} x = iterate(getfield(p, :itr), state...) x === nothing && return x idx, next = x return (_pairs_elt(p, idx), next) end @propagate_inbounds function iterate(r::Reverse{<:Pairs}, state=(reverse(getfield(r.itr, :itr)),)) x = iterate(state...) x === nothing && return x idx, next = x return (_pairs_elt(r.itr, idx), (state[1], next)) end @inline isdone(v::Pairs, state...) = isdone(getfield(v, :itr), state...) IteratorSize(::Type{<:Pairs{<:Any, <:Any, I}}) where {I} = IteratorSize(I) IteratorSize(::Type{<:Pairs{<:Any, <:Any, <:AbstractUnitRange, <:Tuple}}) = HasLength() function last(v::Pairs{K, V}) where {K, V} idx = last(getfield(v, :itr)) return Pair{K, V}(idx, v[idx]) end haskey(v::Pairs, key) = (key in getfield(v, :itr)) keys(v::Pairs) = getfield(v, :itr) values(v::Pairs) = getfield(v, :data) # TODO: this should be a view of data subset by itr getindex(v::Pairs, key) = getfield(v, :data)[key] setindex!(v::Pairs, value, key) = (getfield(v, :data)[key] = value; v) get(v::Pairs, key, default) = get(getfield(v, :data), key, default) get(f::Base.Callable, v::Pairs, key) = get(f, getfield(v, :data), key) # zip struct Zip{Is<:Tuple} is::Is end """ zip(iters...) Run multiple iterators at the same time, until any of them is exhausted. The value type of the `zip` iterator is a tuple of values of its subiterators. !!! note `zip` orders the calls to its subiterators in such a way that stateful iterators will not advance when another iterator finishes in the current iteration. !!! note `zip()` with no arguments yields an infinite iterator of empty tuples. See also: [`enumerate`](@ref), [`Base.splat`](@ref). # Examples ```jldoctest julia> a = 1:5 1:5 julia> b = ["e","d","b","c","a"] 5-element Vector{String}: "e" "d" "b" "c" "a" julia> c = zip(a,b) zip(1:5, ["e", "d", "b", "c", "a"]) julia> length(c) 5 julia> first(c) (1, "e") ``` """ zip(a...) = Zip(a) function length(z::Zip) n = _zip_min_length(z.is) n === nothing && throw(ArgumentError("iterator is of undefined length")) return n end function _zip_min_length(is) i = is[1] n = _zip_min_length(tail(is)) if IteratorSize(i) isa IsInfinite return n else return n === nothing ? length(i) : min(n, length(i)) end end _zip_min_length(is::Tuple{}) = nothing size(z::Zip) = _promote_tuple_shape(Base.map(size, z.is)...) axes(z::Zip) = _promote_tuple_shape(Base.map(axes, z.is)...) _promote_tuple_shape((a,)::Tuple{OneTo}, (b,)::Tuple{OneTo}) = (intersect(a, b),) _promote_tuple_shape((m,)::Tuple{Integer}, (n,)::Tuple{Integer}) = (min(m, n),) _promote_tuple_shape(a, b) = promote_shape(a, b) _promote_tuple_shape(a, b...) = _promote_tuple_shape(a, _promote_tuple_shape(b...)) _promote_tuple_shape(a) = a eltype(::Type{Zip{Is}}) where {Is<:Tuple} = TupleOrBottom(map(eltype, fieldtypes(Is))...) #eltype(::Type{Zip{Tuple{}}}) = Tuple{} #eltype(::Type{Zip{Tuple{A}}}) where {A} = Tuple{eltype(A)} #eltype(::Type{Zip{Tuple{A, B}}}) where {A, B} = Tuple{eltype(A), eltype(B)} @inline isdone(z::Zip) = _zip_any_isdone(z.is, Base.map(_ -> (), z.is)) @inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, Base.map(tuple, ss)) @inline function _zip_any_isdone(is, ss) d1 = isdone(is[1], ss[1]...) d1 === true && return true return d1 | _zip_any_isdone(tail(is), tail(ss)) end @inline _zip_any_isdone(::Tuple{}, ::Tuple{}) = false @propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, Base.map(_ -> (), z.is)) @propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, Base.map(tuple, ss)) # This first queries isdone from every iterator. If any gives true, it immediately returns # nothing. It then iterates all those where isdone returned missing, afterwards all those # it returned false, again terminating immediately if any iterator is exhausted. Finally, # the results are interleaved appropriately. @propagate_inbounds function _zip_iterate_all(is, ss) d, ds = _zip_isdone(is, ss) d && return nothing xs1 = _zip_iterate_some(is, ss, ds, missing) xs1 === nothing && return nothing xs2 = _zip_iterate_some(is, ss, ds, false) xs2 === nothing && return nothing return _zip_iterate_interleave(xs1, xs2, ds) end @propagate_inbounds function _zip_iterate_some(is, ss, ds::Tuple{T,Vararg{Any}}, f::T) where T x = iterate(is[1], ss[1]...) x === nothing && return nothing y = _zip_iterate_some(tail(is), tail(ss), tail(ds), f) y === nothing && return nothing return (x, y...) end @propagate_inbounds _zip_iterate_some(is, ss, ds::Tuple{Any,Vararg{Any}}, f) = _zip_iterate_some(tail(is), tail(ss), tail(ds), f) _zip_iterate_some(::Tuple{}, ::Tuple{}, ::Tuple{}, ::Any) = () function _zip_iterate_interleave(xs1, xs2, ds) t = _zip_iterate_interleave(tail(xs1), xs2, tail(ds)) ((xs1[1][1], t[1]...), (xs1[1][2], t[2]...)) end function _zip_iterate_interleave(xs1, xs2, ds::Tuple{Bool,Vararg{Any}}) t = _zip_iterate_interleave(xs1, tail(xs2), tail(ds)) ((xs2[1][1], t[1]...), (xs2[1][2], t[2]...)) end _zip_iterate_interleave(::Tuple{}, ::Tuple{}, ::Tuple{}) = ((), ()) function _zip_isdone(is, ss) d = isdone(is[1], ss[1]...) dยด, ds = _zip_isdone(tail(is), tail(ss)) return (d === true || dยด, (d, ds...)) end _zip_isdone(::Tuple{}, ::Tuple{}) = (false, ()) IteratorSize(::Type{Zip{Is}}) where {Is<:Tuple} = zip_iteratorsize(ntuple(n -> IteratorSize(fieldtype(Is, n)), _counttuple(Is)::Int)...) IteratorEltype(::Type{Zip{Is}}) where {Is<:Tuple} = zip_iteratoreltype(ntuple(n -> IteratorEltype(fieldtype(Is, n)), _counttuple(Is)::Int)...) zip_iteratorsize() = IsInfinite() zip_iteratorsize(I) = I zip_iteratorsize(a, b) = and_iteratorsize(a,b) # as `and_iteratorsize` but inherit `Union{HasLength,IsInfinite}` of the shorter iterator zip_iteratorsize(::HasLength, ::IsInfinite) = HasLength() zip_iteratorsize(::HasShape, ::IsInfinite) = HasLength() zip_iteratorsize(a::IsInfinite, b) = zip_iteratorsize(b,a) zip_iteratorsize(a::IsInfinite, b::IsInfinite) = IsInfinite() zip_iteratorsize(a, b, tail...) = zip_iteratorsize(a, zip_iteratorsize(b, tail...)) zip_iteratoreltype() = HasEltype() zip_iteratoreltype(a) = a zip_iteratoreltype(a, tail...) = and_iteratoreltype(a, zip_iteratoreltype(tail...)) reverse(z::Zip) = Zip(Base.map(reverse, z.is)) # n.b. we assume all iterators are the same length last(z::Zip) = getindex.(z.is, minimum(Base.map(lastindex, z.is))) # filter struct Filter{F,I} flt::F itr::I end """ Iterators.filter(flt, itr) Given a predicate function `flt` and an iterable object `itr`, return an iterable object which upon iteration yields the elements `x` of `itr` that satisfy `flt(x)`. The order of the original iterator is preserved. This function is *lazy*; that is, it is guaranteed to return in ``ฮ˜(1)`` time and use ``ฮ˜(1)`` additional space, and `flt` will not be called by an invocation of `filter`. Calls to `flt` will be made when iterating over the returned iterable object. These calls are not cached and repeated calls will be made when reiterating. See [`Base.filter`](@ref) for an eager implementation of filtering for arrays. # Examples ```jldoctest julia> f = Iterators.filter(isodd, [1, 2, 3, 4, 5]) Base.Iterators.Filter{typeof(isodd), Vector{Int64}}(isodd, [1, 2, 3, 4, 5]) julia> foreach(println, f) 1 3 5 julia> [x for x in [1, 2, 3, 4, 5] if isodd(x)] # collects a generator over Iterators.filter 3-element Vector{Int64}: 1 3 5 ``` """ filter(flt, itr) = Filter(flt, itr) function iterate(f::Filter, state...) y = iterate(f.itr, state...) while y !== nothing if f.flt(y[1]) return y end y = iterate(f.itr, y[2]) end nothing end eltype(::Type{Filter{F,I}}) where {F,I} = eltype(I) IteratorEltype(::Type{Filter{F,I}}) where {F,I} = IteratorEltype(I) IteratorSize(::Type{<:Filter}) = SizeUnknown() reverse(f::Filter) = Filter(f.flt, reverse(f.itr)) last(f::Filter) = first(reverse(f)) # Accumulate -- partial reductions of a function over an iterator struct Accumulate{F,I,T} f::F itr::I init::T end """ Iterators.accumulate(f, itr; [init]) Given a 2-argument function `f` and an iterator `itr`, return a new iterator that successively applies `f` to the previous value and the next element of `itr`. This is effectively a lazy version of [`Base.accumulate`](@ref). !!! compat "Julia 1.5" Keyword argument `init` is added in Julia 1.5. # Examples ```jldoctest julia> a = Iterators.accumulate(+, [1,2,3,4]); julia> foreach(println, a) 1 3 6 10 julia> b = Iterators.accumulate(/, (2, 5, 2, 5); init = 100); julia> collect(b) 4-element Vector{Float64}: 50.0 10.0 5.0 1.0 ``` """ accumulate(f, itr; init = Base._InitialValue()) = Accumulate(f, itr, init) function iterate(itr::Accumulate) state = iterate(itr.itr) if state === nothing return nothing end val = Base.BottomRF(itr.f)(itr.init, state[1]) return (val, (val, state[2])) end function iterate(itr::Accumulate, state) nxt = iterate(itr.itr, state[2]) if nxt === nothing return nothing end val = itr.f(state[1], nxt[1]) return (val, (val, nxt[2])) end length(itr::Accumulate) = length(itr.itr) size(itr::Accumulate) = size(itr.itr) IteratorSize(::Type{<:Accumulate{<:Any,I}}) where {I} = IteratorSize(I) IteratorEltype(::Type{<:Accumulate}) = EltypeUnknown() # Rest -- iterate starting at the given state struct Rest{I,S} itr::I st::S end """ rest(iter, state) An iterator that yields the same elements as `iter`, but starting at the given `state`. See also: [`Iterators.drop`](@ref), [`Iterators.peel`](@ref), [`Base.rest`](@ref). # Examples ```jldoctest julia> collect(Iterators.rest([1,2,3,4], 2)) 3-element Vector{Int64}: 2 3 4 ``` """ rest(itr,state) = Rest(itr,state) rest(itr::Rest,state) = Rest(itr.itr,state) rest(itr) = itr """ peel(iter) Returns the first element and an iterator over the remaining elements. If the iterator is empty return `nothing` (like `iterate`). !!! compat "Julia 1.7" Prior versions throw a BoundsError if the iterator is empty. See also: [`Iterators.drop`](@ref), [`Iterators.take`](@ref). # Examples ```jldoctest julia> (a, rest) = Iterators.peel("abc"); julia> a 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> collect(rest) 2-element Vector{Char}: 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) ``` """ function peel(itr) y = iterate(itr) y === nothing && return y val, s = y val, rest(itr, s) end @propagate_inbounds iterate(i::Rest, st=i.st) = iterate(i.itr, st) isdone(i::Rest, st...) = isdone(i.itr, st...) eltype(::Type{<:Rest{I}}) where {I} = eltype(I) IteratorEltype(::Type{<:Rest{I}}) where {I} = IteratorEltype(I) rest_iteratorsize(a) = SizeUnknown() rest_iteratorsize(::IsInfinite) = IsInfinite() IteratorSize(::Type{<:Rest{I}}) where {I} = rest_iteratorsize(IteratorSize(I)) # Count -- infinite counting struct Count{T,S} start::T step::S end """ countfrom(start=1, step=1) An iterator that counts forever, starting at `start` and incrementing by `step`. # Examples ```jldoctest julia> for v in Iterators.countfrom(5, 2) v > 10 && break println(v) end 5 7 9 ``` """ countfrom(start::T, step::S) where {T,S} = Count{typeof(start+step),S}(start, step) countfrom(start::Number, step::Number) = Count(promote(start, step)...) countfrom(start) = Count(start, oneunit(start)) countfrom() = Count(1, 1) eltype(::Type{<:Count{T}}) where {T} = T iterate(it::Count, state=it.start) = (state, state + it.step) IteratorSize(::Type{<:Count}) = IsInfinite() # Take -- iterate through the first n elements struct Take{I} xs::I n::Int function Take(xs::I, n::Integer) where {I} n < 0 && throw(ArgumentError("Take length must be nonnegative")) return new{I}(xs, n) end end """ take(iter, n) An iterator that generates at most the first `n` elements of `iter`. See also: [`drop`](@ref Iterators.drop), [`peel`](@ref Iterators.peel), [`first`](@ref), [`Base.take!`](@ref). # Examples ```jldoctest julia> a = 1:2:11 1:2:11 julia> collect(a) 6-element Vector{Int64}: 1 3 5 7 9 11 julia> collect(Iterators.take(a,3)) 3-element Vector{Int64}: 1 3 5 ``` """ take(xs, n::Integer) = Take(xs, Int(n)) take(xs::Take, n::Integer) = Take(xs.xs, min(Int(n), xs.n)) eltype(::Type{Take{I}}) where {I} = eltype(I) IteratorEltype(::Type{Take{I}}) where {I} = IteratorEltype(I) take_iteratorsize(a) = HasLength() take_iteratorsize(::SizeUnknown) = SizeUnknown() IteratorSize(::Type{Take{I}}) where {I} = take_iteratorsize(IteratorSize(I)) length(t::Take) = _min_length(t.xs, 1:t.n, IteratorSize(t.xs), HasLength()) isdone(t::Take) = isdone(t.xs) isdone(t::Take, state) = (state[1] <= 0) | isdone(t.xs, tail(state)) @propagate_inbounds function iterate(it::Take, state=(it.n,)) n, rest = state[1], tail(state) n <= 0 && return nothing y = iterate(it.xs, rest...) y === nothing && return nothing return y[1], (n - 1, y[2]) end # Drop -- iterator through all but the first n elements struct Drop{I} xs::I n::Int function Drop(xs::I, n::Integer) where {I} n < 0 && throw(ArgumentError("Drop length must be nonnegative")) return new{I}(xs, n) end end """ drop(iter, n) An iterator that generates all but the first `n` elements of `iter`. # Examples ```jldoctest julia> a = 1:2:11 1:2:11 julia> collect(a) 6-element Vector{Int64}: 1 3 5 7 9 11 julia> collect(Iterators.drop(a,4)) 2-element Vector{Int64}: 9 11 ``` """ drop(xs, n::Integer) = Drop(xs, Int(n)) drop(xs::Take, n::Integer) = Take(drop(xs.xs, Int(n)), max(0, xs.n - Int(n))) drop(xs::Drop, n::Integer) = Drop(xs.xs, Int(n) + xs.n) eltype(::Type{Drop{I}}) where {I} = eltype(I) IteratorEltype(::Type{Drop{I}}) where {I} = IteratorEltype(I) drop_iteratorsize(::SizeUnknown) = SizeUnknown() drop_iteratorsize(::Union{HasShape, HasLength}) = HasLength() drop_iteratorsize(::IsInfinite) = IsInfinite() IteratorSize(::Type{Drop{I}}) where {I} = drop_iteratorsize(IteratorSize(I)) length(d::Drop) = _diff_length(d.xs, 1:d.n, IteratorSize(d.xs), HasLength()) function iterate(it::Drop) y = iterate(it.xs) for i in 1:it.n y === nothing && return y y = iterate(it.xs, y[2]) end y end iterate(it::Drop, state) = iterate(it.xs, state) isdone(it::Drop, state) = isdone(it.xs, state) # takewhile struct TakeWhile{I,P<:Function} pred::P xs::I end """ takewhile(pred, iter) An iterator that generates element from `iter` as long as predicate `pred` is true, afterwards, drops every element. !!! compat "Julia 1.4" This function requires at least Julia 1.4. # Examples ```jldoctest julia> s = collect(1:5) 5-element Vector{Int64}: 1 2 3 4 5 julia> collect(Iterators.takewhile(<(3),s)) 2-element Vector{Int64}: 1 2 ``` """ takewhile(pred,xs) = TakeWhile(pred,xs) function iterate(ibl::TakeWhile, itr...) y = iterate(ibl.xs,itr...) y === nothing && return nothing ibl.pred(y[1]) || return nothing y end IteratorSize(::Type{<:TakeWhile}) = SizeUnknown() eltype(::Type{TakeWhile{I,P}} where P) where {I} = eltype(I) IteratorEltype(::Type{TakeWhile{I, P}} where P) where {I} = IteratorEltype(I) # dropwhile struct DropWhile{I,P<:Function} pred::P xs::I end """ dropwhile(pred, iter) An iterator that drops element from `iter` as long as predicate `pred` is true, afterwards, returns every element. !!! compat "Julia 1.4" This function requires at least Julia 1.4. # Examples ```jldoctest julia> s = collect(1:5) 5-element Vector{Int64}: 1 2 3 4 5 julia> collect(Iterators.dropwhile(<(3),s)) 3-element Vector{Int64}: 3 4 5 ``` """ dropwhile(pred,itr) = DropWhile(pred,itr) iterate(ibl::DropWhile,itr) = iterate(ibl.xs, itr) function iterate(ibl::DropWhile) y = iterate(ibl.xs) while y !== nothing ibl.pred(y[1]) || break y = iterate(ibl.xs,y[2]) end y end IteratorSize(::Type{<:DropWhile}) = SizeUnknown() eltype(::Type{DropWhile{I,P}}) where {I,P} = eltype(I) IteratorEltype(::Type{DropWhile{I,P}}) where {I,P} = IteratorEltype(I) # Cycle an iterator forever struct Cycle{I} xs::I end """ cycle(iter) An iterator that cycles through `iter` forever. If `iter` is empty, so is `cycle(iter)`. See also: [`Iterators.repeated`](@ref), [`Base.repeat`](@ref). # Examples ```jldoctest julia> for (i, v) in enumerate(Iterators.cycle("hello")) print(v) i > 10 && break end hellohelloh ``` """ cycle(xs) = Cycle(xs) eltype(::Type{Cycle{I}}) where {I} = eltype(I) IteratorEltype(::Type{Cycle{I}}) where {I} = IteratorEltype(I) IteratorSize(::Type{Cycle{I}}) where {I} = IsInfinite() iterate(it::Cycle) = iterate(it.xs) isdone(it::Cycle) = isdone(it.xs) isdone(it::Cycle, state) = false function iterate(it::Cycle, state) y = iterate(it.xs, state) y === nothing && return iterate(it) y end reverse(it::Cycle) = Cycle(reverse(it.xs)) last(it::Cycle) = last(it.xs) # Repeated - repeat an object infinitely many times struct Repeated{O} x::O end repeated(x) = Repeated(x) """ repeated(x[, n::Int]) An iterator that generates the value `x` forever. If `n` is specified, generates `x` that many times (equivalent to `take(repeated(x), n)`). See also: [`Iterators.cycle`](@ref), [`Base.repeat`](@ref). # Examples ```jldoctest julia> a = Iterators.repeated([1 2], 4); julia> collect(a) 4-element Vector{Matrix{Int64}}: [1 2] [1 2] [1 2] [1 2] ``` """ repeated(x, n::Integer) = take(repeated(x), Int(n)) eltype(::Type{Repeated{O}}) where {O} = O iterate(it::Repeated, state...) = (it.x, nothing) IteratorSize(::Type{<:Repeated}) = IsInfinite() IteratorEltype(::Type{<:Repeated}) = HasEltype() reverse(it::Union{Repeated,Take{<:Repeated}}) = it last(it::Union{Repeated,Take{<:Repeated}}) = first(it) # Product -- cartesian product of iterators struct ProductIterator{T<:Tuple} iterators::T end """ product(iters...) Return an iterator over the product of several iterators. Each generated element is a tuple whose `i`th element comes from the `i`th argument iterator. The first iterator changes the fastest. See also: [`zip`](@ref), [`Iterators.flatten`](@ref). # Examples ```jldoctest julia> collect(Iterators.product(1:2, 3:5)) 2ร—3 Matrix{Tuple{Int64, Int64}}: (1, 3) (1, 4) (1, 5) (2, 3) (2, 4) (2, 5) julia> ans == [(x,y) for x in 1:2, y in 3:5] # collects a generator involving Iterators.product true ``` """ product(iters...) = ProductIterator(iters) IteratorSize(::Type{ProductIterator{Tuple{}}}) = HasShape{0}() IteratorSize(::Type{ProductIterator{T}}) where {T<:Tuple} = prod_iteratorsize(ntuple(n -> IteratorSize(fieldtype(T, n)), _counttuple(T)::Int)..., HasShape{0}()) prod_iteratorsize() = HasShape{0}() prod_iteratorsize(I) = I prod_iteratorsize(::HasLength, ::HasLength) = HasShape{2}() prod_iteratorsize(::HasLength, ::HasShape{N}) where {N} = HasShape{N+1}() prod_iteratorsize(::HasShape{N}, ::HasLength) where {N} = HasShape{N+1}() prod_iteratorsize(::HasShape{M}, ::HasShape{N}) where {M,N} = HasShape{M+N}() # products can have an infinite iterator prod_iteratorsize(::IsInfinite, ::IsInfinite) = IsInfinite() prod_iteratorsize(a, ::IsInfinite) = IsInfinite() prod_iteratorsize(::IsInfinite, b) = IsInfinite() prod_iteratorsize(a, b) = SizeUnknown() prod_iteratorsize(a, b, tail...) = prod_iteratorsize(a, prod_iteratorsize(b, tail...)) size(P::ProductIterator) = _prod_size(P.iterators) _prod_size(::Tuple{}) = () _prod_size(t::Tuple) = (_prod_size1(t[1], IteratorSize(t[1]))..., _prod_size(tail(t))...) _prod_size1(a, ::HasShape) = size(a) _prod_size1(a, ::HasLength) = (length(a),) _prod_size1(a, A) = throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) axes(P::ProductIterator) = _prod_indices(P.iterators) _prod_indices(::Tuple{}) = () _prod_indices(t::Tuple) = (_prod_axes1(t[1], IteratorSize(t[1]))..., _prod_indices(tail(t))...) _prod_axes1(a, ::HasShape) = axes(a) _prod_axes1(a, ::HasLength) = (OneTo(length(a)),) _prod_axes1(a, A) = throw(ArgumentError("Cannot compute indices for object of type $(typeof(a))")) ndims(p::ProductIterator) = length(axes(p)) length(P::ProductIterator) = reduce(checked_mul, size(P); init=1) IteratorEltype(::Type{ProductIterator{Tuple{}}}) = HasEltype() IteratorEltype(::Type{ProductIterator{Tuple{I}}}) where {I} = IteratorEltype(I) function IteratorEltype(::Type{ProductIterator{T}}) where {T<:Tuple} E = ntuple(n -> IteratorEltype(fieldtype(T, n)), _counttuple(T)::Int) any(I -> I == EltypeUnknown(), E) && return EltypeUnknown() return E[end] end eltype(::Type{ProductIterator{I}}) where {I} = _prod_eltype(I) _prod_eltype(::Type{Tuple{}}) = Tuple{} _prod_eltype(::Type{I}) where {I<:Tuple} = TupleOrBottom(ntuple(n -> eltype(fieldtype(I, n)), _counttuple(I)::Int)...) iterate(::ProductIterator{Tuple{}}) = (), true iterate(::ProductIterator{Tuple{}}, state) = nothing @inline isdone(P::ProductIterator) = any(isdone, P.iterators) @inline function _pisdone(iters, states) iter1 = first(iters) done1 = isdone(iter1, first(states)[2]) # check step done1 === true || return done1 # false or missing done1 = isdone(iter1) # check restart done1 === true || return done1 # false or missing return _pisdone(tail(iters), tail(states)) # check tail end @inline isdone(::ProductIterator{Tuple{}}, states) = true @inline isdone(P::ProductIterator, states) = _pisdone(P.iterators, states) @inline _piterate() = () @inline function _piterate(iter1, rest...) next = iterate(iter1) next === nothing && return nothing restnext = _piterate(rest...) restnext === nothing && return nothing return (next, restnext...) end @inline function iterate(P::ProductIterator) isdone(P) === true && return nothing next = _piterate(P.iterators...) next === nothing && return nothing return (Base.map(x -> x[1], next), next) end @inline _piterate1(::Tuple{}, ::Tuple{}) = nothing @inline function _piterate1(iters, states) iter1 = first(iters) next = iterate(iter1, first(states)[2]) restnext = tail(states) if next === nothing isdone(iter1) === true && return nothing restnext = _piterate1(tail(iters), restnext) restnext === nothing && return nothing next = iterate(iter1) next === nothing && return nothing end return (next, restnext...) end @inline function iterate(P::ProductIterator, states) isdone(P, states) === true && return nothing next = _piterate1(P.iterators, states) next === nothing && return nothing return (Base.map(x -> x[1], next), next) end reverse(p::ProductIterator) = ProductIterator(Base.map(reverse, p.iterators)) last(p::ProductIterator) = Base.map(last, p.iterators) intersect(a::ProductIterator, b::ProductIterator) = ProductIterator(intersect.(a.iterators, b.iterators)) # flatten an iterator of iterators struct Flatten{I} it::I end """ flatten(iter) Given an iterator that yields iterators, return an iterator that yields the elements of those iterators. Put differently, the elements of the argument iterator are concatenated. # Examples ```jldoctest julia> collect(Iterators.flatten((1:2, 8:9))) 4-element Vector{Int64}: 1 2 8 9 julia> [(x,y) for x in 0:1 for y in 'a':'c'] # collects generators involving Iterators.flatten 6-element Vector{Tuple{Int64, Char}}: (0, 'a') (0, 'b') (0, 'c') (1, 'a') (1, 'b') (1, 'c') ``` """ flatten(itr) = Flatten(itr) eltype(::Type{Flatten{I}}) where {I} = eltype(eltype(I)) eltype(::Type{Flatten{Tuple{}}}) = eltype(Tuple{}) IteratorEltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, IteratorEltype(I)) IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) _flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) _flatteneltype(I, et) = EltypeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}, slurp...) = HasLength() # length==0 flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() flatten_iteratorsize(a, b) = SizeUnknown() _flatten_iteratorsize(sz, ::EltypeUnknown, I) = SizeUnknown() _flatten_iteratorsize(sz, ::HasEltype, I) = flatten_iteratorsize(sz, eltype(I)) _flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength() IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I) flatten_length(f, T::Type{Union{}}, slurp...) = 0 function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N} return N * length(f.it) end flatten_length(f, ::Type{<:Number}) = length(f.it) flatten_length(f, T) = throw(ArgumentError( "Iterates of the argument to Flatten are not known to have constant length")) length(f::Flatten{I}) where {I} = flatten_length(f, eltype(I)) length(f::Flatten{Tuple{}}) = 0 @propagate_inbounds function iterate(f::Flatten, state=()) if state !== () y = iterate(tail(state)...) y !== nothing && return (y[1], (state[1], state[2], y[2])) end x = (state === () ? iterate(f.it) : iterate(f.it, state[1])) x === nothing && return nothing y = iterate(x[1]) while y === nothing x = iterate(f.it, x[2]) x === nothing && return nothing y = iterate(x[1]) end return y[1], (x[2], x[1], y[2]) end reverse(f::Flatten) = Flatten(reverse(itr) for itr in reverse(f.it)) last(f::Flatten) = last(last(f.it)) """ Iterators.flatmap(f, iterators...) Equivalent to `flatten(map(f, iterators...))`. See also [`Iterators.flatten`](@ref), [`Iterators.map`](@ref). !!! compat "Julia 1.9" This function was added in Julia 1.9. # Examples ```jldoctest julia> Iterators.flatmap(n -> -n:2:n, 1:3) |> collect 9-element Vector{Int64}: -1 1 -2 0 2 -3 -1 1 3 julia> stack(n -> -n:2:n, 1:3) ERROR: DimensionMismatch: stack expects uniform slices, got axes(x) == (1:3,) while first had (1:2,) [...] julia> Iterators.flatmap(n -> (-n, 10n), 1:2) |> collect 4-element Vector{Int64}: -1 10 -2 20 julia> ans == vec(stack(n -> (-n, 10n), 1:2)) true ``` """ flatmap(f, c...) = flatten(map(f, c...)) if Base !== Core.Compiler # views are not defined @doc """ partition(collection, n) Iterate over a collection `n` elements at a time. # Examples ```jldoctest julia> collect(Iterators.partition([1,2,3,4,5], 2)) 3-element Vector{SubArray{Int64, 1, Vector{Int64}, Tuple{UnitRange{Int64}}, true}}: [1, 2] [3, 4] [5] ``` """ function partition(c, n::Integer) n < 1 && throw(ArgumentError("cannot create partitions of length $n")) return PartitionIterator(c, Int(n)) end struct PartitionIterator{T} c::T n::Int end # Partitions are explicitly a linear indexing operation, so reshape to 1-d immediately PartitionIterator(A::AbstractArray, n::Int) = PartitionIterator(Base.vec(A), n) PartitionIterator(v::AbstractVector, n::Int) = PartitionIterator{typeof(v)}(v, n) eltype(::Type{PartitionIterator{T}}) where {T} = Vector{eltype(T)} # Arrays use a generic `view`-of-a-`vec`, so we cannot exactly predict what we'll get back eltype(::Type{PartitionIterator{T}}) where {T<:AbstractArray} = AbstractVector{eltype(T)} # But for some common implementations in Base we know the answer exactly eltype(::Type{PartitionIterator{T}}) where {T<:Vector} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} IteratorEltype(::Type{PartitionIterator{T}}) where {T} = IteratorEltype(T) IteratorEltype(::Type{PartitionIterator{T}}) where {T<:AbstractArray} = EltypeUnknown() IteratorEltype(::Type{PartitionIterator{T}}) where {T<:Vector} = IteratorEltype(T) partition_iteratorsize(::HasShape) = HasLength() partition_iteratorsize(isz) = isz function IteratorSize(::Type{PartitionIterator{T}}) where {T} partition_iteratorsize(IteratorSize(T)) end function length(itr::PartitionIterator) l = length(itr.c) return cld(l, itr.n) end function iterate(itr::PartitionIterator{<:AbstractRange}, state = firstindex(itr.c)) state > lastindex(itr.c) && return nothing r = min(state + itr.n - 1, lastindex(itr.c)) return @inbounds itr.c[state:r], r + 1 end function iterate(itr::PartitionIterator{<:AbstractArray}, state = firstindex(itr.c)) state > lastindex(itr.c) && return nothing r = min(state + itr.n - 1, lastindex(itr.c)) return @inbounds view(itr.c, state:r), r + 1 end struct IterationCutShort; end function iterate(itr::PartitionIterator, state...) # This is necessary to remember whether we cut the # last element short. In such cases, we do return that # element, but not the next one state === (IterationCutShort(),) && return nothing v = Vector{eltype(itr.c)}(undef, itr.n) i = 0 y = iterate(itr.c, state...) while y !== nothing i += 1 v[i] = y[1] if i >= itr.n break end y = iterate(itr.c, y[2]) end i === 0 && return nothing return resize!(v, i), y === nothing ? IterationCutShort() : y[2] end @doc """ Stateful(itr) There are several different ways to think about this iterator wrapper: 1. It provides a mutable wrapper around an iterator and its iteration state. 2. It turns an iterator-like abstraction into a `Channel`-like abstraction. 3. It's an iterator that mutates to become its own rest iterator whenever an item is produced. `Stateful` provides the regular iterator interface. Like other mutable iterators (e.g. [`Base.Channel`](@ref)), if iteration is stopped early (e.g. by a [`break`](@ref) in a [`for`](@ref) loop), iteration can be resumed from the same spot by continuing to iterate over the same iterator object (in contrast, an immutable iterator would restart from the beginning). # Examples ```jldoctest julia> a = Iterators.Stateful("abcdef"); julia> isempty(a) false julia> popfirst!(a) 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> collect(Iterators.take(a, 3)) 3-element Vector{Char}: 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) julia> collect(a) 2-element Vector{Char}: 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase) 'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase) julia> Iterators.reset!(a); popfirst!(a) 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> Iterators.reset!(a, "hello"); popfirst!(a) 'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase) ``` ```jldoctest julia> a = Iterators.Stateful([1,1,1,2,3,4]); julia> for x in a; x == 1 || break; end julia> peek(a) 3 julia> sum(a) # Sum the remaining elements 7 ``` """ mutable struct Stateful{T, VS, N<:Integer} itr::T # A bit awkward right now, but adapted to the new iteration protocol nextvalstate::Union{VS, Nothing} # Number of remaining elements, if itr is HasLength or HasShape. # if not, store -1 - number_of_consumed_elements. # This allows us to defer calculating length until asked for. # See PR #45924 remaining::N @inline function Stateful{<:Any, Any}(itr::T) where {T} itl = iterlength(itr) new{T, Any, typeof(itl)}(itr, iterate(itr), itl) end @inline function Stateful(itr::T) where {T} VS = approx_iter_type(T) itl = iterlength(itr) return new{T, VS, typeof(itl)}(itr, iterate(itr)::VS, itl) end end function iterlength(it)::Signed if IteratorSize(it) isa Union{HasShape, HasLength} return length(it) else -1 end end function reset!(s::Stateful{T,VS}, itr::T=s.itr) where {T,VS} s.itr = itr itl = iterlength(itr) setfield!(s, :nextvalstate, iterate(itr)) s.remaining = itl s end # Try to find an appropriate type for the (value, state tuple), # by doing a recursive unrolling of the iteration protocol up to # fixpoint. approx_iter_type(itrT::Type) = _approx_iter_type(itrT, Base._return_type(iterate, Tuple{itrT})) # Not actually called, just passed to return type to avoid # having to typesplit on Nothing function doiterate(itr, valstate::Union{Nothing, Tuple{Any, Any}}) valstate === nothing && return nothing val, st = valstate return iterate(itr, st) end function _approx_iter_type(itrT::Type, vstate::Type) vstate <: Union{Nothing, Tuple{Any, Any}} || return Any vstate <: Union{} && return Union{} itrT <: Union{} && return Union{} nextvstate = Base._return_type(doiterate, Tuple{itrT, vstate}) return (nextvstate <: vstate ? vstate : Any) end Stateful(x::Stateful) = x convert(::Type{Stateful}, itr) = Stateful(itr) @inline isdone(s::Stateful, st=nothing) = s.nextvalstate === nothing @inline function popfirst!(s::Stateful) vs = s.nextvalstate if vs === nothing throw(Base.EOFError()) else val, state = vs Core.setfield!(s, :nextvalstate, iterate(s.itr, state)) rem = s.remaining s.remaining = rem - typeof(rem)(1) return val end end @inline function peek(s::Stateful, sentinel=nothing) ns = s.nextvalstate return ns !== nothing ? ns[1] : sentinel end @inline iterate(s::Stateful, state=nothing) = s.nextvalstate === nothing ? nothing : (popfirst!(s), nothing) IteratorSize(::Type{<:Stateful{T}}) where {T} = IteratorSize(T) isa HasShape ? HasLength() : IteratorSize(T) eltype(::Type{<:Stateful{T}}) where {T} = eltype(T) IteratorEltype(::Type{<:Stateful{T}}) where {T} = IteratorEltype(T) function length(s::Stateful) rem = s.remaining # If rem is actually remaining length, return it. # else, rem is number of consumed elements. if rem >= 0 rem else length(s.itr) - (typeof(rem)(1) - rem) end end end # if statement several hundred lines above """ only(x) Return the one and only element of collection `x`, or throw an [`ArgumentError`](@ref) if the collection has zero or multiple elements. See also [`first`](@ref), [`last`](@ref). !!! compat "Julia 1.4" This method requires at least Julia 1.4. # Examples ```jldoctest julia> only(["a"]) "a" julia> only("a") 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> only(()) ERROR: ArgumentError: Tuple contains 0 elements, must contain exactly 1 element Stacktrace: [...] julia> only(('a', 'b')) ERROR: ArgumentError: Tuple contains 2 elements, must contain exactly 1 element Stacktrace: [...] ``` """ @propagate_inbounds function only(x) i = iterate(x) @boundscheck if i === nothing throw(ArgumentError("Collection is empty, must contain exactly 1 element")) end (ret, state) = i::NTuple{2,Any} @boundscheck if iterate(x, state) !== nothing throw(ArgumentError("Collection has multiple elements, must contain exactly 1 element")) end return ret end # Collections of known size only(x::Ref) = x[] only(x::Number) = x only(x::Char) = x only(x::Tuple{Any}) = x[1] only(x::Tuple) = throw( ArgumentError("Tuple contains $(length(x)) elements, must contain exactly 1 element") ) only(a::AbstractArray{<:Any, 0}) = @inbounds return a[] only(x::NamedTuple{<:Any, <:Tuple{Any}}) = first(x) only(x::NamedTuple) = throw( ArgumentError("NamedTuple contains $(length(x)) elements, must contain exactly 1 element") ) end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/namedtuple.jlNK# This file is a part of Julia. License is MIT: https://julialang.org/license """ NamedTuple `NamedTuple`s are, as their name suggests, named [`Tuple`](@ref)s. That is, they're a tuple-like collection of values, where each entry has a unique name, represented as a [`Symbol`](@ref). Like `Tuple`s, `NamedTuple`s are immutable; neither the names nor the values can be modified in place after construction. A named tuple can be created as a tuple literal with keys, e.g. `(a=1, b=2)`, or as a tuple literal with semicolon after the opening parenthesis, e.g. `(; a=1, b=2)` (this form also accepts programmatically generated names as described below), or using a `NamedTuple` type as constructor, e.g. `NamedTuple{(:a, :b)}((1,2))`. Accessing the value associated with a name in a named tuple can be done using field access syntax, e.g. `x.a`, or using [`getindex`](@ref), e.g. `x[:a]` or `x[(:a, :b)]`. A tuple of the names can be obtained using [`keys`](@ref), and a tuple of the values can be obtained using [`values`](@ref). !!! note Iteration over `NamedTuple`s produces the *values* without the names. (See example below.) To iterate over the name-value pairs, use the [`pairs`](@ref) function. The [`@NamedTuple`](@ref) macro can be used for conveniently declaring `NamedTuple` types. # Examples ```jldoctest julia> x = (a=1, b=2) (a = 1, b = 2) julia> x.a 1 julia> x[:a] 1 julia> x[(:a,)] (a = 1,) julia> keys(x) (:a, :b) julia> values(x) (1, 2) julia> collect(x) 2-element Vector{Int64}: 1 2 julia> collect(pairs(x)) 2-element Vector{Pair{Symbol, Int64}}: :a => 1 :b => 2 ``` In a similar fashion as to how one can define keyword arguments programmatically, a named tuple can be created by giving pairs `name::Symbol => value` after a semicolon inside a tuple literal. This and the `name=value` syntax can be mixed: ```jldoctest julia> (; :a => 1, :b => 2, c=3) (a = 1, b = 2, c = 3) ``` The name-value pairs can also be provided by splatting a named tuple or any iterator that yields two-value collections holding each a symbol as first value: ```jldoctest julia> keys = (:a, :b, :c); values = (1, 2, 3); julia> NamedTuple{keys}(values) (a = 1, b = 2, c = 3) julia> (; (keys .=> values)...) (a = 1, b = 2, c = 3) julia> nt1 = (a=1, b=2); julia> nt2 = (c=3, d=4); julia> (; nt1..., nt2..., b=20) # the final b overwrites the value from nt1 (a = 1, b = 20, c = 3, d = 4) julia> (; zip(keys, values)...) # zip yields tuples such as (:a, 1) (a = 1, b = 2, c = 3) ``` As in keyword arguments, identifiers and dot expressions imply names: ```jldoctest julia> x = 0 0 julia> t = (; x) (x = 0,) julia> (; t.x) (x = 0,) ``` !!! compat "Julia 1.5" Implicit names from identifiers and dot expressions are available as of Julia 1.5. !!! compat "Julia 1.7" Use of `getindex` methods with multiple `Symbol`s is available as of Julia 1.7. """ Core.NamedTuple if nameof(@__MODULE__) === :Base @eval function (NT::Type{NamedTuple{names,T}})(args::Tuple) where {names, T <: Tuple} if length(args) != length(names::Tuple) throw(ArgumentError("Wrong number of arguments to named tuple constructor.")) end # Note T(args) might not return something of type T; e.g. # Tuple{Type{Float64}}((Float64,)) returns a Tuple{DataType} $(Expr(:splatnew, :NT, :(T(args)))) end function (NT::Type{NamedTuple{names, T}})(nt::NamedTuple) where {names, T <: Tuple} if @generated Expr(:new, :NT, Any[ :(let Tn = fieldtype(NT, $n), ntn = getfield(nt, $(QuoteNode(names[n]))) ntn isa Tn ? ntn : convert(Tn, ntn) end) for n in 1:length(names) ]...) else NT(map(Fix1(getfield, nt), names)) end end function NamedTuple{names}(nt::NamedTuple) where {names} if @generated idx = Int[ fieldindex(nt, names[n]) for n in 1:length(names) ] types = Tuple{(fieldtype(nt, idx[n]) for n in 1:length(idx))...} Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(idx[n]))) for n in 1:length(idx) ]...) else length_names = length(names::Tuple) types = Tuple{(fieldtype(typeof(nt), names[n]) for n in 1:length_names)...} _new_NamedTuple(NamedTuple{names, types}, map(Fix1(getfield, nt), names)) end end (NT::Type{NamedTuple{names, T}})(itr) where {names, T <: Tuple} = NT(T(itr)) (NT::Type{NamedTuple{names}})(itr) where {names} = NT(Tuple(itr)) NamedTuple(itr) = (; itr...) end # if Base # Like NamedTuple{names, T} as a constructor, but omits the additional # `convert` call, when the types are known to match the fields @eval function _new_NamedTuple(T::Type{NamedTuple{NTN, NTT}} where {NTN, NTT}, args::Tuple) $(Expr(:splatnew, :T, :args)) end length(t::NamedTuple) = nfields(t) iterate(t::NamedTuple, iter=1) = iter > nfields(t) ? nothing : (getfield(t, iter), iter + 1) rest(t::NamedTuple) = t @inline rest(t::NamedTuple{names}, i::Int) where {names} = NamedTuple{rest(names,i)}(t) firstindex(t::NamedTuple) = 1 lastindex(t::NamedTuple) = nfields(t) getindex(t::NamedTuple, i::Int) = getfield(t, i) getindex(t::NamedTuple, i::Symbol) = getfield(t, i) getindex(t::NamedTuple, ::Colon) = t @inline getindex(t::NamedTuple, idxs::Tuple{Vararg{Symbol}}) = NamedTuple{idxs}(t) @inline getindex(t::NamedTuple, idxs::AbstractVector{Symbol}) = NamedTuple{Tuple(idxs)}(t) indexed_iterate(t::NamedTuple, i::Int, state=1) = (getfield(t, i), i+1) isempty(::NamedTuple{()}) = true isempty(::NamedTuple) = false empty(::NamedTuple) = NamedTuple() prevind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)-1 nextind(@nospecialize(t::NamedTuple), i::Integer) = Int(i)+1 convert(::Type{NT}, nt::NT) where {names, NT<:NamedTuple{names}} = nt convert(::Type{NT}, nt::NT) where {names, T<:Tuple, NT<:NamedTuple{names,T}} = nt function convert(::Type{NT}, nt::NamedTuple{names}) where {names, T<:Tuple, NT<:NamedTuple{names,T}} if !@isdefined T # converting abstract NT to an abstract Tuple type, to a concrete NT1, is not straightforward, so this could just be an error, but we define it anyways # _tuple_error(NT, nt) T1 = Tuple{ntuple(i -> fieldtype(NT, i), Val(length(names)))...} NT1 = NamedTuple{names, T1} else T1 = T NT1 = NT end return NT1(T1(nt))::NT1::NT end if nameof(@__MODULE__) === :Base Tuple(nt::NamedTuple) = (nt...,) (::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) end function show(io::IO, t::NamedTuple) n = nfields(t) for i = 1:n # if field types aren't concrete, show full type if typeof(getfield(t, i)) !== fieldtype(typeof(t), i) show(io, typeof(t)) print(io, "(") show(io, Tuple(t)) print(io, ")") return end end if n == 0 print(io, "NamedTuple()") else typeinfo = get(io, :typeinfo, Any) print(io, "(") for i = 1:n show_sym(io, fieldname(typeof(t), i)) print(io, " = ") show(IOContext(io, :typeinfo => t isa typeinfo <: NamedTuple ? fieldtype(typeinfo, i) : Any), getfield(t, i)) if n == 1 print(io, ",") elseif i < n print(io, ", ") end end print(io, ")") end end eltype(::Type{T}) where T<:NamedTuple = nteltype(T) nteltype(::Type) = Any nteltype(::Type{NamedTuple{names,T}} where names) where {T} = eltype(T) keytype(@nospecialize nt::NamedTuple) = keytype(typeof(nt)) keytype(@nospecialize T::Type{<:NamedTuple}) = Symbol valtype(@nospecialize nt::NamedTuple) = valtype(typeof(nt)) valtype(@nospecialize T::Type{<:NamedTuple}) = eltype(T) ==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) ==(a::NamedTuple, b::NamedTuple) = false isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b)) isequal(a::NamedTuple, b::NamedTuple) = false _nt_names(::NamedTuple{names}) where {names} = names _nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names hash(x::NamedTuple, h::UInt) = xor(objectid(_nt_names(x)), hash(Tuple(x), h)) (<)(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) < Tuple(b) isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b)) same_names(::NamedTuple{names}...) where {names} = true same_names(::NamedTuple...) = false # NOTE: this method signature makes sure we don't define map(f) function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names if !same_names(nt, nts...) throw(ArgumentError("Named tuple names do not match.")) end NamedTuple{names}(map(f, map(Tuple, (nt, nts...))...)) end @assume_effects :total function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) @nospecialize an bn names = Symbol[an...] for n in bn if !sym_in(n, an) push!(names, n) end end (names...,) end @assume_effects :total function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple}) @nospecialize names a b bn = _nt_names(b) return Tuple{Any[ fieldtype(sym_in(names[n], bn) ? b : a, names[n]) for n in 1:length(names) ]...} end @assume_effects :foldable function merge_fallback(@nospecialize(a::NamedTuple), @nospecialize(b::NamedTuple), @nospecialize(an::Tuple{Vararg{Symbol}}), @nospecialize(bn::Tuple{Vararg{Symbol}})) names = merge_names(an, bn) types = merge_types(names, typeof(a), typeof(b)) n = length(names) A = Vector{Any}(undef, n) for i=1:n n = names[i] A[i] = getfield(sym_in(n, bn) ? b : a, n) end _new_NamedTuple(NamedTuple{names, types}, (A...,)) end """ merge(a::NamedTuple, bs::NamedTuple...) Construct a new named tuple by merging two or more existing ones, in a left-associative manner. Merging proceeds left-to-right, between pairs of named tuples, and so the order of fields present in both the leftmost and rightmost named tuples take the same position as they are found in the leftmost named tuple. However, values are taken from matching fields in the rightmost named tuple that contains that field. Fields present in only the rightmost named tuple of a pair are appended at the end. A fallback is implemented for when only a single named tuple is supplied, with signature `merge(a::NamedTuple)`. !!! compat "Julia 1.1" Merging 3 or more `NamedTuple` requires at least Julia 1.1. # Examples ```jldoctest julia> merge((a=1, b=2, c=3), (b=4, d=5)) (a = 1, b = 4, c = 3, d = 5) ``` ```jldoctest julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) (a = 1, b = 3, c = (d = 2,)) ``` """ function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} if @generated names = merge_names(an, bn) types = merge_types(names, a, b) vals = Any[ :(getfield($(sym_in(names[n], bn) ? :b : :a), $(QuoteNode(names[n])))) for n in 1:length(names) ] :( _new_NamedTuple(NamedTuple{$names,$types}, ($(vals...),)) ) else merge_fallback(a, b, an, bn) end end merge(a::NamedTuple, b::NamedTuple{()}) = a merge(a::NamedTuple{()}, b::NamedTuple{()}) = a merge(a::NamedTuple{()}, b::NamedTuple) = b merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, getfield(b, :data)) merge(a::NamedTuple, b::Iterators.Zip{<:Tuple{Any,Any}}) = merge(a, NamedTuple{Tuple(b.is[1])}(b.is[2])) merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...) merge(a::NamedTuple) = a """ merge(a::NamedTuple, iterable) Interpret an iterable of key-value pairs as a named tuple, and perform a merge. ```jldoctest julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5]) (a = 1, b = 4, c = 3, d = 5) ``` """ function merge(a::NamedTuple, itr) names = Symbol[] vals = Any[] inds = IdDict{Symbol,Int}() for (k, v) in itr k = k::Symbol oldind = get(inds, k, 0) if oldind > 0 vals[oldind] = v else push!(names, k) push!(vals, v) inds[k] = length(names) end end merge(a, NamedTuple{(names...,)}((vals...,))) end keys(nt::NamedTuple{names}) where {names} = names::Tuple{Vararg{Symbol}} values(nt::NamedTuple) = Tuple(nt) haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = isdefined(nt, key) ? getfield(nt, key) : default get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) ? getfield(nt, key) : f() tail(t::NamedTuple{names}) where names = NamedTuple{tail(names::Tuple)}(t) front(t::NamedTuple{names}) where names = NamedTuple{front(names::Tuple)}(t) reverse(nt::NamedTuple) = NamedTuple{reverse(keys(nt))}(reverse(values(nt))) @assume_effects :total function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) @nospecialize an bn names = Symbol[] for n in an if !sym_in(n, bn) push!(names, n) end end (names...,) end @assume_effects :foldable function diff_types(@nospecialize(a::NamedTuple), @nospecialize(names::Tuple{Vararg{Symbol}})) return Tuple{Any[ fieldtype(typeof(a), names[n]) for n in 1:length(names) ]...} end @assume_effects :foldable function diff_fallback(@nospecialize(a::NamedTuple), @nospecialize(an::Tuple{Vararg{Symbol}}), @nospecialize(bn::Tuple{Vararg{Symbol}})) names = diff_names(an, bn) isempty(names) && return (;) types = diff_types(a, names) n = length(names) A = Vector{Any}(undef, n) for i=1:n n = names[i] A[i] = getfield(a, n) end _new_NamedTuple(NamedTuple{names, types}, (A...,)) end """ structdiff(a::NamedTuple, b::Union{NamedTuple,Type{NamedTuple}}) Construct a copy of named tuple `a`, except with fields that exist in `b` removed. `b` can be a named tuple, or a type of the form `NamedTuple{field_names}`. """ function structdiff(a::NamedTuple{an}, b::Union{NamedTuple{bn}, Type{NamedTuple{bn}}}) where {an, bn} if @generated names = diff_names(an, bn) isempty(names) && return (;) # just a fast pass idx = Int[ fieldindex(a, names[n]) for n in 1:length(names) ] types = Tuple{Any[ fieldtype(a, idx[n]) for n in 1:length(idx) ]...} vals = Any[ :(getfield(a, $(idx[n]))) for n in 1:length(idx) ] return :( _new_NamedTuple(NamedTuple{$names,$types}, ($(vals...),)) ) else return diff_fallback(a, an, bn) end end structdiff(a::NamedTuple{an}, b::Union{NamedTuple{an}, Type{NamedTuple{an}}}) where {an} = (;) """ setindex(nt::NamedTuple, val, key::Symbol) Constructs a new `NamedTuple` with the key `key` set to `val`. If `key` is already in the keys of `nt`, `val` replaces the old value. ```jldoctest julia> nt = (a = 3,) (a = 3,) julia> Base.setindex(nt, 33, :b) (a = 3, b = 33) julia> Base.setindex(nt, 4, :a) (a = 4,) julia> Base.setindex(nt, "a", :a) (a = "a",) ``` """ function setindex(nt::NamedTuple, v, idx::Symbol) merge(nt, (; idx => v)) end """ @NamedTuple{key1::Type1, key2::Type2, ...} @NamedTuple begin key1::Type1; key2::Type2; ...; end This macro gives a more convenient syntax for declaring `NamedTuple` types. It returns a `NamedTuple` type with the given keys and types, equivalent to `NamedTuple{(:key1, :key2, ...), Tuple{Type1,Type2,...}}`. If the `::Type` declaration is omitted, it is taken to be `Any`. The `begin ... end` form allows the declarations to be split across multiple lines (similar to a `struct` declaration), but is otherwise equivalent. The `NamedTuple` macro is used when printing `NamedTuple` types to e.g. the REPL. For example, the tuple `(a=3.1, b="hello")` has a type `NamedTuple{(:a, :b), Tuple{Float64, String}}`, which can also be declared via `@NamedTuple` as: ```jldoctest julia> @NamedTuple{a::Float64, b::String} @NamedTuple{a::Float64, b::String} julia> @NamedTuple begin a::Float64 b::String end @NamedTuple{a::Float64, b::String} ``` !!! compat "Julia 1.5" This macro is available as of Julia 1.5. """ macro NamedTuple(ex) Meta.isexpr(ex, :braces) || Meta.isexpr(ex, :block) || throw(ArgumentError("@NamedTuple expects {...} or begin...end")) decls = filter(e -> !(e isa LineNumberNode), ex.args) all(e -> e isa Symbol || Meta.isexpr(e, :(::)), decls) || throw(ArgumentError("@NamedTuple must contain a sequence of name or name::type expressions")) vars = [QuoteNode(e isa Symbol ? e : e.args[1]) for e in decls] types = [esc(e isa Symbol ? :Any : e.args[2]) for e in decls] return :(NamedTuple{($(vars...),), Tuple{$(types...)}}) end """ @Kwargs{key1::Type1, key2::Type2, ...} This macro gives a convenient way to construct the type representation of keyword arguments from the same syntax as [`@NamedTuple`](@ref). For example, when we have a function call like `func([positional arguments]; kw1=1.0, kw2="2")`, we can use this macro to construct the internal type representation of the keyword arguments as `@Kwargs{kw1::Float64, kw2::String}`. The macro syntax is specifically designed to simplify the signature type of a keyword method when it is printed in the stack trace view. ```julia julia> @Kwargs{init::Int} # the internal representation of keyword arguments Base.Pairs{Symbol, Int64, Tuple{Symbol}, @NamedTuple{init::Int64}} julia> sum("julia"; init=1) ERROR: MethodError: no method matching +(::Char, ::Char) Closest candidates are: +(::Any, ::Any, ::Any, ::Any...) @ Base operators.jl:585 +(::Integer, ::AbstractChar) @ Base char.jl:247 +(::T, ::Integer) where T<:AbstractChar @ Base char.jl:237 Stacktrace: [1] add_sum(x::Char, y::Char) @ Base ./reduce.jl:24 [2] BottomRF @ Base ./reduce.jl:86 [inlined] [3] _foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, init::Int64, itr::String) @ Base ./reduce.jl:62 [4] foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, nt::Int64, itr::String) @ Base ./reduce.jl:48 [inlined] [5] mapfoldl_impl(f::typeof(identity), op::typeof(Base.add_sum), nt::Int64, itr::String) @ Base ./reduce.jl:44 [inlined] [6] mapfoldl(f::typeof(identity), op::typeof(Base.add_sum), itr::String; init::Int64) @ Base ./reduce.jl:175 [inlined] [7] mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String; kw::@Kwargs{init::Int64}) @ Base ./reduce.jl:307 [inlined] [8] sum(f::typeof(identity), a::String; kw::@Kwargs{init::Int64}) @ Base ./reduce.jl:535 [inlined] [9] sum(a::String; kw::@Kwargs{init::Int64}) @ Base ./reduce.jl:564 [inlined] [10] top-level scope @ REPL[12]:1 ``` !!! compat "Julia 1.10" This macro is available as of Julia 1.10. """ macro Kwargs(ex) return :(let NT = @NamedTuple $ex Base.Pairs{keytype(NT),eltype(NT),typeof(NT.parameters[1]),NT} end) end @constprop :aggressive function split_rest(t::NamedTuple{names}, n::Int, st...) where {names} _check_length_split_rest(length(t), n) names_front, names_last_n = split_rest(names, n, st...) return NamedTuple{names_front}(t), NamedTuple{names_last_n}(t) end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./build_h.jlษ# This file is automatically generated in base/Makefile const MACHINE = "x86_64-linux-gnu" const libm_name = "libopenlibm" const USE_BLAS64 = true const USE_GPL_LIBS = true const libllvm_version_string = "15.0.7jl" const libllvm_name = "libLLVM-15jl" const VERSION_STRING = "1.10.3" const TAGGED_RELEASE_BANNER = "Official https://julialang.org/ release" const SYSCONFDIR = "../etc" const DATAROOTDIR = "../share" const DOCDIR = "../share/doc/julia" const LIBDIR = "../lib" const LIBEXECDIR = "../libexec" const PRIVATE_LIBDIR = "../lib/julia" const PRIVATE_LIBEXECDIR = "../libexec/julia" const INCLUDEDIR = "../include" const DARWIN_FRAMEWORK = false const BUILD_TRIPLET = "x86_64-linux-gnu-libgfortran5-cxx11" T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./version_git.jl`# This file was autogenerated by base/version_git.sh struct GitVersionInfo commit::String commit_short::String branch::String build_number::Int date_string::String tagged_commit::Bool fork_master_distance::Int fork_master_timestamp::Float64 build_system_commit::String build_system_commit_short::String end const GIT_VERSION_INFO = GitVersionInfo( "0b4590a5507d3f3046e5bafc007cacbbfc9b310b", "0b4590a5507", "v1.10.3", 0, "2024-04-30 10:59 UTC", true, 353, 1688376058.0, "ea50eb719242bc3e844227a2ebf54a49d308bafc", "ea50eb7", ) N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/hashing.jlฆ # This file is a part of Julia. License is MIT: https://julialang.org/license ## hashing a single value ## """ hash(x[, h::UInt]) -> UInt Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The optional second argument `h` is another hash code to be mixed with the result. New types should implement the 2-argument form, typically by calling the 2-argument `hash` method recursively in order to mix hashes of the contents with each other (and with `h`). Typically, any type that implements `hash` should also implement its own [`==`](@ref) (hence [`isequal`](@ref)) to guarantee the property mentioned above. Types supporting subtraction (operator `-`) should also implement [`widen`](@ref), which is required to hash values inside heterogeneous arrays. The hash value may change when a new Julia process is started. ```jldoctest julia> a = hash(10) 0x95ea2955abd45275 julia> hash(10, a) # only use the output of another hash function as the second argument 0xd42bad54a8575b16 ``` See also: [`objectid`](@ref), [`Dict`](@ref), [`Set`](@ref). """ hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) hash(T::Type, h::UInt) = hash_uint(3h - ccall(:jl_type_hash, UInt, (Any,), T)) ## hashing general objects ## hash(@nospecialize(x), h::UInt) = hash_uint(3h - objectid(x)) hash(x::Symbol) = objectid(x) ## core data hashing functions ## function hash_64_64(n::UInt64) a::UInt64 = n a = ~a + a << 21 a = a โŠป a >> 24 a = a + a << 3 + a << 8 a = a โŠป a >> 14 a = a + a << 2 + a << 4 a = a โŠป a >> 28 a = a + a << 31 return a end function hash_64_32(n::UInt64) a::UInt64 = n a = ~a + a << 18 a = a โŠป a >> 31 a = a * 21 a = a โŠป a >> 11 a = a + a << 6 a = a โŠป a >> 22 return a % UInt32 end function hash_32_32(n::UInt32) a::UInt32 = n a = a + 0x7ed55d16 + a << 12 a = a โŠป 0xc761c23c โŠป a >> 19 a = a + 0x165667b1 + a << 5 a = a + 0xd3a2646c โŠป a << 9 a = a + 0xfd7046c5 + a << 3 a = a โŠป 0xb55a4f09 โŠป a >> 16 return a end if UInt === UInt64 hash_uint64(x::UInt64) = hash_64_64(x) hash_uint(x::UInt) = hash_64_64(x) else hash_uint64(x::UInt64) = hash_64_32(x) hash_uint(x::UInt) = hash_32_32(x) end ## efficient value-based hashing of integers ## hash(x::Int64, h::UInt) = hash_uint64(bitcast(UInt64, x)) - 3h hash(x::UInt64, h::UInt) = hash_uint64(x) - 3h hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h) function hash_integer(n::Integer, h::UInt) h โŠป= hash_uint((n % UInt) โŠป h) n = abs(n) n >>>= sizeof(UInt) << 3 while n != 0 h โŠป= hash_uint((n % UInt) โŠป h) n >>>= sizeof(UInt) << 3 end return h end ## symbol & expression hashing ## if UInt === UInt64 hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x83c7900696d26dc6)) hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x2c97bf8b3de87020) else hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x96d26dc6)) hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x469d72af) end ## hashing strings ## const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 @assume_effects :total function hash(s::String, h::UInt) h += memhash_seed ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/rounding.jl # This file is a part of Julia. License is MIT: https://julialang.org/license module Rounding let fenv_consts = Vector{Cint}(undef, 9) ccall(:jl_get_fenv_consts, Cvoid, (Ptr{Cint},), fenv_consts) global const JL_FE_INEXACT = fenv_consts[1] global const JL_FE_UNDERFLOW = fenv_consts[2] global const JL_FE_OVERFLOW = fenv_consts[3] global const JL_FE_DIVBYZERO = fenv_consts[4] global const JL_FE_INVALID = fenv_consts[5] global const JL_FE_TONEAREST = fenv_consts[6] global const JL_FE_UPWARD = fenv_consts[7] global const JL_FE_DOWNWARD = fenv_consts[8] global const JL_FE_TOWARDZERO = fenv_consts[9] end export RoundingMode, RoundNearest, RoundToZero, RoundUp, RoundDown, RoundFromZero, RoundNearestTiesAway, RoundNearestTiesUp, rounding, setrounding, get_zero_subnormals, set_zero_subnormals ## rounding modes ## """ RoundingMode A type used for controlling the rounding mode of floating point operations (via [`rounding`](@ref)/[`setrounding`](@ref) functions), or as optional arguments for rounding to the nearest integer (via the [`round`](@ref) function). Currently supported rounding modes are: - [`RoundNearest`](@ref) (default) - [`RoundNearestTiesAway`](@ref) - [`RoundNearestTiesUp`](@ref) - [`RoundToZero`](@ref) - [`RoundFromZero`](@ref) - [`RoundUp`](@ref) - [`RoundDown`](@ref) !!! compat "Julia 1.9" `RoundFromZero` requires at least Julia 1.9. Prior versions support `RoundFromZero` for `BigFloat`s only. """ struct RoundingMode{T} end """ RoundNearest The default rounding mode. Rounds to the nearest integer, with ties (fractional values of 0.5) being rounded to the nearest even integer. """ const RoundNearest = RoundingMode{:Nearest}() """ RoundToZero [`round`](@ref) using this rounding mode is an alias for [`trunc`](@ref). """ const RoundToZero = RoundingMode{:ToZero}() """ RoundUp [`round`](@ref) using this rounding mode is an alias for [`ceil`](@ref). """ const RoundUp = RoundingMode{:Up}() """ RoundDown [`round`](@ref) using this rounding mode is an alias for [`floor`](@ref). """ const RoundDown = RoundingMode{:Down}() """ RoundFromZero Rounds away from zero. !!! compat "Julia 1.9" `RoundFromZero` requires at least Julia 1.9. Prior versions support `RoundFromZero` for `BigFloat`s only. # Examples ```jldoctest julia> BigFloat("1.0000000000000001", 5, RoundFromZero) 1.06 ``` """ const RoundFromZero = RoundingMode{:FromZero}() """ RoundNearestTiesAway Rounds to nearest integer, with ties rounded away from zero (C/C++ [`round`](@ref) behaviour). """ const RoundNearestTiesAway = RoundingMode{:NearestTiesAway}() """ RoundNearestTiesUp Rounds to nearest integer, with ties rounded toward positive infinity (Java/JavaScript [`round`](@ref) behaviour). """ const RoundNearestTiesUp = RoundingMode{:NearestTiesUp}() to_fenv(::RoundingMode{:Nearest}) = JL_FE_TONEAREST to_fenv(::RoundingMode{:ToZero}) = JL_FE_TOWARDZERO to_fenv(::RoundingMode{:Up}) = JL_FE_UPWARD to_fenv(::RoundingMode{:Down}) = JL_FE_DOWNWARD function from_fenv(r::Integer) if r == JL_FE_TONEAREST return RoundNearest elseif r == JL_FE_DOWNWARD return RoundDown elseif r == JL_FE_UPWARD return RoundUp elseif r == JL_FE_TOWARDZERO return RoundToZero else throw(ArgumentError("invalid rounding mode code: $r")) end end """ setrounding(T, mode) Set the rounding mode of floating point type `T`, controlling the rounding of basic arithmetic functions ([`+`](@ref), [`-`](@ref), [`*`](@ref), [`/`](@ref) and [`sqrt`](@ref)) and type conversion. Other numerical functions may give incorrect or invalid values when using rounding modes other than the default [`RoundNearest`](@ref). Note that this is currently only supported for `T == BigFloat`. !!! warning This function is not thread-safe. It will affect code running on all threads, but its behavior is undefined if called concurrently with computations that use the setting. """ setrounding(T::Type, mode) """ rounding(T) Get the current floating point rounding mode for type `T`, controlling the rounding of basic arithmetic functions ([`+`](@ref), [`-`](@ref), [`*`](@ref), [`/`](@ref) and [`sqrt`](@ref)) and type conversion. See [`RoundingMode`](@ref) for available modes. """ :rounding setrounding_raw(::Type{<:Union{Float32,Float64}}, i::Integer) = ccall(:jl_set_fenv_rounding, Int32, (Int32,), i) rounding_raw(::Type{<:Union{Float32,Float64}}) = ccall(:jl_get_fenv_rounding, Int32, ()) rounding(::Type{T}) where {T<:Union{Float32,Float64}} = from_fenv(rounding_raw(T)) """ setrounding(f::Function, T, mode) Change the rounding mode of floating point type `T` for the duration of `f`. It is logically equivalent to: old = rounding(T) setrounding(T, mode) f() setrounding(T, old) See [`RoundingMode`](@ref) for available rounding modes. """ function setrounding(f::Function, ::Type{T}, rounding::RoundingMode) where T old_rounding_raw = rounding_raw(T) setrounding(T,rounding) try return f() finally setrounding_raw(T,old_rounding_raw) end end function setrounding_raw(f::Function, ::Type{T}, rounding) where T old_rounding_raw = rounding_raw(T) setrounding_raw(T,rounding) try return f() finally setrounding_raw(T,old_rounding_raw) end end # Should be equivalent to: # setrounding(Float64,r) do # convert(T,x) # end # but explicit checks are currently quicker (~20x). # Assumes conversion is performed by rounding to nearest value. # To avoid ambiguous dispatch with methods in mpfr.jl: (::Type{T})(x::Real, r::RoundingMode) where {T<:AbstractFloat} = _convert_rounding(T,x,r)::T _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Nearest}) where {T<:AbstractFloat} = convert(T,x)::T function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Down}) where T<:AbstractFloat y = convert(T,x)::T y > x ? prevfloat(y) : y end function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Up}) where T<:AbstractFloat y = convert(T,x)::T y < x ? nextfloat(y) : y end function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:ToZero}) where T<:AbstractFloat y = convert(T,x)::T if x > 0.0 y > x ? prevfloat(y) : y else y < x ? nextfloat(y) : y end end """ set_zero_subnormals(yes::Bool) -> Bool If `yes` is `false`, subsequent floating-point operations follow rules for IEEE arithmetic on subnormal values ("denormals"). Otherwise, floating-point operations are permitted (but not required) to convert subnormal inputs or outputs to zero. Returns `true` unless `yes==true` but the hardware does not support zeroing of subnormal numbers. `set_zero_subnormals(true)` can speed up some computations on some hardware. However, it can break identities such as `(x-y==0) == (x==y)`. !!! warning This function only affects the current thread. """ set_zero_subnormals(yes::Bool) = ccall(:jl_set_zero_subnormals,Int32,(Int8,),yes)==0 """ get_zero_subnormals() -> Bool Return `false` if operations on subnormal floating-point values ("denormals") obey rules for IEEE arithmetic, and `true` if they might be converted to zeros. !!! warning This function only affects the current thread. """ get_zero_subnormals() = ccall(:jl_get_zero_subnormals,Int32,())!=0 end #module J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/div.jl™-# This file is a part of Julia. License is MIT: https://julialang.org/license # Div is truncating by default """ div(x, y, r::RoundingMode=RoundToZero) The quotient from Euclidean (integer) division. Computes `x / y`, rounded to an integer according to the rounding mode `r`. In other words, the quantity round(x / y, r) without any intermediate rounding. !!! compat "Julia 1.4" The three-argument method taking a `RoundingMode` requires Julia 1.4 or later. See also [`fld`](@ref) and [`cld`](@ref), which are special cases of this function. !!! compat "Julia 1.9" `RoundFromZero` requires at least Julia 1.9. # Examples: ```jldoctest julia> div(4, 3, RoundDown) # Matches fld(4, 3) 1 julia> div(4, 3, RoundUp) # Matches cld(4, 3) 2 julia> div(5, 2, RoundNearest) 2 julia> div(5, 2, RoundNearestTiesAway) 3 julia> div(-5, 2, RoundNearest) -2 julia> div(-5, 2, RoundNearestTiesAway) -3 julia> div(-5, 2, RoundNearestTiesUp) -2 julia> div(4, 3, RoundFromZero) 2 julia> div(-4, 3, RoundFromZero) -2 ``` """ div(x, y, r::RoundingMode) div(a, b) = div(a, b, RoundToZero) """ rem(x, y, r::RoundingMode=RoundToZero) Compute the remainder of `x` after integer division by `y`, with the quotient rounded according to the rounding mode `r`. In other words, the quantity x - y * round(x / y, r) without any intermediate rounding. - if `r == RoundNearest`, then the result is exact, and in the interval ``[-|y| / 2, |y| / 2]``. See also [`RoundNearest`](@ref). - if `r == RoundToZero` (default), then the result is exact, and in the interval ``[0, |y|)`` if `x` is positive, or ``(-|y|, 0]`` otherwise. See also [`RoundToZero`](@ref). - if `r == RoundDown`, then the result is in the interval ``[0, y)`` if `y` is positive, or ``(y, 0]`` otherwise. The result may not be exact if `x` and `y` have different signs, and `abs(x) < abs(y)`. See also [`RoundDown`](@ref). - if `r == RoundUp`, then the result is in the interval ``(-y, 0]`` if `y` is positive, or ``[0, -y)`` otherwise. The result may not be exact if `x` and `y` have the same sign, and `abs(x) < abs(y)`. See also [`RoundUp`](@ref). - if `r == RoundFromZero`, then the result is in the interval ``(-y, 0]`` if `y` is positive, or ``[0, -y)`` otherwise. The result may not be exact if `x` and `y` have the same sign, and `abs(x) < abs(y)`. See also [`RoundFromZero`](@ref). !!! compat "Julia 1.9" `RoundFromZero` requires at least Julia 1.9. # Examples: ```jldoctest julia> x = 9; y = 4; julia> x % y # same as rem(x, y) 1 julia> x รท y # same as div(x, y) 2 julia> x == div(x, y) * y + rem(x, y) true ``` """ rem(x, y, r::RoundingMode) # TODO: Make these primitive and have the two-argument version call these rem(x, y, ::RoundingMode{:ToZero}) = rem(x, y) rem(x, y, ::RoundingMode{:Down}) = mod(x, y) rem(x, y, ::RoundingMode{:Up}) = mod(x, -y) rem(x, y, r::RoundingMode{:Nearest}) = x - y * div(x, y, r) rem(x::Integer, y::Integer, r::RoundingMode{:Nearest}) = divrem(x, y, r)[2] function rem(x, y, ::typeof(RoundFromZero)) signbit(x) == signbit(y) ? rem(x, y, RoundUp) : rem(x, y, RoundDown) end """ fld(x, y) Largest integer less than or equal to `x / y`. Equivalent to `div(x, y, RoundDown)`. See also [`div`](@ref), [`cld`](@ref), [`fld1`](@ref). # Examples ```jldoctest julia> fld(7.3, 5.5) 1.0 julia> fld.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: -2 -2 -1 -1 -1 0 0 0 1 1 1 ``` Because `fld(x, y)` implements strictly correct floored rounding based on the true value of floating-point numbers, unintuitive situations can arise. For example: ```jldoctest julia> fld(6.0, 0.1) 59.0 julia> 6.0 / 0.1 60.0 julia> 6.0 / big(0.1) 59.99999999999999666933092612453056361837965690217069245739573412231113406246995 ``` What is happening here is that the true value of the floating-point number written as `0.1` is slightly larger than the numerical value 1/10 while `6.0` represents the number 6 precisely. Therefore the true value of `6.0 / 0.1` is slightly less than 60. When doing division, this is rounded to precisely `60.0`, but `fld(6.0, 0.1)` always takes the floor of the true value, so the result is `59.0`. """ fld(a, b) = div(a, b, RoundDown) """ cld(x, y) Smallest integer larger than or equal to `x / y`. Equivalent to `div(x, y, RoundUp)`. See also [`div`](@ref), [`fld`](@ref). # Examples ```jldoctest julia> cld(5.5, 2.2) 3.0 julia> cld.(-5:5, 3)' 1ร—11 adjoint(::Vector{Int64}) with eltype Int64: -1 -1 -1 0 0 0 1 1 1 2 2 ``` """ cld(a, b) = div(a, b, RoundUp) # divrem """ divrem(x, y, r::RoundingMode=RoundToZero) The quotient and remainder from Euclidean division. Equivalent to `(div(x, y, r), rem(x, y, r))`. Equivalently, with the default value of `r`, this call is equivalent to `(x รท y, x % y)`. See also: [`fldmod`](@ref), [`cld`](@ref). # Examples ```jldoctest julia> divrem(3, 7) (0, 3) julia> divrem(7, 3) (2, 1) ``` """ divrem(x, y) = divrem(x, y, RoundToZero) function divrem(a, b, r::RoundingMode) if r === RoundToZero # For compat. Remove in 2.0. (div(a, b), rem(a, b)) elseif r === RoundDown # For compat. Remove in 2.0. (fld(a, b), mod(a, b)) else (div(a, b, r), rem(a, b, r)) end end # avoids calling rem for Integers-Integers (all modes), # a - d * b not precise for Floats - AbstractFloat, AbstractIrrational. # Rationals are still slower function divrem(a::Integer, b::Integer, r::Union{typeof(RoundUp), typeof(RoundDown), typeof(RoundToZero)}) if r === RoundToZero # For compat. Remove in 2.0. d = div(a, b) (d, a - d * b) elseif r === RoundDown # For compat. Remove in 2.0. d = fld(a, b) (d, a - d * b) elseif r === RoundUp # For compat. Remove in 2.0. d = div(a, b, r) (d, a - d * b) end end function divrem(x::Integer, y::Integer, rnd::typeof(RoundNearest)) (q, r) = divrem(x, y) if x >= 0 if y >= 0 r >= (yรท2) + (isodd(y) | iseven(q)) ? (q+true, r-y) : (q, r) else r >= -(yรท2) + (isodd(y) | iseven(q)) ? (q-true, r+y) : (q, r) end else if y >= 0 r <= -signed(yรท2) - (isodd(y) | iseven(q)) ? (q-true, r+y) : (q, r) else r <= (yรท2) - (isodd(y) | iseven(q)) ? (q+true, r-y) : (q, r) end end end function divrem(x::Integer, y::Integer, rnd:: typeof(RoundNearestTiesAway)) (q, r) = divrem(x, y) if x >= 0 if y >= 0 r >= (yรท2) + isodd(y) ? (q+true, r-y) : (q, r) else r >= -(yรท2) + isodd(y) ? (q-true, r+y) : (q, r) end else if y >= 0 r <= -signed(yรท2) - isodd(y) ? (q-true, r+y) : (q, r) else r <= (yรท2) - isodd(y) ? (q+true, r-y) : (q, r) end end end function divrem(x::Integer, y::Integer, rnd::typeof(RoundNearestTiesUp)) (q, r) = divrem(x, y) if x >= 0 if y >= 0 r >= (yรท2) + isodd(y) ? (q+true, r-y) : (q, r) else r >= -(yรท2) + true ? (q-true, r+y) : (q, r) end else if y >= 0 r <= -signed(yรท2) - true ? (q-true, r+y) : (q, r) else r <= (yรท2) - isodd(y) ? (q+true, r-y) : (q, r) end end end function divrem(x, y, ::typeof(RoundFromZero)) signbit(x) == signbit(y) ? divrem(x, y, RoundUp) : divrem(x, y, RoundDown) end """ fldmod(x, y) The floored quotient and modulus after division. A convenience wrapper for `divrem(x, y, RoundDown)`. Equivalent to `(fld(x, y), mod(x, y))`. See also: [`fld`](@ref), [`cld`](@ref), [`fldmod1`](@ref). """ fldmod(x, y) = divrem(x, y, RoundDown) # We definite generic rounding methods for other rounding modes in terms of # RoundToZero. function div(x::Signed, y::Unsigned, ::typeof(RoundDown)) (q, r) = divrem(x, y) q - (signbit(x) & (r != 0)) end function div(x::Unsigned, y::Signed, ::typeof(RoundDown)) (q, r) = divrem(x, y) q - (signbit(y) & (r != 0)) end function div(x::Signed, y::Unsigned, ::typeof(RoundUp)) (q, r) = divrem(x, y) q + (!signbit(x) & (r != 0)) end function div(x::Unsigned, y::Signed, ::typeof(RoundUp)) (q, r) = divrem(x, y) q + (!signbit(y) & (r != 0)) end function div(x::Integer, y::Integer, rnd::Union{typeof(RoundNearest), typeof(RoundNearestTiesAway), typeof(RoundNearestTiesUp)}) divrem(x, y, rnd)[1] end function div(x::Integer, y::Integer, ::typeof(RoundFromZero)) signbit(x) == signbit(y) ? div(x, y, RoundUp) : div(x, y, RoundDown) end # For bootstrapping purposes, we define div for integers directly. Provide the # generic signature also div(a::T, b::T, ::typeof(RoundToZero)) where {T<:Union{BitSigned, BitUnsigned64}} = div(a, b) div(a::Bool, b::Bool, r::RoundingMode) = div(a, b) # Prevent ambiguities for rm in (RoundUp, RoundDown, RoundToZero, RoundFromZero) @eval div(a::Bool, b::Bool, r::$(typeof(rm))) = div(a, b) end function div(x::Bool, y::Bool, rnd::Union{typeof(RoundNearest), typeof(RoundNearestTiesAway), typeof(RoundNearestTiesUp)}) div(x, y) end fld(a::T, b::T) where {T<:Union{Integer,AbstractFloat}} = div(a, b, RoundDown) cld(a::T, b::T) where {T<:Union{Integer,AbstractFloat}} = div(a, b, RoundUp) div(a::Int128, b::Int128, ::typeof(RoundToZero)) = div(a, b) div(a::UInt128, b::UInt128, ::typeof(RoundToZero)) = div(a, b) rem(a::Int128, b::Int128, ::typeof(RoundToZero)) = rem(a, b) rem(a::UInt128, b::UInt128, ::typeof(RoundToZero)) = rem(a, b) # These are kept for compatibility with external packages overriding fld / cld. # In 2.0, packages should extend div(a, b, r) instead, in which case, these can # be removed. fld(x::Real, y::Real) = div(promote(x, y)..., RoundDown) cld(x::Real, y::Real) = div(promote(x, y)..., RoundUp) fld(x::Signed, y::Unsigned) = div(x, y, RoundDown) fld(x::Unsigned, y::Signed) = div(x, y, RoundDown) cld(x::Signed, y::Unsigned) = div(x, y, RoundUp) cld(x::Unsigned, y::Signed) = div(x, y, RoundUp) fld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundDown))) cld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundUp))) # Promotion function div(x::Real, y::Real, r::RoundingMode) typeof(x) === typeof(y) && throw(MethodError(div, (x, y, r))) if r === RoundToZero # For compat. Remove in 2.0. div(promote(x, y)...) else div(promote(x, y)..., r) end end # Integers # fld(x, y) == div(x, y) - ((x >= 0) != (y >= 0) && rem(x, y) != 0 ? 1 : 0) div(x::T, y::T, ::typeof(RoundDown)) where {T<:Unsigned} = div(x, y) function div(x::T, y::T, ::typeof(RoundDown)) where T<:Integer d = div(x, y, RoundToZero) return d - (signbit(x โŠป y) & (d * y != x)) end # cld(x, y) = div(x, y) + ((x > 0) == (y > 0) && rem(x, y) != 0 ? 1 : 0) function div(x::T, y::T, ::typeof(RoundUp)) where T<:Unsigned d = div(x, y, RoundToZero) return d + (d * y != x) end function div(x::T, y::T, ::typeof(RoundUp)) where T<:Integer d = div(x, y, RoundToZero) return d + (((x > 0) == (y > 0)) & (d * y != x)) end # Real # NOTE: C89 fmod() and x87 FPREM implicitly provide truncating float division, # so it is used here as the basis of float div(). div(x::T, y::T, r::RoundingMode) where {T<:AbstractFloat} = convert(T, round((x - rem(x, y, r)) / y)) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/float.jl‹# This file is a part of Julia. License is MIT: https://julialang.org/license const IEEEFloat = Union{Float16, Float32, Float64} ## floating point traits ## """ Inf16 Positive infinity of type [`Float16`](@ref). """ const Inf16 = bitcast(Float16, 0x7c00) """ NaN16 A not-a-number value of type [`Float16`](@ref). """ const NaN16 = bitcast(Float16, 0x7e00) """ Inf32 Positive infinity of type [`Float32`](@ref). """ const Inf32 = bitcast(Float32, 0x7f800000) """ NaN32 A not-a-number value of type [`Float32`](@ref). """ const NaN32 = bitcast(Float32, 0x7fc00000) const Inf64 = bitcast(Float64, 0x7ff0000000000000) const NaN64 = bitcast(Float64, 0x7ff8000000000000) const Inf = Inf64 """ Inf, Inf64 Positive infinity of type [`Float64`](@ref). See also: [`isfinite`](@ref), [`typemax`](@ref), [`NaN`](@ref), [`Inf32`](@ref). # Examples ```jldoctest julia> ฯ€/0 Inf julia> +1.0 / -0.0 -Inf julia> โ„ฏ^-Inf 0.0 ``` """ Inf, Inf64 const NaN = NaN64 """ NaN, NaN64 A not-a-number value of type [`Float64`](@ref). See also: [`isnan`](@ref), [`missing`](@ref), [`NaN32`](@ref), [`Inf`](@ref). # Examples ```jldoctest julia> 0/0 NaN julia> Inf - Inf NaN julia> NaN == NaN, isequal(NaN, NaN), NaN === NaN (false, true, true) ``` """ NaN, NaN64 # bit patterns reinterpret(::Type{Unsigned}, x::Float64) = reinterpret(UInt64, x) reinterpret(::Type{Unsigned}, x::Float32) = reinterpret(UInt32, x) reinterpret(::Type{Unsigned}, x::Float16) = reinterpret(UInt16, x) reinterpret(::Type{Signed}, x::Float64) = reinterpret(Int64, x) reinterpret(::Type{Signed}, x::Float32) = reinterpret(Int32, x) reinterpret(::Type{Signed}, x::Float16) = reinterpret(Int16, x) sign_mask(::Type{Float64}) = 0x8000_0000_0000_0000 exponent_mask(::Type{Float64}) = 0x7ff0_0000_0000_0000 exponent_one(::Type{Float64}) = 0x3ff0_0000_0000_0000 exponent_half(::Type{Float64}) = 0x3fe0_0000_0000_0000 significand_mask(::Type{Float64}) = 0x000f_ffff_ffff_ffff sign_mask(::Type{Float32}) = 0x8000_0000 exponent_mask(::Type{Float32}) = 0x7f80_0000 exponent_one(::Type{Float32}) = 0x3f80_0000 exponent_half(::Type{Float32}) = 0x3f00_0000 significand_mask(::Type{Float32}) = 0x007f_ffff sign_mask(::Type{Float16}) = 0x8000 exponent_mask(::Type{Float16}) = 0x7c00 exponent_one(::Type{Float16}) = 0x3c00 exponent_half(::Type{Float16}) = 0x3800 significand_mask(::Type{Float16}) = 0x03ff mantissa(x::T) where {T} = reinterpret(Unsigned, x) & significand_mask(T) for T in (Float16, Float32, Float64) @eval significand_bits(::Type{$T}) = $(trailing_ones(significand_mask(T))) @eval exponent_bits(::Type{$T}) = $(sizeof(T)*8 - significand_bits(T) - 1) @eval exponent_bias(::Type{$T}) = $(Int(exponent_one(T) >> significand_bits(T))) # maximum float exponent @eval exponent_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T)) - exponent_bias(T) - 1) # maximum float exponent without bias @eval exponent_raw_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T))) end """ exponent_max(T) Maximum [`exponent`](@ref) value for a floating point number of type `T`. # Examples ```jldoctest julia> Base.exponent_max(Float64) 1023 ``` Note, `exponent_max(T) + 1` is a possible value of the exponent field with bias, which might be used as sentinel value for `Inf` or `NaN`. """ function exponent_max end """ exponent_raw_max(T) Maximum value of the [`exponent`](@ref) field for a floating point number of type `T` without bias, i.e. the maximum integer value representable by [`exponent_bits(T)`](@ref) bits. """ function exponent_raw_max end """ uabs(x::Integer) Return the absolute value of `x`, possibly returning a different type should the operation be susceptible to overflow. This typically arises when `x` is a two's complement signed integer, so that `abs(typemin(x)) == typemin(x) < 0`, in which case the result of `uabs(x)` will be an unsigned integer of the same size. """ uabs(x::Integer) = abs(x) uabs(x::BitSigned) = unsigned(abs(x)) ## conversions to floating-point ## # TODO: deprecate in 2.0 Float16(x::Integer) = convert(Float16, convert(Float32, x)::Float32) for t1 in (Float16, Float32, Float64) for st in (Int8, Int16, Int32, Int64) @eval begin (::Type{$t1})(x::($st)) = sitofp($t1, x) promote_rule(::Type{$t1}, ::Type{$st}) = $t1 end end for ut in (Bool, UInt8, UInt16, UInt32, UInt64) @eval begin (::Type{$t1})(x::($ut)) = uitofp($t1, x) promote_rule(::Type{$t1}, ::Type{$ut}) = $t1 end end end Bool(x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(:Bool, Bool, x)) promote_rule(::Type{Float64}, ::Type{UInt128}) = Float64 promote_rule(::Type{Float64}, ::Type{Int128}) = Float64 promote_rule(::Type{Float32}, ::Type{UInt128}) = Float32 promote_rule(::Type{Float32}, ::Type{Int128}) = Float32 promote_rule(::Type{Float16}, ::Type{UInt128}) = Float16 promote_rule(::Type{Float16}, ::Type{Int128}) = Float16 function Float64(x::UInt128) if x < UInt128(1) << 104 # Can fit it in two 52 bits mantissas low_exp = 0x1p52 high_exp = 0x1p104 low_bits = (x % UInt64) & Base.significand_mask(Float64) low_value = reinterpret(Float64, reinterpret(UInt64, low_exp) | low_bits) - low_exp high_bits = ((x >> 52) % UInt64) high_value = reinterpret(Float64, reinterpret(UInt64, high_exp) | high_bits) - high_exp low_value + high_value else # Large enough that low bits only affect rounding, pack low bits low_exp = 0x1p76 high_exp = 0x1p128 low_bits = ((x >> 12) % UInt64) >> 12 | (x % UInt64) & 0xFFFFFF low_value = reinterpret(Float64, reinterpret(UInt64, low_exp) | low_bits) - low_exp high_bits = ((x >> 76) % UInt64) high_value = reinterpret(Float64, reinterpret(UInt64, high_exp) | high_bits) - high_exp low_value + high_value end end function Float64(x::Int128) sign_bit = ((x >> 127) % UInt64) << 63 ux = uabs(x) if ux < UInt128(1) << 104 # Can fit it in two 52 bits mantissas low_exp = 0x1p52 high_exp = 0x1p104 low_bits = (ux % UInt64) & Base.significand_mask(Float64) low_value = reinterpret(Float64, reinterpret(UInt64, low_exp) | low_bits) - low_exp high_bits = ((ux >> 52) % UInt64) high_value = reinterpret(Float64, reinterpret(UInt64, high_exp) | high_bits) - high_exp reinterpret(Float64, sign_bit | reinterpret(UInt64, low_value + high_value)) else # Large enough that low bits only affect rounding, pack low bits low_exp = 0x1p76 high_exp = 0x1p128 low_bits = ((ux >> 12) % UInt64) >> 12 | (ux % UInt64) & 0xFFFFFF low_value = reinterpret(Float64, reinterpret(UInt64, low_exp) | low_bits) - low_exp high_bits = ((ux >> 76) % UInt64) high_value = reinterpret(Float64, reinterpret(UInt64, high_exp) | high_bits) - high_exp reinterpret(Float64, sign_bit | reinterpret(UInt64, low_value + high_value)) end end function Float32(x::UInt128) x == 0 && return 0f0 n = top_set_bit(x) # ndigits0z(x,2) if n <= 24 y = ((x % UInt32) << (24-n)) & 0x007f_ffff else y = ((x >> (n-25)) % UInt32) & 0x00ff_ffff # keep 1 extra bit y = (y+one(UInt32))>>1 # round, ties up (extra leading bit in case of next exponent) y &= ~UInt32(trailing_zeros(x) == (n-25)) # fix last bit to round to even end d = ((n+126) % UInt32) << 23 reinterpret(Float32, d + y) end function Float32(x::Int128) x == 0 && return 0f0 s = ((x >>> 96) % UInt32) & 0x8000_0000 # sign bit x = abs(x) % UInt128 n = top_set_bit(x) # ndigits0z(x,2) if n <= 24 y = ((x % UInt32) << (24-n)) & 0x007f_ffff else y = ((x >> (n-25)) % UInt32) & 0x00ff_ffff # keep 1 extra bit y = (y+one(UInt32))>>1 # round, ties up (extra leading bit in case of next exponent) y &= ~UInt32(trailing_zeros(x) == (n-25)) # fix last bit to round to even end d = ((n+126) % UInt32) << 23 reinterpret(Float32, s | d + y) end # TODO: optimize Float16(x::UInt128) = convert(Float16, Float64(x)) Float16(x::Int128) = convert(Float16, Float64(x)) Float16(x::Float32) = fptrunc(Float16, x) Float16(x::Float64) = fptrunc(Float16, x) Float32(x::Float64) = fptrunc(Float32, x) Float32(x::Float16) = fpext(Float32, x) Float64(x::Float32) = fpext(Float64, x) Float64(x::Float16) = fpext(Float64, x) AbstractFloat(x::Bool) = Float64(x) AbstractFloat(x::Int8) = Float64(x) AbstractFloat(x::Int16) = Float64(x) AbstractFloat(x::Int32) = Float64(x) AbstractFloat(x::Int64) = Float64(x) # LOSSY AbstractFloat(x::Int128) = Float64(x) # LOSSY AbstractFloat(x::UInt8) = Float64(x) AbstractFloat(x::UInt16) = Float64(x) AbstractFloat(x::UInt32) = Float64(x) AbstractFloat(x::UInt64) = Float64(x) # LOSSY AbstractFloat(x::UInt128) = Float64(x) # LOSSY Bool(x::Float16) = x==0 ? false : x==1 ? true : throw(InexactError(:Bool, Bool, x)) """ float(x) Convert a number or array to a floating point data type. See also: [`complex`](@ref), [`oftype`](@ref), [`convert`](@ref). # Examples ```jldoctest julia> float(1:1000) 1.0:1.0:1000.0 julia> float(typemax(Int32)) 2.147483647e9 ``` """ float(x) = AbstractFloat(x) """ float(T::Type) Return an appropriate type to represent a value of type `T` as a floating point value. Equivalent to `typeof(float(zero(T)))`. # Examples ```jldoctest julia> float(Complex{Int}) ComplexF64 (alias for Complex{Float64}) julia> float(Int) Float64 ``` """ float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T float(::Type{Union{}}, slurp...) = Union{}(0.0) """ unsafe_trunc(T, x) Return the nearest integral value of type `T` whose absolute value is less than or equal to the absolute value of `x`. If the value is not representable by `T`, an arbitrary value will be returned. See also [`trunc`](@ref). # Examples ```jldoctest julia> unsafe_trunc(Int, -2.2) -2 julia> unsafe_trunc(Int, NaN) -9223372036854775808 ``` """ function unsafe_trunc end for Ti in (Int8, Int16, Int32, Int64) @eval begin unsafe_trunc(::Type{$Ti}, x::IEEEFloat) = fptosi($Ti, x) end end for Ti in (UInt8, UInt16, UInt32, UInt64) @eval begin unsafe_trunc(::Type{$Ti}, x::IEEEFloat) = fptoui($Ti, x) end end function unsafe_trunc(::Type{UInt128}, x::Float64) xu = reinterpret(UInt64,x) k = Int(xu >> 52) & 0x07ff - 1075 xu = (xu & 0x000f_ffff_ffff_ffff) | 0x0010_0000_0000_0000 if k <= 0 UInt128(xu >> -k) else UInt128(xu) << k end end function unsafe_trunc(::Type{Int128}, x::Float64) copysign(unsafe_trunc(UInt128,x) % Int128, x) end function unsafe_trunc(::Type{UInt128}, x::Float32) xu = reinterpret(UInt32,x) k = Int(xu >> 23) & 0x00ff - 150 xu = (xu & 0x007f_ffff) | 0x0080_0000 if k <= 0 UInt128(xu >> -k) else UInt128(xu) << k end end function unsafe_trunc(::Type{Int128}, x::Float32) copysign(unsafe_trunc(UInt128,x) % Int128, x) end unsafe_trunc(::Type{UInt128}, x::Float16) = unsafe_trunc(UInt128, Float32(x)) unsafe_trunc(::Type{Int128}, x::Float16) = unsafe_trunc(Int128, Float32(x)) # matches convert methods # also determines floor, ceil, round trunc(::Type{Signed}, x::IEEEFloat) = trunc(Int,x) trunc(::Type{Unsigned}, x::IEEEFloat) = trunc(UInt,x) trunc(::Type{Integer}, x::IEEEFloat) = trunc(Int,x) # fallbacks floor(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundDown)) ceil(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundUp)) round(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundNearest)) # Bool trunc(::Type{Bool}, x::AbstractFloat) = (-1 < x < 2) ? 1 <= x : throw(InexactError(:trunc, Bool, x)) floor(::Type{Bool}, x::AbstractFloat) = (0 <= x < 2) ? 1 <= x : throw(InexactError(:floor, Bool, x)) ceil(::Type{Bool}, x::AbstractFloat) = (-1 < x <= 1) ? 0 < x : throw(InexactError(:ceil, Bool, x)) round(::Type{Bool}, x::AbstractFloat) = (-0.5 <= x < 1.5) ? 0.5 < x : throw(InexactError(:round, Bool, x)) round(x::IEEEFloat, r::RoundingMode{:ToZero}) = trunc_llvm(x) round(x::IEEEFloat, r::RoundingMode{:Down}) = floor_llvm(x) round(x::IEEEFloat, r::RoundingMode{:Up}) = ceil_llvm(x) round(x::IEEEFloat, r::RoundingMode{:Nearest}) = rint_llvm(x) ## floating point promotions ## promote_rule(::Type{Float32}, ::Type{Float16}) = Float32 promote_rule(::Type{Float64}, ::Type{Float16}) = Float64 promote_rule(::Type{Float64}, ::Type{Float32}) = Float64 widen(::Type{Float16}) = Float32 widen(::Type{Float32}) = Float64 ## floating point arithmetic ## -(x::IEEEFloat) = neg_float(x) +(x::T, y::T) where {T<:IEEEFloat} = add_float(x, y) -(x::T, y::T) where {T<:IEEEFloat} = sub_float(x, y) *(x::T, y::T) where {T<:IEEEFloat} = mul_float(x, y) /(x::T, y::T) where {T<:IEEEFloat} = div_float(x, y) muladd(x::T, y::T, z::T) where {T<:IEEEFloat} = muladd_float(x, y, z) # TODO: faster floating point div? # TODO: faster floating point fld? # TODO: faster floating point mod? function unbiased_exponent(x::T) where {T<:IEEEFloat} return (reinterpret(Unsigned, x) & exponent_mask(T)) >> significand_bits(T) end function explicit_mantissa_noinfnan(x::T) where {T<:IEEEFloat} m = mantissa(x) issubnormal(x) || (m |= significand_mask(T) + uinttype(T)(1)) return m end function _to_float(number::U, ep) where {U<:Unsigned} F = floattype(U) S = signed(U) epint = unsafe_trunc(S,ep) lz::signed(U) = unsafe_trunc(S, Core.Intrinsics.ctlz_int(number) - U(exponent_bits(F))) number <<= lz epint -= lz bits = U(0) if epint >= 0 bits = number & significand_mask(F) bits |= ((epint + S(1)) << significand_bits(F)) & exponent_mask(F) else bits = (number >> -epint) & significand_mask(F) end return reinterpret(F, bits) end @assume_effects :terminates_locally :nothrow function rem_internal(x::T, y::T) where {T<:IEEEFloat} xuint = reinterpret(Unsigned, x) yuint = reinterpret(Unsigned, y) if xuint <= yuint if xuint < yuint return x end return zero(T) end e_x = unbiased_exponent(x) e_y = unbiased_exponent(y) # Most common case where |y| is "very normal" and |x/y| < 2^EXPONENT_WIDTH if e_y > (significand_bits(T)) && (e_x - e_y) <= (exponent_bits(T)) m_x = explicit_mantissa_noinfnan(x) m_y = explicit_mantissa_noinfnan(y) d = urem_int((m_x << (e_x - e_y)), m_y) iszero(d) && return zero(T) return _to_float(d, e_y - uinttype(T)(1)) end # Both are subnormals if e_x == 0 && e_y == 0 return reinterpret(T, urem_int(xuint, yuint) & significand_mask(T)) end m_x = explicit_mantissa_noinfnan(x) e_x -= uinttype(T)(1) m_y = explicit_mantissa_noinfnan(y) lz_m_y = uinttype(T)(exponent_bits(T)) if e_y > 0 e_y -= uinttype(T)(1) else m_y = mantissa(y) lz_m_y = Core.Intrinsics.ctlz_int(m_y) end tz_m_y = Core.Intrinsics.cttz_int(m_y) sides_zeroes_cnt = lz_m_y + tz_m_y # n>0 exp_diff = e_x - e_y # Shift hy right until the end or n = 0 right_shift = min(exp_diff, tz_m_y) m_y >>= right_shift exp_diff -= right_shift e_y += right_shift # Shift hx left until the end or n = 0 left_shift = min(exp_diff, uinttype(T)(exponent_bits(T))) m_x <<= left_shift exp_diff -= left_shift m_x = urem_int(m_x, m_y) iszero(m_x) && return zero(T) iszero(exp_diff) && return _to_float(m_x, e_y) while exp_diff > sides_zeroes_cnt exp_diff -= sides_zeroes_cnt m_x <<= sides_zeroes_cnt m_x = urem_int(m_x, m_y) end m_x <<= exp_diff m_x = urem_int(m_x, m_y) return _to_float(m_x, e_y) end function rem(x::T, y::T) where {T<:IEEEFloat} if isfinite(x) && !iszero(x) && isfinite(y) && !iszero(y) return copysign(rem_internal(abs(x), abs(y)), x) elseif isinf(x) || isnan(y) || iszero(y) # y can still be Inf return T(NaN) else return x end end function mod(x::T, y::T) where {T<:AbstractFloat} r = rem(x,y) if r == 0 copysign(r,y) elseif (r > 0) โŠป (y > 0) r+y else r end end ## floating point comparisons ## ==(x::T, y::T) where {T<:IEEEFloat} = eq_float(x, y) !=(x::T, y::T) where {T<:IEEEFloat} = ne_float(x, y) <( x::T, y::T) where {T<:IEEEFloat} = lt_float(x, y) <=(x::T, y::T) where {T<:IEEEFloat} = le_float(x, y) isequal(x::T, y::T) where {T<:IEEEFloat} = fpiseq(x, y) # interpret as sign-magnitude integer @inline function _fpint(x) IntT = inttype(typeof(x)) ix = reinterpret(IntT, x) return ifelse(ix < zero(IntT), ix โŠป typemax(IntT), ix) end @inline function isless(a::T, b::T) where T<:IEEEFloat (isnan(a) || isnan(b)) && return !isnan(a) return _fpint(a) < _fpint(b) end # Exact Float (Tf) vs Integer (Ti) comparisons # Assumes: # - typemax(Ti) == 2^n-1 # - typemax(Ti) can't be exactly represented by Tf: # => Tf(typemax(Ti)) == 2^n or Inf # - typemin(Ti) can be exactly represented by Tf # # 1. convert y::Ti to float fy::Tf # 2. perform Tf comparison x vs fy # 3. if x == fy, check if (1) resulted in rounding: # a. convert fy back to Ti and compare with original y # b. unsafe_convert undefined behaviour if fy == Tf(typemax(Ti)) # (but consequently x == fy > y) for Ti in (Int64,UInt64,Int128,UInt128) for Tf in (Float32,Float64) @eval begin function ==(x::$Tf, y::$Ti) fy = ($Tf)(y) (x == fy) & (fy != $(Tf(typemax(Ti)))) & (y == unsafe_trunc($Ti,fy)) end ==(y::$Ti, x::$Tf) = x==y function <(x::$Ti, y::$Tf) fx = ($Tf)(x) (fx < y) | ((fx == y) & ((fx == $(Tf(typemax(Ti)))) | (x < unsafe_trunc($Ti,fx)) )) end function <=(x::$Ti, y::$Tf) fx = ($Tf)(x) (fx < y) | ((fx == y) & ((fx == $(Tf(typemax(Ti)))) | (x <= unsafe_trunc($Ti,fx)) )) end function <(x::$Tf, y::$Ti) fy = ($Tf)(y) (x < fy) | ((x == fy) & (fy < $(Tf(typemax(Ti)))) & (unsafe_trunc($Ti,fy) < y)) end function <=(x::$Tf, y::$Ti) fy = ($Tf)(y) (x < fy) | ((x == fy) & (fy < $(Tf(typemax(Ti)))) & (unsafe_trunc($Ti,fy) <= y)) end end end end for op in (:(==), :<, :<=) @eval begin ($op)(x::Float16, y::Union{Int128,UInt128,Int64,UInt64}) = ($op)(Float64(x), Float64(y)) ($op)(x::Union{Int128,UInt128,Int64,UInt64}, y::Float16) = ($op)(Float64(x), Float64(y)) ($op)(x::Union{Float16,Float32}, y::Union{Int32,UInt32}) = ($op)(Float64(x), Float64(y)) ($op)(x::Union{Int32,UInt32}, y::Union{Float16,Float32}) = ($op)(Float64(x), Float64(y)) ($op)(x::Float16, y::Union{Int16,UInt16}) = ($op)(Float32(x), Float32(y)) ($op)(x::Union{Int16,UInt16}, y::Float16) = ($op)(Float32(x), Float32(y)) end end abs(x::IEEEFloat) = abs_float(x) """ isnan(f) -> Bool Test whether a number value is a NaN, an indeterminate value which is neither an infinity nor a finite number ("not a number"). See also: [`iszero`](@ref), [`isone`](@ref), [`isinf`](@ref), [`ismissing`](@ref). """ isnan(x::AbstractFloat) = (x != x)::Bool isnan(x::Number) = false isfinite(x::AbstractFloat) = !isnan(x - x) isfinite(x::Real) = decompose(x)[3] != 0 isfinite(x::Integer) = true """ isinf(f) -> Bool Test whether a number is infinite. See also: [`Inf`](@ref), [`iszero`](@ref), [`isfinite`](@ref), [`isnan`](@ref). """ isinf(x::Real) = !isnan(x) & !isfinite(x) isinf(x::IEEEFloat) = abs(x) === oftype(x, Inf) const hx_NaN = hash_uint64(reinterpret(UInt64, NaN)) function hash(x::Float64, h::UInt) # see comments on trunc and hash(Real, UInt) if typemin(Int64) <= x < typemax(Int64) xi = fptosi(Int64, x) if isequal(xi, x) return hash(xi, h) end elseif typemin(UInt64) <= x < typemax(UInt64) xu = fptoui(UInt64, x) if isequal(xu, x) return hash(xu, h) end elseif isnan(x) return hx_NaN โŠป h # NaN does not have a stable bit pattern end return hash_uint64(bitcast(UInt64, x)) - 3h end hash(x::Float32, h::UInt) = hash(Float64(x), h) function hash(x::Float16, h::UInt) # see comments on trunc and hash(Real, UInt) if isfinite(x) # all finite Float16 fit in Int64 xi = fptosi(Int64, x) if isequal(xi, x) return hash(xi, h) end elseif isnan(x) return hx_NaN โŠป h # NaN does not have a stable bit pattern end return hash_uint64(bitcast(UInt64, Float64(x))) - 3h end ## generic hashing for rational values ## function hash(x::Real, h::UInt) # decompose x as num*2^pow/den num, pow, den = decompose(x) # handle special values num == 0 && den == 0 && return hash(NaN, h) num == 0 && return hash(ifelse(den > 0, 0.0, -0.0), h) den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) # normalize decomposition if den < 0 num = -num den = -den end num_z = trailing_zeros(num) num >>= num_z den_z = trailing_zeros(den) den >>= den_z pow += num_z - den_z # If the real can be represented as an Int64, UInt64, or Float64, hash as those types. # To be an Integer the denominator must be 1 and the power must be non-negative. if den == 1 # left = ceil(log2(num*2^pow)) left = top_set_bit(abs(num)) + pow # 2^-1074 is the minimum Float64 so if the power is smaller, not a Float64 if -1074 <= pow if 0 <= pow # if pow is non-negative, it is an integer left <= 63 && return hash(Int64(num) << Int(pow), h) left <= 64 && !signbit(num) && return hash(UInt64(num) << Int(pow), h) end # typemin(Int64) handled by Float64 case # 2^1024 is the maximum Float64 so if the power is greater, not a Float64 # Float64s only have 53 mantisa bits (including implicit bit) left <= 1024 && left - pow <= 53 && return hash(ldexp(Float64(num), pow), h) end else h = hash_integer(den, h) end # handle generic rational values h = hash_integer(pow, h) h = hash_integer(num, h) return h end #= `decompose(x)`: non-canonical decomposition of rational values as `num*2^pow/den`. The decompose function is the point where rational-valued numeric types that support hashing hook into the hashing protocol. `decompose(x)` should return three integer values `num, pow, den`, such that the value of `x` is mathematically equal to num*2^pow/den The decomposition need not be canonical in the sense that it just needs to be *some* way to express `x` in this form, not any particular way โ€“ with the restriction that `num` and `den` may not share any odd common factors. They may, however, have powers of two in common โ€“ the generic hashing code will normalize those as necessary. Special values: - `x` is zero: `num` should be zero and `den` should have the same sign as `x` - `x` is infinite: `den` should be zero and `num` should have the same sign as `x` - `x` is not a number: `num` and `den` should both be zero =# decompose(x::Integer) = x, 0, 1 function decompose(x::Float16)::NTuple{3,Int} isnan(x) && return 0, 0, 0 isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 n = reinterpret(UInt16, x) s = (n & 0x03ff) % Int16 e = ((n & 0x7c00) >> 10) % Int s |= Int16(e != 0) << 10 d = ifelse(signbit(x), -1, 1) s, e - 25 + (e == 0), d end function decompose(x::Float32)::NTuple{3,Int} isnan(x) && return 0, 0, 0 isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 n = reinterpret(UInt32, x) s = (n & 0x007fffff) % Int32 e = ((n & 0x7f800000) >> 23) % Int s |= Int32(e != 0) << 23 d = ifelse(signbit(x), -1, 1) s, e - 150 + (e == 0), d end function decompose(x::Float64)::Tuple{Int64, Int, Int} isnan(x) && return 0, 0, 0 isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 n = reinterpret(UInt64, x) s = (n & 0x000fffffffffffff) % Int64 e = ((n & 0x7ff0000000000000) >> 52) % Int s |= Int64(e != 0) << 52 d = ifelse(signbit(x), -1, 1) s, e - 1075 + (e == 0), d end """ precision(num::AbstractFloat; base::Integer=2) precision(T::Type; base::Integer=2) Get the precision of a floating point number, as defined by the effective number of bits in the significand, or the precision of a floating-point type `T` (its current default, if `T` is a variable-precision type like [`BigFloat`](@ref)). If `base` is specified, then it returns the maximum corresponding number of significand digits in that base. !!! compat "Julia 1.8" The `base` keyword requires at least Julia 1.8. """ function precision end _precision(::Type{Float16}) = 11 _precision(::Type{Float32}) = 24 _precision(::Type{Float64}) = 53 function _precision(x, base::Integer=2) base > 1 || throw(DomainError(base, "`base` cannot be less than 2.")) p = _precision(x) return base == 2 ? Int(p) : floor(Int, p / log2(base)) end precision(::Type{T}; base::Integer=2) where {T<:AbstractFloat} = _precision(T, base) precision(::T; base::Integer=2) where {T<:AbstractFloat} = precision(T; base) """ nextfloat(x::AbstractFloat, n::Integer) The result of `n` iterative applications of `nextfloat` to `x` if `n >= 0`, or `-n` applications of [`prevfloat`](@ref) if `n < 0`. """ function nextfloat(f::IEEEFloat, d::Integer) F = typeof(f) fumax = reinterpret(Unsigned, F(Inf)) U = typeof(fumax) isnan(f) && return f fi = reinterpret(Signed, f) fneg = fi < 0 fu = unsigned(fi & typemax(fi)) dneg = d < 0 da = uabs(d) if da > typemax(U) fneg = dneg fu = fumax else du = da % U if fneg โŠป dneg if du > fu fu = min(fumax, du - fu) fneg = !fneg else fu = fu - du end else if fumax - fu < du fu = fumax else fu = fu + du end end end if fneg fu |= sign_mask(F) end reinterpret(F, fu) end """ nextfloat(x::AbstractFloat) Return the smallest floating point number `y` of the same type as `x` such `x < y`. If no such `y` exists (e.g. if `x` is `Inf` or `NaN`), then return `x`. See also: [`prevfloat`](@ref), [`eps`](@ref), [`issubnormal`](@ref). """ nextfloat(x::AbstractFloat) = nextfloat(x,1) """ prevfloat(x::AbstractFloat, n::Integer) The result of `n` iterative applications of `prevfloat` to `x` if `n >= 0`, or `-n` applications of [`nextfloat`](@ref) if `n < 0`. """ prevfloat(x::AbstractFloat, d::Integer) = nextfloat(x, -d) """ prevfloat(x::AbstractFloat) Return the largest floating point number `y` of the same type as `x` such `y < x`. If no such `y` exists (e.g. if `x` is `-Inf` or `NaN`), then return `x`. """ prevfloat(x::AbstractFloat) = nextfloat(x,-1) for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128) for Tf in (Float16, Float32, Float64) if Ti <: Unsigned || sizeof(Ti) < sizeof(Tf) # Here `Tf(typemin(Ti))-1` is exact, so we can compare the lower-bound # directly. `Tf(typemax(Ti))+1` is either always exactly representable, or # rounded to `Inf` (e.g. when `Ti==UInt128 && Tf==Float32`). @eval begin function trunc(::Type{$Ti},x::$Tf) if $(Tf(typemin(Ti))-one(Tf)) < x < $(Tf(typemax(Ti))+one(Tf)) return unsafe_trunc($Ti,x) else throw(InexactError(:trunc, $Ti, x)) end end function (::Type{$Ti})(x::$Tf) # When typemax(Ti) is not representable by Tf but typemax(Ti) + 1 is, # then < Tf(typemax(Ti) + 1) is stricter than <= Tf(typemax(Ti)). Using # the former causes us to throw on UInt64(Float64(typemax(UInt64))+1) if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))+one(Tf))) && isinteger(x) return unsafe_trunc($Ti,x) else throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) end end end else # Here `eps(Tf(typemin(Ti))) > 1`, so the only value which can be truncated to # `Tf(typemin(Ti)` is itself. Similarly, `Tf(typemax(Ti))` is inexact and will # be rounded up. This assumes that `Tf(typemin(Ti)) > -Inf`, which is true for # these types, but not for `Float16` or larger integer types. @eval begin function trunc(::Type{$Ti},x::$Tf) if $(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))) return unsafe_trunc($Ti,x) else throw(InexactError(:trunc, $Ti, x)) end end function (::Type{$Ti})(x::$Tf) if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))) && isinteger(x) return unsafe_trunc($Ti,x) else throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) end end end end end end """ issubnormal(f) -> Bool Test whether a floating point number is subnormal. An IEEE floating point number is [subnormal](https://en.wikipedia.org/wiki/Subnormal_number) when its exponent bits are zero and its significand is not zero. # Examples ```jldoctest julia> floatmin(Float32) 1.1754944f-38 julia> issubnormal(1.0f-37) false julia> issubnormal(1.0f-38) true ``` """ function issubnormal(x::T) where {T<:IEEEFloat} y = reinterpret(Unsigned, x) (y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0) end ispow2(x::AbstractFloat) = !iszero(x) && frexp(x)[1] == 0.5 iseven(x::AbstractFloat) = isinteger(x) && (abs(x) > maxintfloat(x) || iseven(Integer(x))) isodd(x::AbstractFloat) = isinteger(x) && abs(x) โ‰ค maxintfloat(x) && isodd(Integer(x)) @eval begin typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00)) typemax(::Type{Float16}) = $(Inf16) typemin(::Type{Float32}) = $(-Inf32) typemax(::Type{Float32}) = $(Inf32) typemin(::Type{Float64}) = $(-Inf64) typemax(::Type{Float64}) = $(Inf64) typemin(x::T) where {T<:Real} = typemin(T) typemax(x::T) where {T<:Real} = typemax(T) floatmin(::Type{Float16}) = $(bitcast(Float16, 0x0400)) floatmin(::Type{Float32}) = $(bitcast(Float32, 0x00800000)) floatmin(::Type{Float64}) = $(bitcast(Float64, 0x0010000000000000)) floatmax(::Type{Float16}) = $(bitcast(Float16, 0x7bff)) floatmax(::Type{Float32}) = $(bitcast(Float32, 0x7f7fffff)) floatmax(::Type{Float64}) = $(bitcast(Float64, 0x7fefffffffffffff)) eps(x::AbstractFloat) = isfinite(x) ? abs(x) >= floatmin(x) ? ldexp(eps(typeof(x)), exponent(x)) : nextfloat(zero(x)) : oftype(x, NaN) eps(::Type{Float16}) = $(bitcast(Float16, 0x1400)) eps(::Type{Float32}) = $(bitcast(Float32, 0x34000000)) eps(::Type{Float64}) = $(bitcast(Float64, 0x3cb0000000000000)) eps() = eps(Float64) end """ floatmin(T = Float64) Return the smallest positive normal number representable by the floating-point type `T`. # Examples ```jldoctest julia> floatmin(Float16) Float16(6.104e-5) julia> floatmin(Float32) 1.1754944f-38 julia> floatmin() 2.2250738585072014e-308 ``` """ floatmin(x::T) where {T<:AbstractFloat} = floatmin(T) """ floatmax(T = Float64) Return the largest finite number representable by the floating-point type `T`. See also: [`typemax`](@ref), [`floatmin`](@ref), [`eps`](@ref). # Examples ```jldoctest julia> floatmax(Float16) Float16(6.55e4) julia> floatmax(Float32) 3.4028235f38 julia> floatmax() 1.7976931348623157e308 julia> typemax(Float64) Inf ``` """ floatmax(x::T) where {T<:AbstractFloat} = floatmax(T) floatmin() = floatmin(Float64) floatmax() = floatmax(Float64) """ eps(::Type{T}) where T<:AbstractFloat eps() Return the *machine epsilon* of the floating point type `T` (`T = Float64` by default). This is defined as the gap between 1 and the next largest value representable by `typeof(one(T))`, and is equivalent to `eps(one(T))`. (Since `eps(T)` is a bound on the *relative error* of `T`, it is a "dimensionless" quantity like [`one`](@ref).) # Examples ```jldoctest julia> eps() 2.220446049250313e-16 julia> eps(Float32) 1.1920929f-7 julia> 1.0 + eps() 1.0000000000000002 julia> 1.0 + eps()/2 1.0 ``` """ eps(::Type{<:AbstractFloat}) """ eps(x::AbstractFloat) Return the *unit in last place* (ulp) of `x`. This is the distance between consecutive representable floating point values at `x`. In most cases, if the distance on either side of `x` is different, then the larger of the two is taken, that is eps(x) == max(x-prevfloat(x), nextfloat(x)-x) The exceptions to this rule are the smallest and largest finite values (e.g. `nextfloat(-Inf)` and `prevfloat(Inf)` for [`Float64`](@ref)), which round to the smaller of the values. The rationale for this behavior is that `eps` bounds the floating point rounding error. Under the default `RoundNearest` rounding mode, if ``y`` is a real number and ``x`` is the nearest floating point number to ``y``, then ```math |y-x| \\leq \\operatorname{eps}(x)/2. ``` See also: [`nextfloat`](@ref), [`issubnormal`](@ref), [`floatmax`](@ref). # Examples ```jldoctest julia> eps(1.0) 2.220446049250313e-16 julia> eps(prevfloat(2.0)) 2.220446049250313e-16 julia> eps(2.0) 4.440892098500626e-16 julia> x = prevfloat(Inf) # largest finite Float64 1.7976931348623157e308 julia> x + eps(x)/2 # rounds up Inf julia> x + prevfloat(eps(x)/2) # rounds down 1.7976931348623157e308 ``` """ eps(::AbstractFloat) ## byte order swaps for arbitrary-endianness serialization/deserialization ## bswap(x::IEEEFloat) = bswap_int(x) # integer size of float uinttype(::Type{Float64}) = UInt64 uinttype(::Type{Float32}) = UInt32 uinttype(::Type{Float16}) = UInt16 inttype(::Type{Float64}) = Int64 inttype(::Type{Float32}) = Int32 inttype(::Type{Float16}) = Int16 # float size of integer floattype(::Type{UInt64}) = Float64 floattype(::Type{UInt32}) = Float32 floattype(::Type{UInt16}) = Float16 floattype(::Type{Int64}) = Float64 floattype(::Type{Int32}) = Float32 floattype(::Type{Int16}) = Float16 ## Array operations on floating point numbers ## float(A::AbstractArray{<:AbstractFloat}) = A function float(A::AbstractArray{T}) where T if !isconcretetype(T) error("`float` not defined on abstractly-typed arrays; please convert to a more specific type") end convert(AbstractArray{typeof(float(zero(T)))}, A) end float(r::StepRange) = float(r.start):float(r.step):float(last(r)) float(r::UnitRange) = float(r.start):float(last(r)) float(r::StepRangeLen{T}) where {T} = StepRangeLen{typeof(float(T(r.ref)))}(float(r.ref), float(r.step), length(r), r.offset) function float(r::LinRange) LinRange(float(r.start), float(r.stop), length(r)) end U/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/twiceprecision.jlRs# This file is a part of Julia. License is MIT: https://julialang.org/license # Twice-precision arithmetic. # Necessary for creating nicely-behaved ranges like r = 0.1:0.1:0.3 # that return r[3] == 0.3. Otherwise, we have roundoff error due to # 0.1 + 2*0.1 = 0.30000000000000004 """ hi, lo = splitprec(F::Type{<:AbstractFloat}, i::Integer) Represent an integer `i` as a pair of floating-point numbers `hi` and `lo` (of type `F`) such that: - `widen(hi) + widen(lo) โ‰ˆ i`. It is exact if 1.5 * (number of precision bits in `F`) is greater than the number of bits in `i`. - all bits in `hi` are more significant than any of the bits in `lo` - `hi` can be exactly multiplied by the `hi` component of another call to `splitprec`. In particular, while `convert(Float64, i)` can be lossy since Float64 has only 53 bits of precision, `splitprec(Float64, i)` is exact for any Int64/UInt64. """ function splitprec(::Type{F}, i::Integer) where {F<:AbstractFloat} hi = truncbits(F(i), cld(precision(F), 2)) ihi = oftype(i, hi) hi, F(i - ihi) end function truncmask(x::F, mask) where {F<:IEEEFloat} reinterpret(F, mask & reinterpret(uinttype(F), x)) end truncmask(x, mask) = x function truncbits(x::F, nb) where {F<:IEEEFloat} truncmask(x, typemax(uinttype(F)) << nb) end truncbits(x, nb) = x ## Dekker arithmetic """ hi, lo = canonicalize2(big, little) Generate a representation where all the nonzero bits in `hi` are more significant than any of the nonzero bits in `lo`. `big` must be larger in absolute value than `little`. """ function canonicalize2(big, little) h = big+little h, (big - h) + little end """ zhi, zlo = add12(x, y) A high-precision representation of `x + y` for floating-point numbers. Mathematically, `zhi + zlo = x + y`, where `zhi` contains the most significant bits and `zlo` the least significant. Because of the way floating-point numbers are printed, `lo` may not look the way you might expect from the standpoint of decimal representation, even though it is exact from the standpoint of binary representation. Example: ```julia-repl julia> 1.0 + 1.0001e-15 1.000000000000001 julia> big(1.0) + big(1.0001e-15) 1.000000000000001000100000000000020165767380775934141445417482375879192346701529 julia> hi, lo = Base.add12(1.0, 1.0001e-15) (1.000000000000001, -1.1012302462515652e-16) julia> big(hi) + big(lo) 1.000000000000001000100000000000020165767380775934141445417482375879192346701529 ``` `lo` differs from 1.0e-19 because `hi` is not exactly equal to the first 16 decimal digits of the answer. """ function add12(x::T, y::T) where {T} x, y = ifelse(abs(y) > abs(x), (y, x), (x, y)) canonicalize2(x, y) end add12(x, y) = add12(promote(x, y)...) """ zhi, zlo = mul12(x, y) A high-precision representation of `x * y` for floating-point numbers. Mathematically, `zhi + zlo = x * y`, where `zhi` contains the most significant bits and `zlo` the least significant. Example: ```julia-repl julia> x = Float32(ฯ€) 3.1415927f0 julia> x * x 9.869605f0 julia> Float64(x) * Float64(x) 9.869604950382893 julia> hi, lo = Base.mul12(x, x) (9.869605f0, -1.140092f-7) julia> Float64(hi) + Float64(lo) 9.869604950382893 ``` """ function mul12(x::T, y::T) where {T<:AbstractFloat} (h, l) = Math.two_mul(x, y) ifelse(!isfinite(h), (h, h), (h, l)) end mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) mul12(x, y) = mul12(promote(x, y)...) """ zhi, zlo = div12(x, y) A high-precision representation of `x / y` for floating-point numbers. Mathematically, `zhi + zlo โ‰ˆ x / y`, where `zhi` contains the most significant bits and `zlo` the least significant. Example: ```julia-repl julia> x, y = Float32(ฯ€), 3.1f0 (3.1415927f0, 3.1f0) julia> x / y 1.013417f0 julia> Float64(x) / Float64(y) 1.0134170444063078 julia> hi, lo = Base.div12(x, y) (1.013417f0, 3.8867366f-8) julia> Float64(hi) + Float64(lo) 1.0134170444063066 ``` """ function div12(x::T, y::T) where {T<:AbstractFloat} # We lose precision if any intermediate calculation results in a subnormal. # To prevent this from happening, standardize the values. xs, xe = frexp(x) ys, ye = frexp(y) r = xs / ys rh, rl = canonicalize2(r, -fma(r, ys, -xs)/ys) ifelse(iszero(r) | !isfinite(r), (r, r), (ldexp(rh, xe-ye), ldexp(rl, xe-ye))) end div12(x::T, y::T) where {T} = (p = x / y; (p, zero(p))) div12(x, y) = div12(promote(x, y)...) ## TwicePrecision """ TwicePrecision{T}(hi::T, lo::T) TwicePrecision{T}((num, denom)) A number with twice the precision of `T`, e.g., quad-precision if `T = Float64`. !!! warning `TwicePrecision` is an internal type used to increase the precision of floating-point ranges, and not intended for external use. If you encounter them in real code, the most likely explanation is that you are directly accessing the fields of a range. Use the function interface instead, `step(r)` rather than `r.step` # Extended help `hi` represents the high bits (most significant bits) and `lo` the low bits (least significant bits). Rational values `num//denom` can be approximated conveniently using the syntax `TwicePrecision{T}((num, denom))`. When used with `T<:Union{Float16,Float32,Float64}` to construct an "exact" `StepRangeLen`, `ref` should be the range element with smallest magnitude and `offset` set to the corresponding index. For efficiency, multiplication of `step` by the index is not performed at twice precision: `step.hi` should have enough trailing zeros in its `bits` representation that `(0:len-1)*step.hi` is exact (has no roundoff error). If `step` has an exact rational representation `num//denom`, then you can construct `step` using step = TwicePrecision{T}((num, denom), nb) where `nb` is the number of trailing zero bits of `step.hi`. For ranges, you can set `nb = ceil(Int, log2(len-1))`. """ struct TwicePrecision{T} hi::T # most significant bits lo::T # least significant bits end TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) TwicePrecision{T}(x::TwicePrecision{T}) where {T} = x function TwicePrecision{T}(x) where {T} xT = T(x) ฮ”x = x - xT TwicePrecision{T}(xT, T(ฮ”x)) end TwicePrecision{T}(i::Integer) where {T<:AbstractFloat} = TwicePrecision{T}(canonicalize2(splitprec(T, i)...)...) TwicePrecision(x) = TwicePrecision{typeof(x)}(x) # Numerator/Denominator constructors function TwicePrecision{T}(nd::Tuple{Integer,Integer}) where {T<:Union{Float16,Float32}} n, d = nd TwicePrecision{T}(n/d) end function TwicePrecision{T}(nd::Tuple{Any,Any}) where {T} n, d = nd TwicePrecision{T}(TwicePrecision{T}(n) / d) end function TwicePrecision{T}(nd::Tuple{I,I}, nb::Integer) where {T,I} twiceprecision(TwicePrecision{T}(nd), nb) end # Fix #39798 # See steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, # step::Tuple{Integer,Integer}, nb::Integer, # len::Integer, offset::Integer) function TwicePrecision{T}(nd::Tuple{Integer,Integer}, nb::Integer) where T twiceprecision(TwicePrecision{T}(nd), nb) end # Truncating constructors. Useful for generating values that can be # exactly multiplied by small integers. function twiceprecision(val::T, nb::Integer) where {T<:IEEEFloat} hi = truncbits(val, nb) TwicePrecision{T}(hi, val - hi) end function twiceprecision(val::TwicePrecision{T}, nb::Integer) where {T<:IEEEFloat} hi = truncbits(val.hi, nb) TwicePrecision{T}(hi, (val.hi - hi) + val.lo) end nbitslen(r::StepRangeLen) = nbitslen(eltype(r), length(r), r.offset) nbitslen(::Type{T}, len, offset) where {T<:IEEEFloat} = min(cld(precision(T), 2), nbitslen(len, offset)) # The +1 here is for safety, because the precision of the significand # is 1 bit higher than the number that are explicitly stored. nbitslen(len, offset) = len < 2 ? 0 : top_set_bit(max(offset-1, len-offset) - 1) + 1 eltype(::Type{TwicePrecision{T}}) where {T} = T promote_rule(::Type{TwicePrecision{R}}, ::Type{TwicePrecision{S}}) where {R,S} = TwicePrecision{promote_type(R,S)} promote_rule(::Type{TwicePrecision{R}}, ::Type{S}) where {R,S<:Number} = TwicePrecision{promote_type(R,S)} (::Type{T})(x::TwicePrecision) where {T<:Number} = (T(x.hi) + T(x.lo))::T convert(::Type{TwicePrecision{T}}, x::TwicePrecision{T}) where {T} = x convert(::Type{TwicePrecision{T}}, x::TwicePrecision) where {T} = TwicePrecision{T}(convert(T, x.hi), convert(T, x.lo))::TwicePrecision{T} convert(::Type{T}, x::TwicePrecision) where {T<:Number} = T(x)::T convert(::Type{TwicePrecision{T}}, x::Number) where {T} = TwicePrecision{T}(x)::TwicePrecision{T} float(x::TwicePrecision{<:AbstractFloat}) = x float(x::TwicePrecision) = TwicePrecision(float(x.hi), float(x.lo)) big(x::TwicePrecision) = big(x.hi) + big(x.lo) -(x::TwicePrecision) = TwicePrecision(-x.hi, -x.lo) function zero(::Type{TwicePrecision{T}}) where {T} z = zero(T) TwicePrecision{T}(z, z) end # Arithmetic function +(x::TwicePrecision, y::Number) s_hi, s_lo = add12(x.hi, y) TwicePrecision(canonicalize2(s_hi, s_lo+x.lo)...) end +(x::Number, y::TwicePrecision) = y+x function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T r = x.hi + y.hi s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo TwicePrecision(canonicalize2(r, s)...) end +(x::TwicePrecision, y::TwicePrecision) = +(promote(x, y)...) -(x::TwicePrecision, y::TwicePrecision) = x + (-y) -(x::TwicePrecision, y::Number) = x + (-y) -(x::Number, y::TwicePrecision) = x + (-y) function *(x::TwicePrecision, v::Number) v == 0 && return TwicePrecision(x.hi*v, x.lo*v) x * TwicePrecision(oftype(x.hi*v, v)) end function *(x::TwicePrecision{<:IEEEFloat}, v::Integer) v == 0 && return TwicePrecision(x.hi*v, x.lo*v) nb = top_set_bit(abs(v)-1) u = truncbits(x.hi, nb) TwicePrecision(canonicalize2(u*v, ((x.hi-u) + x.lo)*v)...) end *(v::Number, x::TwicePrecision) = x*v function *(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} zh, zl = mul12(x.hi, y.hi) ret = TwicePrecision{T}(canonicalize2(zh, (x.hi * y.lo + x.lo * y.hi) + zl)...) ifelse(iszero(zh) | !isfinite(zh), TwicePrecision{T}(zh, zh), ret) end *(x::TwicePrecision, y::TwicePrecision) = *(promote(x, y)...) function /(x::TwicePrecision, v::Number) x / TwicePrecision(oftype(x.hi/v, v)) end function /(x::TwicePrecision, y::TwicePrecision) hi = x.hi / y.hi uh, ul = mul12(hi, y.hi) lo = ((((x.hi - uh) - ul) + x.lo) - hi*y.lo)/y.hi ret = TwicePrecision(canonicalize2(hi, lo)...) ifelse(iszero(hi) | !isfinite(hi), TwicePrecision(hi, hi), ret) end ## StepRangeLen # Use TwicePrecision only for Float64; use Float64 for T<:Union{Float16,Float32} # See also _linspace1 # Ratio-of-integers constructors function steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref), TwicePrecision{Float64}(step, nb), len, offset) end function steprangelen_hp(::Type{T}, ref::Tuple{Integer,Integer}, step::Tuple{Integer,Integer}, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], len, offset) end # AbstractFloat constructors (can supply a single number or a 2-tuple const F_or_FF = Union{AbstractFloat, Tuple{AbstractFloat,AbstractFloat}} asF64(x::AbstractFloat) = Float64(x) asF64(x::Tuple{AbstractFloat,AbstractFloat}) = Float64(x[1]) + Float64(x[2]) function steprangelen_hp(::Type{Float64}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) StepRangeLen(TwicePrecision{Float64}(ref...), twiceprecision(TwicePrecision{Float64}(step...), nb), len, offset) end function steprangelen_hp(::Type{T}, ref::F_or_FF, step::F_or_FF, nb::Integer, len::Integer, offset::Integer) where {T<:IEEEFloat} StepRangeLen{T}(asF64(ref), asF64(step), len, offset) end StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, len::Integer, offset::Integer=1) where {T} = StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}(ref, step, len, offset) # Construct range for rational start=start_n/den, step=step_n/den function floatrange(::Type{T}, start_n::Integer, step_n::Integer, len::Integer, den::Integer) where T len = len + 0 # promote with Int if len < 2 || step_n == 0 return steprangelen_hp(T, (start_n, den), (step_n, den), 0, len, oneunit(len)) end # index of smallest-magnitude value L = typeof(len) imin = clamp(round(typeof(len), -start_n/step_n+1), oneunit(L), len) # Compute smallest-magnitude element to 2x precision ref_n = start_n+(imin-1)*step_n # this shouldn't overflow, so don't check nb = nbitslen(T, len, imin) steprangelen_hp(T, (ref_n, den), (step_n, den), nb, len, imin) end function (:)(start::T, step::T, stop::T) where T<:IEEEFloat step == 0 && throw(ArgumentError("range step cannot be zero")) # see if the inputs have exact rational approximations (and if so, # perform all computations in terms of the rationals) step_n, step_d = rat(step) if step_d != 0 && T(step_n/step_d) == step start_n, start_d = rat(start) stop_n, stop_d = rat(stop) if start_d != 0 && stop_d != 0 && T(start_n/start_d) == start && T(stop_n/stop_d) == stop den = lcm_unchecked(start_d, step_d) # use same denominator for start and step m = maxintfloat(T, Int) if den != 0 && abs(start*den) <= m && abs(step*den) <= m && # will round succeed? rem(den, start_d) == 0 && rem(den, step_d) == 0 # check lcm overflow start_n = round(Int, start*den) step_n = round(Int, step*den) len = max(0, Int(div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d))) # Integer ops could overflow, so check that this makes sense if isbetween(start, start + (len-1)*step, stop + step/2) && !isbetween(start, start + len*step, stop) # Return a 2x precision range return floatrange(T, start_n, step_n, len, den) end end end end # Fallback, taking start and step literally # n.b. we use Int as the default length type for IEEEFloats lf = (stop-start)/step if lf < 0 len = 0 elseif lf == 0 len = 1 else len = round(Int, lf) + 1 stopโ€ฒ = start + (len-1)*step # if we've overshot the end, subtract one: len -= (start < stop < stopโ€ฒ) + (start > stop > stopโ€ฒ) end steprangelen_hp(T, start, step, 0, len, 1) end step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T<:AbstractFloat} = T(r.step) step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T} = T(r.step) range_start_step_length(a::Real, st::IEEEFloat, len::Integer) = range_start_step_length(promote(a, st)..., len) range_start_step_length(a::IEEEFloat, st::Real, len::Integer) = range_start_step_length(promote(a, st)..., len) range_start_step_length(a::IEEEFloat, st::IEEEFloat, len::Integer) = range_start_step_length(promote(a, st)..., len) function range_start_step_length(a::T, st::T, len::Integer) where T<:IEEEFloat len = len + 0 # promote with Int start_n, start_d = rat(a) step_n, step_d = rat(st) if start_d != 0 && step_d != 0 && T(start_n/start_d) == a && T(step_n/step_d) == st den = lcm_unchecked(start_d, step_d) m = maxintfloat(T, Int) if abs(den*a) <= m && abs(den*st) <= m && rem(den, start_d) == 0 && rem(den, step_d) == 0 start_n = round(Int, den*a) step_n = round(Int, den*st) return floatrange(T, start_n, step_n, len, den) end end steprangelen_hp(T, a, st, 0, len, 1) end range_step_stop_length(step::Real, stop::IEEEFloat, len::Integer) = range_step_stop_length(promote(step, stop)..., len) range_step_stop_length(step::IEEEFloat, stop::Real, len::Integer) = range_step_stop_length(promote(step, stop)..., len) function range_step_stop_length(step::IEEEFloat, stop::IEEEFloat, len::Integer) r = range_start_step_length(stop, negate(step), len) reverse(r) end # This assumes that r.step has already been split so that (0:len-1)*r.step.hi is exact function unsafe_getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, i::Integer) where T # Very similar to _getindex_hiprec, but optimized to avoid a 2nd call to add12 @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) u = oftype(r.offset, i) - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo x_hi, x_lo = add12(r.ref.hi, shift_hi) T(x_hi + (x_lo + (shift_lo + r.ref.lo))) end function _getindex_hiprec(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Integer) i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) u = oftype(r.offset, i) - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo x_hi, x_lo = add12(r.ref.hi, shift_hi) x_hi, x_lo = add12(x_hi, x_lo + (shift_lo + r.ref.lo)) TwicePrecision(x_hi, x_lo) end function getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, s::OrdinalRange{S}) where {T, S<:Integer} @boundscheck checkbounds(r, s) len = length(s) L = typeof(len) sstep = step_hp(s) rstep = step_hp(r) if S === Bool #rstep *= one(sstep) if len == 0 return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) elseif len == 1 if first(s) return StepRangeLen{T}(first(r), rstep, oneunit(L), oneunit(L)) else return StepRangeLen{T}(first(r), rstep, zero(L), oneunit(L)) end else # len == 2 return StepRangeLen{T}(last(r), step(r), oneunit(L), oneunit(L)) end else soffset = round(L, (r.offset - first(s))/sstep + 1) soffset = clamp(soffset, oneunit(L), len) ioffset = L(first(s) + (soffset - oneunit(L)) * sstep) if sstep == 1 || len < 2 newstep = rstep #* one(sstep) else newstep = rstep * sstep newstep = twiceprecision(newstep, nbitslen(T, len, soffset)) end soffset = max(oneunit(L), soffset) if ioffset == r.offset return StepRangeLen{T}(r.ref, newstep, len, soffset) else return StepRangeLen{T}(r.ref + (ioffset-r.offset)*rstep, newstep, len, soffset) end end end *(x::Real, r::StepRangeLen{<:Real,<:TwicePrecision}) = StepRangeLen(x*r.ref, twiceprecision(x*r.step, nbitslen(r)), length(r), r.offset) *(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = x*r /(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = StepRangeLen(r.ref/x, twiceprecision(r.step/x, nbitslen(r)), length(r), r.offset) StepRangeLen{T,R,S,L}(r::StepRangeLen{T,R,S,L}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = r StepRangeLen{T,R,S,L}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision,L} = _convertSRL(StepRangeLen{T,R,S,L}, r) StepRangeLen{Float64}(r::StepRangeLen) = _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::StepRangeLen) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) StepRangeLen{Float64}(r::AbstractRange) = _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64},Int}, r) StepRangeLen{T}(r::AbstractRange) where {T<:IEEEFloat} = _convertSRL(StepRangeLen{T,Float64,Float64,Int}, r) function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{<:Integer}) where {T,R,S,L} StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{<:Integer}) where {T,R,S,L} StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end function _convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} # if start and step have a rational approximation in the old type, # then we transfer that rational approximation to the new type f, s = first(r), step(r) start_n, start_d = rat(f) step_n, step_d = rat(s) if start_d != 0 && step_d != 0 && U(start_n/start_d) == f && U(step_n/step_d) == s den = lcm_unchecked(start_d, step_d) m = maxintfloat(T, Int) if den != 0 && abs(f*den) <= m && abs(s*den) <= m && rem(den, start_d) == 0 && rem(den, step_d) == 0 start_n = round(Int, f*den) step_n = round(Int, s*den) return floatrange(T, start_n, step_n, L(length(r)), den) end end return __convertSRL(StepRangeLen{T,R,S,L}, r) end function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::StepRangeLen{U}) where {T,R,S,L,U} StepRangeLen{T,R,S,L}(R(r.ref), S(r.step), L(length(r)), L(r.offset)) end function __convertSRL(::Type{StepRangeLen{T,R,S,L}}, r::AbstractRange{U}) where {T,R,S,L,U} StepRangeLen{T,R,S,L}(R(first(r)), S(step(r)), L(length(r))) end function sum(r::StepRangeLen) l = length(r) # Compute the contribution of step over all indices. # Indexes on opposite side of r.offset contribute with opposite sign, # r.step * (sum(1:np) - sum(1:nn)) np, nn = l - r.offset, r.offset - 1 # positive, negative # To prevent overflow in sum(1:n), multiply its factors by the step sp, sn = sumpair(np), sumpair(nn) W = widen(typeof(l)) ฮ”n = W(sp[1]) * W(sp[2]) - W(sn[1]) * W(sn[2]) s = r.step * ฮ”n # Add in contributions of ref ref = r.ref * l convert(eltype(r), s + ref) end function sum(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}) l = length(r) # Compute the contribution of step over all indices. # Indexes on opposite side of r.offset contribute with opposite sign, # r.step * (sum(1:np) - sum(1:nn)) np, nn = l - r.offset, r.offset - 1 # positive, negative # To prevent overflow in sum(1:n), multiply its factors by the step sp, sn = sumpair(np), sumpair(nn) tp = _tp_prod(r.step, sp[1], sp[2]) tn = _tp_prod(r.step, sn[1], sn[2]) s_hi, s_lo = add12(tp.hi, -tn.hi) s_lo += tp.lo - tn.lo # Add in contributions of ref ref = r.ref * l sm_hi, sm_lo = add12(s_hi, ref.hi) add12(sm_hi, sm_lo + ref.lo)[1] end # sum(1:n) as a product of two integers sumpair(n::Integer) = iseven(n) ? (n+1, n>>1) : (n, (n+1)>>1) function +(r1::StepRangeLen{T,R}, r2::StepRangeLen{T,R}) where T where R<:TwicePrecision len = length(r1) (len == length(r2) || throw(DimensionMismatch("argument dimensions must match"))) if r1.offset == r2.offset imid = r1.offset ref = r1.ref + r2.ref else imid = round(typeof(len), (r1.offset+r2.offset)/2) ref1mid = _getindex_hiprec(r1, imid) ref2mid = _getindex_hiprec(r2, imid) ref = ref1mid + ref2mid end step = twiceprecision(r1.step + r2.step, nbitslen(T, len, imid)) StepRangeLen{T,typeof(ref),typeof(step),typeof(len)}(ref, step, len, imid) end ## LinRange # For Float16, Float32, and Float64, this returns a StepRangeLen function range_start_stop_length(start::T, stop::T, len::Integer) where {T<:IEEEFloat} len = len + 0 # promote with Int len < 2 && return _linspace1(T, start, stop, len) if start == stop return steprangelen_hp(T, start, zero(T), 0, len, 1) end # Attempt to find exact rational approximations start_n, start_d = rat(start) stop_n, stop_d = rat(stop) if start_d != 0 && stop_d != 0 den = lcm_unchecked(start_d, stop_d) m = maxintfloat(T, Int) if den != 0 && abs(den*start) <= m && abs(den*stop) <= m start_n = round(Int, den*start) stop_n = round(Int, den*stop) if T(start_n/den) == start && T(stop_n/den) == stop return _linspace(T, start_n, stop_n, len, den) end end end _linspace(start, stop, len) end function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} len = len + 0 # promote with Int (isfinite(start) && isfinite(stop)) || throw(ArgumentError("start and stop must be finite, got $start and $stop")) # Find the index that returns the smallest-magnitude element ฮ”, ฮ”fac = stop-start, 1 if !isfinite(ฮ”) # handle overflow for large endpoints ฮ”, ฮ”fac = stop/len - start/len, len end tmin = -(start/ฮ”)/ฮ”fac # t such that (1-t)*start + t*stop == 0 L = typeof(len) lenn1 = len - oneunit(L) imin = round(L, tmin*lenn1 + 1) # index approximately corresponding to t if 1 < imin < len # The smallest-magnitude element is in the interior t = (imin - 1)/lenn1 ref = T((1-t)*start + t*stop) step = imin-1 < len-imin ? (ref-start)/(imin-1) : (stop-ref)/(len-imin) elseif imin <= 1 imin = oneunit(L) ref = start step = (ฮ”/(lenn1))*ฮ”fac else imin = len ref = stop step = (ฮ”/(lenn1))*ฮ”fac end if len == 2 && !isfinite(step) # For very large endpoints where step overflows, exploit the # split-representation to handle the overflow return steprangelen_hp(T, start, (-start, stop), 0, len, oneunit(L)) end # 2x calculations to get high precision endpoint matching while also # preventing overflow in ref_hi+(i-offset)*step_hi m, k = prevfloat(floatmax(T)), max(imin-1, len-imin) step_hi_pre = clamp(step, max(-(m+ref)/k, (-m+ref)/k), min((m-ref)/k, (m+ref)/k)) nb = nbitslen(T, len, imin) step_hi = truncbits(step_hi_pre, nb) x1_hi, x1_lo = add12((1-imin)*step_hi, ref) x2_hi, x2_lo = add12((len-imin)*step_hi, ref) a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo step_lo = (b - a)/(len - 1) ref_lo = a - (1 - imin)*step_lo steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, len, imin) end # range for rational numbers, start = start_n/den, stop = stop_n/den # Note this returns a StepRangeLen _linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where {T<:IEEEFloat} = _linspace(T, start, stop, len, one(start)) function _linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T<:IEEEFloat len = len + 0 # promote with Int len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) L = typeof(len) start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len, oneunit(L)) tmin = -start_n/(Float64(stop_n) - Float64(start_n)) imin = round(typeof(len), tmin*(len-1)+1) imin = clamp(imin, oneunit(L), len) W = widen(L) start_n = W(start_n) stop_n = W(stop_n) ref_num = W(len-imin) * start_n + W(imin-1) * stop_n ref_denom = W(len-1) * den ref = (ref_num, ref_denom) step_full = (stop_n - start_n, ref_denom) steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), len, imin) end # For len < 2 function _linspace1(::Type{T}, start, stop, len::Integer) where T<:IEEEFloat len >= 0 || throw(ArgumentError("range($start, stop=$stop, length=$len): negative length")) if len <= 1 len == 1 && (start == stop || throw(ArgumentError("range($start, stop=$stop, length=$len): endpoints differ"))) # Ensure that first(r)==start and last(r)==stop even for len==0 # The output type must be consistent with steprangelen_hp if T<:Union{Float32,Float16} return StepRangeLen{T}(Float64(start), Float64(start) - Float64(stop), len, 1) else # T == Float64 return StepRangeLen(TwicePrecision(start, zero(T)), TwicePrecision(start, -stop), len, 1) end end throw(ArgumentError("should only be called for len < 2, got $len")) end ### Numeric utilities # Approximate x with a rational representation as a pair of Int values. # Guaranteed to return, but not guaranteed to return a precise answer. # https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations function rat(x) y = x a = d = 1 b = c = 0 m = maxintfloat(narrow(typeof(x)), Int) while abs(y) <= m f = trunc(Int, y) y -= f a, c = f*a + c, a b, d = f*b + d, b max(abs(a), abs(b)) <= convert(Int,m) || return c, d oftype(x,a)/oftype(x,b) == x && break y = inv(y) end return a, b end # This version of lcm does not check for overflows lcm_unchecked(a::T, b::T) where T<:Integer = a * div(b, gcd(a, b)) narrow(::Type{T}) where {T<:AbstractFloat} = Float64 narrow(::Type{Float64}) = Float32 narrow(::Type{Float32}) = Float16 narrow(::Type{Float16}) = Float16 function _tp_prod(t::TwicePrecision, x, y...) @inline _tp_prod(t * x, y...) end _tp_prod(t::TwicePrecision) = t <(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} = x.hi < y.hi || ((x.hi == y.hi) & (x.lo < y.lo)) isbetween(a, x, b) = a <= x <= b || b <= x <= a N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/complex.jlง{# This file is a part of Julia. License is MIT: https://julialang.org/license """ Complex{T<:Real} <: Number Complex number type with real and imaginary part of type `T`. `ComplexF16`, `ComplexF32` and `ComplexF64` are aliases for `Complex{Float16}`, `Complex{Float32}` and `Complex{Float64}` respectively. See also: [`Real`](@ref), [`complex`](@ref), [`real`](@ref). """ struct Complex{T<:Real} <: Number re::T im::T end Complex(x::Real, y::Real) = Complex(promote(x,y)...) Complex(x::Real) = Complex(x, zero(x)) """ im The imaginary unit. See also: [`imag`](@ref), [`angle`](@ref), [`complex`](@ref). # Examples ```jldoctest julia> im * im -1 + 0im julia> (2.0 + 3im)^2 -5.0 + 12.0im ``` """ const im = Complex(false, true) const ComplexF64 = Complex{Float64} const ComplexF32 = Complex{Float32} const ComplexF16 = Complex{Float16} Complex{T}(x::Real) where {T<:Real} = Complex{T}(x,0) Complex{T}(z::Complex) where {T<:Real} = Complex{T}(real(z),imag(z)) (::Type{T})(z::Complex) where {T<:Real} = isreal(z) ? T(real(z))::T : throw(InexactError(nameof(T), T, z)) Complex(z::Complex) = z promote_rule(::Type{Complex{T}}, ::Type{S}) where {T<:Real,S<:Real} = Complex{promote_type(T,S)} promote_rule(::Type{Complex{T}}, ::Type{Complex{S}}) where {T<:Real,S<:Real} = Complex{promote_type(T,S)} widen(::Type{Complex{T}}) where {T} = Complex{widen(T)} float(::Type{Complex{T}}) where {T<:AbstractFloat} = Complex{T} float(::Type{Complex{T}}) where {T} = Complex{float(T)} """ real(z) Return the real part of the complex number `z`. See also: [`imag`](@ref), [`reim`](@ref), [`complex`](@ref), [`isreal`](@ref), [`Real`](@ref). # Examples ```jldoctest julia> real(1 + 3im) 1 ``` """ real(z::Complex) = z.re """ imag(z) Return the imaginary part of the complex number `z`. See also: [`conj`](@ref), [`reim`](@ref), [`adjoint`](@ref), [`angle`](@ref). # Examples ```jldoctest julia> imag(1 + 3im) 3 ``` """ imag(z::Complex) = z.im real(x::Real) = x imag(x::Real) = zero(x) """ reim(z) Return a tuple of the real and imaginary parts of the complex number `z`. # Examples ```jldoctest julia> reim(1 + 3im) (1, 3) ``` """ reim(z) = (real(z), imag(z)) """ real(T::Type) Return the type that represents the real part of a value of type `T`. e.g: for `T == Complex{R}`, returns `R`. Equivalent to `typeof(real(zero(T)))`. # Examples ```jldoctest julia> real(Complex{Int}) Int64 julia> real(Float64) Float64 ``` """ real(T::Type) = typeof(real(zero(T))) real(::Type{T}) where {T<:Real} = T real(C::Type{<:Complex}) = fieldtype(C, 1) real(::Type{Union{}}, slurp...) = Union{}(im) """ isreal(x) -> Bool Test whether `x` or all its elements are numerically equal to some real number including infinities and NaNs. `isreal(x)` is true if `isequal(x, real(x))` is true. # Examples ```jldoctest julia> isreal(5.) true julia> isreal(1 - 3im) false julia> isreal(Inf + 0im) true julia> isreal([4.; complex(0,1)]) false ``` """ isreal(x::Real) = true isreal(z::Complex) = iszero(imag(z)) isinteger(z::Complex) = isreal(z) & isinteger(real(z)) isfinite(z::Complex) = isfinite(real(z)) & isfinite(imag(z)) isnan(z::Complex) = isnan(real(z)) | isnan(imag(z)) isinf(z::Complex) = isinf(real(z)) | isinf(imag(z)) iszero(z::Complex) = iszero(real(z)) & iszero(imag(z)) isone(z::Complex) = isone(real(z)) & iszero(imag(z)) """ complex(r, [i]) Convert real numbers or arrays to complex. `i` defaults to zero. # Examples ```jldoctest julia> complex(7) 7 + 0im julia> complex([1, 2, 3]) 3-element Vector{Complex{Int64}}: 1 + 0im 2 + 0im 3 + 0im ``` """ complex(z::Complex) = z complex(x::Real) = Complex(x) complex(x::Real, y::Real) = Complex(x, y) """ complex(T::Type) Return an appropriate type which can represent a value of type `T` as a complex number. Equivalent to `typeof(complex(zero(T)))`. # Examples ```jldoctest julia> complex(Complex{Int}) Complex{Int64} julia> complex(Int) Complex{Int64} ``` """ complex(::Type{T}) where {T<:Real} = Complex{T} complex(::Type{Complex{T}}) where {T<:Real} = Complex{T} flipsign(x::Complex, y::Real) = ifelse(signbit(y), -x, x) function show(io::IO, z::Complex) r, i = reim(z) compact = get(io, :compact, false)::Bool show(io, r) if signbit(i) && !isnan(i) print(io, compact ? "-" : " - ") if isa(i,Signed) && !isa(i,BigInt) && i == typemin(typeof(i)) show(io, -widen(i)) else show(io, -i) end else print(io, compact ? "+" : " + ") show(io, i) end if !(isa(i,Integer) && !isa(i,Bool) || isa(i,AbstractFloat) && isfinite(i)) print(io, "*") end print(io, "im") end show(io::IO, z::Complex{Bool}) = print(io, z == im ? "im" : "Complex($(z.re),$(z.im))") function show_unquoted(io::IO, z::Complex, ::Int, prec::Int) if operator_precedence(:+) <= prec print(io, "(") show(io, z) print(io, ")") else show(io, z) end end function read(s::IO, ::Type{Complex{T}}) where T<:Real r = read(s,T) i = read(s,T) Complex{T}(r,i) end function write(s::IO, z::Complex) write(s,real(z),imag(z)) end ## byte order swaps: real and imaginary part are swapped individually bswap(z::Complex) = Complex(bswap(real(z)), bswap(imag(z))) ## equality and hashing of complex numbers ## ==(z::Complex, w::Complex) = (real(z) == real(w)) & (imag(z) == imag(w)) ==(z::Complex, x::Real) = isreal(z) && real(z) == x ==(x::Real, z::Complex) = isreal(z) && real(z) == x isequal(z::Complex, w::Complex) = isequal(real(z),real(w))::Bool & isequal(imag(z),imag(w))::Bool isequal(z::Complex, w::Real) = isequal(real(z),w)::Bool & isequal(imag(z),zero(w))::Bool isequal(z::Real, w::Complex) = isequal(z,real(w))::Bool & isequal(zero(z),imag(w))::Bool in(x::Complex, r::AbstractRange{<:Real}) = isreal(x) && real(x) in r if UInt === UInt64 const h_imag = 0x32a7a07f3e7cd1f9 else const h_imag = 0x3e7cd1f9 end const hash_0_imag = hash(0, h_imag) function hash(z::Complex, h::UInt) # TODO: with default argument specialization, this would be better: # hash(real(z), h โŠป hash(imag(z), h โŠป h_imag) โŠป hash(0, h โŠป h_imag)) hash(real(z), h โŠป hash(imag(z), h_imag) โŠป hash_0_imag) end ## generic functions of complex numbers ## """ conj(z) Compute the complex conjugate of a complex number `z`. See also: [`angle`](@ref), [`adjoint`](@ref). # Examples ```jldoctest julia> conj(1 + 3im) 1 - 3im ``` """ conj(z::Complex) = Complex(real(z),-imag(z)) abs(z::Complex) = hypot(real(z), imag(z)) abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z) function inv(z::Complex) c, d = reim(z) (isinf(c) | isinf(d)) && return complex(copysign(zero(c), c), flipsign(-zero(d), d)) complex(c, -d)/(c * c + d * d) end inv(z::Complex{<:Integer}) = inv(float(z)) +(z::Complex) = Complex(+real(z), +imag(z)) -(z::Complex) = Complex(-real(z), -imag(z)) +(z::Complex, w::Complex) = Complex(real(z) + real(w), imag(z) + imag(w)) -(z::Complex, w::Complex) = Complex(real(z) - real(w), imag(z) - imag(w)) *(z::Complex, w::Complex) = Complex(real(z) * real(w) - imag(z) * imag(w), real(z) * imag(w) + imag(z) * real(w)) muladd(z::Complex, w::Complex, x::Complex) = Complex(muladd(real(z), real(w), -muladd(imag(z), imag(w), -real(x))), muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x)))) # handle Bool and Complex{Bool} # avoid type signature ambiguity warnings +(x::Bool, z::Complex{Bool}) = Complex(x + real(z), imag(z)) +(z::Complex{Bool}, x::Bool) = Complex(real(z) + x, imag(z)) -(x::Bool, z::Complex{Bool}) = Complex(x - real(z), - imag(z)) -(z::Complex{Bool}, x::Bool) = Complex(real(z) - x, imag(z)) *(x::Bool, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) *(z::Complex{Bool}, x::Bool) = Complex(real(z) * x, imag(z) * x) +(x::Bool, z::Complex) = Complex(x + real(z), imag(z)) +(z::Complex, x::Bool) = Complex(real(z) + x, imag(z)) -(x::Bool, z::Complex) = Complex(x - real(z), - imag(z)) -(z::Complex, x::Bool) = Complex(real(z) - x, imag(z)) *(x::Bool, z::Complex) = Complex(x * real(z), x * imag(z)) *(z::Complex, x::Bool) = Complex(real(z) * x, imag(z) * x) +(x::Real, z::Complex{Bool}) = Complex(x + real(z), imag(z)) +(z::Complex{Bool}, x::Real) = Complex(real(z) + x, imag(z)) function -(x::Real, z::Complex{Bool}) # we don't want the default type for -(Bool) re = x-real(z) Complex(re, - oftype(re, imag(z))) end -(z::Complex{Bool}, x::Real) = Complex(real(z) - x, imag(z)) *(x::Real, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) *(z::Complex{Bool}, x::Real) = Complex(real(z) * x, imag(z) * x) # adding or multiplying real & complex is common +(x::Real, z::Complex) = Complex(x + real(z), imag(z)) +(z::Complex, x::Real) = Complex(x + real(z), imag(z)) function -(x::Real, z::Complex) # we don't want the default type for -(Bool) re = x - real(z) Complex(re, - oftype(re, imag(z))) end -(z::Complex, x::Real) = Complex(real(z) - x, imag(z)) *(x::Real, z::Complex) = Complex(x * real(z), x * imag(z)) *(z::Complex, x::Real) = Complex(x * real(z), x * imag(z)) muladd(x::Real, z::Complex, y::Number) = muladd(z, x, y) muladd(z::Complex, x::Real, y::Real) = Complex(muladd(real(z),x,y), imag(z)*x) muladd(z::Complex, x::Real, w::Complex) = Complex(muladd(real(z),x,real(w)), muladd(imag(z),x,imag(w))) muladd(x::Real, y::Real, z::Complex) = Complex(muladd(x,y,real(z)), imag(z)) muladd(z::Complex, w::Complex, x::Real) = Complex(muladd(real(z), real(w), -muladd(imag(z), imag(w), -x)), muladd(real(z), imag(w), imag(z) * real(w))) /(a::R, z::S) where {R<:Real,S<:Complex} = (T = promote_type(R,S); a*inv(T(z))) /(z::Complex, x::Real) = Complex(real(z)/x, imag(z)/x) function /(a::Complex{T}, b::Complex{T}) where T<:Real are = real(a); aim = imag(a); bre = real(b); bim = imag(b) if (isinf(bre) | isinf(bim)) if isfinite(a) return complex(zero(T)*sign(are)*sign(bre), -zero(T)*sign(aim)*sign(bim)) end return T(NaN)+T(NaN)*im end if abs(bre) <= abs(bim) r = bre / bim den = bim + r*bre Complex((are*r + aim)/den, (aim*r - are)/den) else r = bim / bre den = bre + r*bim Complex((are + aim*r)/den, (aim - are*r)/den) end end function /(z::Complex{T}, w::Complex{T}) where {T<:Union{Float16,Float32}} c, d = reim(widen(w)) a, b = reim(widen(z)) if (isinf(c) | isinf(d)) if isfinite(z) return complex(zero(T)*sign(real(z))*sign(real(w)), -zero(T)*sign(imag(z))*sign(imag(w))) end return T(NaN)+T(NaN)*im end mag = inv(muladd(c, c, d^2)) re_part = muladd(a, c, b*d) im_part = muladd(b, c, -a*d) return oftype(z, Complex(re_part*mag, im_part*mag)) end # robust complex division for double precision # variables are scaled & unscaled to avoid over/underflow, if necessary # based on arxiv.1210.4539 # a + i*b # p + i*q = --------- # c + i*d function /(z::ComplexF64, w::ComplexF64) a, b = reim(z); c, d = reim(w) absa = abs(a); absb = abs(b); ab = absa >= absb ? absa : absb # equiv. to max(abs(a),abs(b)) but without NaN-handling (faster) absc = abs(c); absd = abs(d); cd = absc >= absd ? absc : absd if (isinf(c) | isinf(d)) if isfinite(z) return complex(0.0*sign(a)*sign(c), -0.0*sign(b)*sign(d)) end return NaN+NaN*im end halfov = 0.5*floatmax(Float64) # overflow threshold twounฯต = floatmin(Float64)*2.0/eps(Float64) # underflow threshold # actual division operations if ab>=halfov || ab<=twounฯต || cd>=halfov || cd<=twounฯต # over/underflow case p,q = scaling_cdiv(a,b,c,d,ab,cd) # scales a,b,c,d before division (unscales after) else p,q = cdiv(a,b,c,d) end return ComplexF64(p,q) end # sub-functionality for /(z::ComplexF64, w::ComplexF64) @inline function cdiv(a::Float64, b::Float64, c::Float64, d::Float64) if abs(d)<=abs(c) p,q = robust_cdiv1(a,b,c,d) else p,q = robust_cdiv1(b,a,d,c) q = -q end return p,q end @noinline function scaling_cdiv(a::Float64, b::Float64, c::Float64, d::Float64, ab::Float64, cd::Float64) # this over/underflow functionality is outlined for performance, cf. #29688 a,b,c,d,s = scaleargs_cdiv(a,b,c,d,ab,cd) p,q = cdiv(a,b,c,d) return p*s,q*s end function scaleargs_cdiv(a::Float64, b::Float64, c::Float64, d::Float64, ab::Float64, cd::Float64) ฯต = eps(Float64) halfov = 0.5*floatmax(Float64) twounฯต = floatmin(Float64)*2.0/ฯต bs = 2.0/(ฯต*ฯต) # scaling s = 1.0 if ab >= halfov a*=0.5; b*=0.5; s*=2.0 # scale down a,b elseif ab <= twounฯต a*=bs; b*=bs; s/=bs # scale up a,b end if cd >= halfov c*=0.5; d*=0.5; s*=0.5 # scale down c,d elseif cd <= twounฯต c*=bs; d*=bs; s*=bs # scale up c,d end return a,b,c,d,s end @inline function robust_cdiv1(a::Float64, b::Float64, c::Float64, d::Float64) r = d/c t = 1.0/(c+d*r) p = robust_cdiv2(a,b,c,d,r,t) q = robust_cdiv2(b,-a,c,d,r,t) return p,q end function robust_cdiv2(a::Float64, b::Float64, c::Float64, d::Float64, r::Float64, t::Float64) if r != 0 br = b*r return (br != 0 ? (a+br)*t : a*t + (b*t)*r) else return (a + d*(b/c)) * t end end function inv(z::Complex{T}) where T<:Union{Float16,Float32} c, d = reim(widen(z)) (isinf(c) | isinf(d)) && return complex(copysign(zero(T), c), flipsign(-zero(T), d)) mag = inv(muladd(c, c, d^2)) return oftype(z, Complex(c*mag, -d*mag)) end function inv(w::ComplexF64) c, d = reim(w) absc, absd = abs(c), abs(d) cd, dc = ifelse(absc>absd, (absc, absd), (absd, absc)) # no overflow from abs2 if sqrt(floatmin(Float64)/2) <= cd <= sqrt(floatmax(Float64)/2) return conj(w) / muladd(cd, cd, dc*dc) end (isinf(c) | isinf(d)) && return complex(copysign(0.0, c), flipsign(-0.0, d)) ฯต = eps(Float64) bs = 2/(ฯต*ฯต) # scaling s = 1.0 if cd >= floatmax(Float64)/2 c *= 0.5; d *= 0.5; s = 0.5 # scale down c, d elseif cd <= 2floatmin(Float64)/ฯต c *= bs; d *= bs; s = bs # scale up c, d end # inversion operations if absd <= absc p, q = robust_cinv(c, d) else q, p = robust_cinv(-d, -c) end return ComplexF64(p*s, q*s) end function robust_cinv(c::Float64, d::Float64) r = d/c z = muladd(d, r, c) p = 1.0/z q = -r/z return p, q end function ssqs(x::T, y::T) where T<:Real k::Int = 0 ฯ = x*x + y*y if !isfinite(ฯ) && (isinf(x) || isinf(y)) ฯ = convert(T, Inf) elseif isinf(ฯ) || (ฯ==0 && (x!=0 || y!=0)) || ฯ= 0 # return Complex(r, iz/r/2) # end # return Complex(abs(iz)/r/2, copysign(r,iz)) # end """ cis(x) More efficient method for `exp(im*x)` by using Euler's formula: ``cos(x) + i sin(x) = \\exp(i x)``. See also [`cispi`](@ref), [`sincos`](@ref), [`exp`](@ref), [`angle`](@ref). # Examples ```jldoctest julia> cis(ฯ€) โ‰ˆ -1 true ``` """ function cis end function cis(theta::Real) s, c = sincos(theta) Complex(c, s) end function cis(z::Complex) v = exp(-imag(z)) s, c = sincos(real(z)) Complex(v * c, v * s) end """ cispi(x) More accurate method for `cis(pi*x)` (especially for large `x`). See also [`cis`](@ref), [`sincospi`](@ref), [`exp`](@ref), [`angle`](@ref). # Examples ```jldoctest julia> cispi(10000) 1.0 + 0.0im julia> cispi(0.25 + 1im) 0.030556854645954562 + 0.03055685464595456im ``` !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ function cispi end cispi(theta::Real) = Complex(reverse(sincospi(theta))...) function cispi(z::Complex) v = exp(-(pi*imag(z))) s, c = sincospi(real(z)) Complex(v * c, v * s) end """ angle(z) Compute the phase angle in radians of a complex number `z`. See also: [`atan`](@ref), [`cis`](@ref). # Examples ```jldoctest julia> rad2deg(angle(1 + im)) 45.0 julia> rad2deg(angle(1 - im)) -45.0 julia> rad2deg(angle(-1 - im)) -135.0 ``` """ angle(z::Complex) = atan(imag(z), real(z)) function log(z::Complex) z = float(z) T = typeof(real(z)) T1 = convert(T,5)/convert(T,4) T2 = convert(T,3) ln2 = log(convert(T,2)) #0.6931471805599453 x, y = reim(z) ฯ, k = ssqs(x,y) ax = abs(x) ay = abs(y) if ax < ay ฮธ, ฮฒ = ax, ay else ฮธ, ฮฒ = ay, ax end if k==0 && (0.5 < ฮฒ*ฮฒ) && (ฮฒ <= T1 || ฯ < T2) ฯฯ = log1p((ฮฒ-1)*(ฮฒ+1)+ฮธ*ฮธ)/2 else ฯฯ = log(ฯ)/2 + k*ln2 end Complex(ฯฯ, angle(z)) end # function log(z::Complex) # ar = abs(real(z)) # ai = abs(imag(z)) # if ar < ai # r = ar/ai # re = log(ai) + log1p(r*r)/2 # else # if ar == 0 # re = isnan(ai) ? ai : -inv(ar) # elseif isinf(ai) # re = oftype(ar,Inf) # else # r = ai/ar # re = log(ar) + log1p(r*r)/2 # end # end # Complex(re, angle(z)) # end function log10(z::Complex) a = log(z) a/log(oftype(real(a),10)) end function log2(z::Complex) a = log(z) a/log(oftype(real(a),2)) end function exp(z::Complex) zr, zi = reim(z) if isnan(zr) Complex(zr, zi==0 ? zi : zr) elseif !isfinite(zi) if zr == Inf Complex(-zr, oftype(zr,NaN)) elseif zr == -Inf Complex(-zero(zr), copysign(zero(zi), zi)) else Complex(oftype(zr,NaN), oftype(zi,NaN)) end else er = exp(zr) if iszero(zi) Complex(er, zi) else s, c = sincos(zi) Complex(er * c, er * s) end end end function expm1(z::Complex{T}) where T<:Real Tf = float(T) zr,zi = reim(z) if isnan(zr) Complex(zr, zi==0 ? zi : zr) elseif !isfinite(zi) if zr == Inf Complex(-zr, oftype(zr,NaN)) elseif zr == -Inf Complex(-one(zr), copysign(zero(zi), zi)) else Complex(oftype(zr,NaN), oftype(zi,NaN)) end else erm1 = expm1(zr) if zi == 0 Complex(erm1, zi) else er = erm1+one(erm1) if isfinite(er) wr = erm1 - 2 * er * (sin(convert(Tf, 0.5) * zi))^2 return Complex(wr, er * sin(zi)) else s, c = sincos(zi) return Complex(er * c, er * s) end end end end function log1p(z::Complex{T}) where T zr,zi = reim(z) if isfinite(zr) isinf(zi) && return log(z) # This is based on a well-known trick for log1p of real z, # allegedly due to Kahan, only modified to handle real(u) <= 0 # differently to avoid inaccuracy near z==-2 and for correct branch cut u = one(float(T)) + z u == 1 ? convert(typeof(u), z) : real(u) <= 0 ? log(u) : log(u)*z/(u-1) elseif isnan(zr) Complex(zr, zr) elseif isfinite(zi) Complex(T(Inf), copysign(zr > 0 ? zero(T) : convert(T, pi), zi)) else Complex(T(Inf), T(NaN)) end end function exp2(z::Complex{T}) where T z = float(z) er = exp2(real(z)) theta = imag(z) * log(convert(float(T), 2)) s, c = sincos(theta) Complex(er * c, er * s) end function exp10(z::Complex{T}) where T z = float(z) er = exp10(real(z)) theta = imag(z) * log(convert(float(T), 10)) s, c = sincos(theta) Complex(er * c, er * s) end # _cpow helper function to avoid method ambiguity with ^(::Complex,::Real) function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where T z = float(z) p = float(p) Tf = float(T) if isreal(p) pแตฃ = real(p) if isinteger(pแตฃ) && abs(pแตฃ) < typemax(Int32) # |p| < typemax(Int32) serves two purposes: it prevents overflow # when converting p to Int, and it also turns out to be roughly # the crossover point for exp(p*log(z)) or similar to be faster. if iszero(pแตฃ) # fix signs of imaginary part for z^0 zer = flipsign(copysign(zero(Tf),pแตฃ), imag(z)) return Complex(one(Tf), zer) end ip = convert(Int, pแตฃ) if isreal(z) zแตฃ = real(z) if ip < 0 iszero(z) && return Complex(Tf(NaN),Tf(NaN)) re = power_by_squaring(inv(zแตฃ), -ip) im = -imag(z) else re = power_by_squaring(zแตฃ, ip) im = imag(z) end # slightly tricky to get the correct sign of zero imag. part return Complex(re, ifelse(iseven(ip) & signbit(zแตฃ), -im, im)) else return ip < 0 ? power_by_squaring(inv(z), -ip) : power_by_squaring(z, ip) end elseif isreal(z) # (note: if both z and p are complex with ยฑ0.0 imaginary parts, # the sign of the ยฑ0.0 imaginary part of the result is ambiguous) if iszero(real(z)) return pแตฃ > 0 ? complex(z) : Complex(Tf(NaN),Tf(NaN)) # 0 or NaN+NaN*im elseif real(z) > 0 return Complex(real(z)^pแตฃ, z isa Real ? ifelse(real(z) < 1, -imag(p), imag(p)) : flipsign(imag(z), pแตฃ)) else zแตฃ = real(z) rแต– = (-zแตฃ)^pแตฃ if isfinite(pแตฃ) # figuring out the sign of 0.0 when p is a complex number # with zero imaginary part and integer/2 real part could be # improved here, but it's not clear if it's worth itโ€ฆ return rแต– * complex(cospi(pแตฃ), flipsign(sinpi(pแตฃ),imag(z))) else iszero(rแต–) && return zero(Complex{Tf}) # no way to get correct signs of 0.0 return Complex(Tf(NaN),Tf(NaN)) # non-finite phase angle or NaN input end end else rแต– = abs(z)^pแตฃ ฯ• = pแตฃ*angle(z) end elseif isreal(z) iszero(z) && return real(p) > 0 ? complex(z) : Complex(Tf(NaN),Tf(NaN)) # 0 or NaN+NaN*im zแตฃ = real(z) pแตฃ, pแตข = reim(p) if zแตฃ > 0 rแต– = zแตฃ^pแตฃ ฯ• = pแตข*log(zแตฃ) else r = -zแตฃ ฮธ = copysign(Tf(ฯ€),imag(z)) rแต– = r^pแตฃ * exp(-pแตข*ฮธ) ฯ• = pแตฃ*ฮธ + pแตข*log(r) end else pแตฃ, pแตข = reim(p) r = abs(z) ฮธ = angle(z) rแต– = r^pแตฃ * exp(-pแตข*ฮธ) ฯ• = pแตฃ*ฮธ + pแตข*log(r) end if isfinite(ฯ•) return rแต– * cis(ฯ•) else iszero(rแต–) && return zero(Complex{Tf}) # no way to get correct signs of 0.0 return Complex(Tf(NaN),Tf(NaN)) # non-finite phase angle or NaN input end end ^(z::Complex{T}, p::Complex{T}) where T<:Real = _cpow(z, p) ^(z::Complex{T}, p::T) where T<:Real = _cpow(z, p) ^(z::T, p::Complex{T}) where T<:Real = _cpow(z, p) ^(z::Complex, n::Bool) = n ? z : one(z) ^(z::Complex, n::Integer) = z^Complex(n) ^(z::Complex{<:AbstractFloat}, n::Bool) = n ? z : one(z) # to resolve ambiguity ^(z::Complex{<:Integer}, n::Bool) = n ? z : one(z) # to resolve ambiguity ^(z::Complex{<:AbstractFloat}, n::Integer) = n>=0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n) ^(z::Complex{<:Integer}, n::Integer) = power_by_squaring(z,n) # DomainError for n<0 function ^(z::Complex{T}, p::S) where {T<:Real,S<:Real} P = promote_type(T,S) return Complex{P}(z) ^ P(p) end function ^(z::T, p::Complex{S}) where {T<:Real,S<:Real} P = promote_type(T,S) return P(z) ^ Complex{P}(p) end function sin(z::Complex{T}) where T F = float(T) zr, zi = reim(z) if zr == 0 Complex(F(zr), sinh(zi)) elseif !isfinite(zr) if zi == 0 || isinf(zi) Complex(F(NaN), F(zi)) else Complex(F(NaN), F(NaN)) end else s, c = sincos(zr) Complex(s * cosh(zi), c * sinh(zi)) end end function cos(z::Complex{T}) where T F = float(T) zr, zi = reim(z) if zr == 0 Complex(cosh(zi), isnan(zi) ? F(zr) : -flipsign(F(zr),zi)) elseif !isfinite(zr) if zi == 0 Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) elseif isinf(zi) Complex(F(Inf), F(NaN)) else Complex(F(NaN), F(NaN)) end else s, c = sincos(zr) Complex(c * cosh(zi), -s * sinh(zi)) end end function tan(z::Complex) zr, zi = reim(z) w = tanh(Complex(-zi, zr)) Complex(imag(w), -real(w)) end function asin(z::Complex) zr, zi = reim(z) if isinf(zr) && isinf(zi) return Complex(copysign(oftype(zr,pi)/4, zr),zi) elseif isnan(zi) && isinf(zr) return Complex(zi, oftype(zr, Inf)) end ฮพ = zr == 0 ? zr : !isfinite(zr) ? oftype(zr,pi)/2 * sign(zr) : atan(zr, real(sqrt(1-z)*sqrt(1+z))) ฮท = asinh(copysign(imag(sqrt(conj(1-z))*sqrt(1+z)), imag(z))) Complex(ฮพ,ฮท) end function acos(z::Complex) z = float(z) zr, zi = reim(z) if isnan(zr) if isinf(zi) return Complex(zr, -zi) else return Complex(zr, zr) end elseif isnan(zi) if isinf(zr) return Complex(zi, abs(zr)) elseif zr==0 return Complex(oftype(zr,pi)/2, zi) else return Complex(zi, zi) end elseif zr==zi==0 return Complex(oftype(zr,pi)/2, -zi) elseif zr==Inf && zi===0.0 return Complex(zi, -zr) elseif zr==-Inf && zi===-0.0 return Complex(oftype(zi,pi), -zr) end ฮพ = 2*atan(real(sqrt(1-z)), real(sqrt(1+z))) ฮท = asinh(imag(sqrt(conj(1+z))*sqrt(1-z))) if isinf(zr) && isinf(zi) ฮพ -= oftype(ฮท,pi)/4 * sign(zr) end Complex(ฮพ,ฮท) end function atan(z::Complex) w = atanh(Complex(-imag(z),real(z))) Complex(imag(w),-real(w)) end function sinh(z::Complex) zr, zi = reim(z) w = sin(Complex(zi, zr)) Complex(imag(w),real(w)) end function cosh(z::Complex) zr, zi = reim(z) cos(Complex(zi,-zr)) end function tanh(z::Complex{T}) where T z = float(z) Tf = float(T) ฮฉ = prevfloat(typemax(Tf)) ฮพ, ฮท = reim(z) if isnan(ฮพ) && ฮท==0 return Complex(ฮพ, ฮท) end if 4*abs(ฮพ) > asinh(ฮฉ) #Overflow? Complex(copysign(one(Tf),ฮพ), copysign(zero(Tf),ฮท*(isfinite(ฮท) ? sin(2*abs(ฮท)) : one(ฮท)))) else t = tan(ฮท) ฮฒ = 1+t*t #sec(ฮท)^2 s = sinh(ฮพ) ฯ = sqrt(1 + s*s) #cosh(ฮพ) if isinf(t) Complex(ฯ/s,1/t) else Complex(ฮฒ*ฯ*s,t)/(1+ฮฒ*s*s) end end end function asinh(z::Complex) w = asin(Complex(-imag(z),real(z))) Complex(imag(w),-real(w)) end function acosh(z::Complex) zr, zi = reim(z) if isnan(zr) || isnan(zi) if isinf(zr) || isinf(zi) return Complex(oftype(zr, Inf), oftype(zi, NaN)) else return Complex(oftype(zr, NaN), oftype(zi, NaN)) end elseif zr==-Inf && zi===-0.0 #Edge case is wrong - WHY? return Complex(oftype(zr,Inf), oftype(zi, -pi)) end ฮพ = asinh(real(sqrt(conj(z-1))*sqrt(z+1))) ฮท = 2*atan(imag(sqrt(z-1)),real(sqrt(z+1))) if isinf(zr) && isinf(zi) ฮท -= oftype(ฮท,pi)/4 * sign(zi) * sign(zr) end Complex(ฮพ, ฮท) end function atanh(z::Complex{T}) where T z = float(z) Tf = float(T) ฮฉ = prevfloat(typemax(Tf)) ฮธ = sqrt(ฮฉ)/4 ฯ = 1/ฮธ x, y = reim(z) ax = abs(x) ay = abs(y) if ax > ฮธ || ay > ฮธ #Prevent overflow if isnan(y) if isinf(x) return Complex(copysign(zero(x),x), y) else return Complex(real(1/z), y) end end if isinf(y) return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y)) end return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) end ฮฒ = copysign(one(Tf), x) z *= ฮฒ x, y = reim(z) if x == 1 if y == 0 ฮพ = oftype(x, Inf) ฮท = y else ym = ay+ฯ ฮพ = log(sqrt(sqrt(4+y*y))/sqrt(ym)) ฮท = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2 end else #Normal case ysq = (ay+ฯ)^2 if x == 0 ฮพ = x else ฮพ = log1p(4x/((1-x)^2 + ysq))/4 end ฮท = angle(Complex((1-x)*(1+x)-ysq, 2y))/2 end ฮฒ * Complex(ฮพ, ฮท) end #Rounding complex numbers #Requires two different RoundingModes for the real and imaginary components """ round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]) round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; digits=0, base=10) round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; sigdigits, base=10) Return the nearest integral value of the same type as the complex-valued `z` to `z`, breaking ties using the specified [`RoundingMode`](@ref)s. The first [`RoundingMode`](@ref) is used for rounding the real components while the second is used for rounding the imaginary components. `RoundingModeReal` and `RoundingModeImaginary` default to [`RoundNearest`](@ref), which rounds to the nearest integer, with ties (fractional values of 0.5) being rounded to the nearest even integer. # Example ```jldoctest julia> round(3.14 + 4.5im) 3.0 + 4.0im julia> round(3.14 + 4.5im, RoundUp, RoundNearestTiesUp) 4.0 + 5.0im julia> round(3.14159 + 4.512im; digits = 1) 3.1 + 4.5im julia> round(3.14159 + 4.512im; sigdigits = 3) 3.14 + 4.51im ``` """ function round(z::Complex, rr::RoundingMode=RoundNearest, ri::RoundingMode=rr; kwargs...) Complex(round(real(z), rr; kwargs...), round(imag(z), ri; kwargs...)) end float(z::Complex{<:AbstractFloat}) = z float(z::Complex) = Complex(float(real(z)), float(imag(z))) big(::Type{Complex{T}}) where {T<:Real} = Complex{big(T)} big(z::Complex{T}) where {T<:Real} = Complex{big(T)}(z) ## Array operations on complex numbers ## complex(A::AbstractArray{<:Complex}) = A function complex(A::AbstractArray{T}) where T if !isconcretetype(T) error("`complex` not defined on abstractly-typed arrays; please convert to a more specific type") end convert(AbstractArray{typeof(complex(zero(T)))}, A) end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/rational.jlH# This file is a part of Julia. License is MIT: https://julialang.org/license """ Rational{T<:Integer} <: Real Rational number type, with numerator and denominator of type `T`. Rationals are checked for overflow. """ struct Rational{T<:Integer} <: Real num::T den::T # Unexported inner constructor of Rational that bypasses all checks global unsafe_rational(::Type{T}, num, den) where {T} = new{T}(num, den) end unsafe_rational(num::T, den::T) where {T<:Integer} = unsafe_rational(T, num, den) unsafe_rational(num::Integer, den::Integer) = unsafe_rational(promote(num, den)...) @noinline __throw_rational_argerror_typemin(T) = throw(ArgumentError("invalid rational: denominator can't be typemin($T)")) function checked_den(::Type{T}, num::T, den::T) where T<:Integer if signbit(den) den = -den signbit(den) && __throw_rational_argerror_typemin(typeof(den)) num = -num end return unsafe_rational(T, num, den) end checked_den(num::T, den::T) where T<:Integer = checked_den(T, num, den) checked_den(num::Integer, den::Integer) = checked_den(promote(num, den)...) @noinline __throw_rational_argerror_zero(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)")) function Rational{T}(num::Integer, den::Integer) where T<:Integer iszero(den) && iszero(num) && __throw_rational_argerror_zero(T) num, den = divgcd(num, den) return checked_den(T, T(num), T(den)) end Rational(n::T, d::T) where {T<:Integer} = Rational{T}(n, d) Rational(n::Integer, d::Integer) = Rational(promote(n, d)...) Rational(n::Integer) = unsafe_rational(n, one(n)) function divgcd(x::Integer,y::Integer) g = gcd(x,y) div(x,g), div(y,g) end """ //(num, den) Divide two integers or rational numbers, giving a [`Rational`](@ref) result. # Examples ```jldoctest julia> 3 // 5 3//5 julia> (3 // 5) // (2 // 1) 3//10 ``` """ //(n::Integer, d::Integer) = Rational(n,d) function //(x::Rational, y::Integer) xn, yn = divgcd(promote(x.num, y)...) checked_den(xn, checked_mul(x.den, yn)) end function //(x::Integer, y::Rational) xn, yn = divgcd(promote(x, y.num)...) checked_den(checked_mul(xn, y.den), yn) end function //(x::Rational, y::Rational) xn,yn = divgcd(promote(x.num, y.num)...) xd,yd = divgcd(promote(x.den, y.den)...) checked_den(checked_mul(xn, yd), checked_mul(xd, yn)) end //(x::Complex, y::Real) = complex(real(x)//y, imag(x)//y) //(x::Number, y::Complex) = x*conj(y)//abs2(y) //(X::AbstractArray, y::Number) = X .// y function show(io::IO, x::Rational) show(io, numerator(x)) if isone(denominator(x)) && get(io, :typeinfo, Any) <: Rational return end print(io, "//") show(io, denominator(x)) end function read(s::IO, ::Type{Rational{T}}) where T<:Integer r = read(s,T) i = read(s,T) r//i end function write(s::IO, z::Rational) write(s,numerator(z),denominator(z)) end function parse(::Type{Rational{T}}, s::AbstractString) where T<:Integer ss = split(s, '/'; limit = 2) if isone(length(ss)) return Rational{T}(parse(T, s)) end @inbounds ns, ds = ss[1], ss[2] if startswith(ds, '/') ds = chop(ds; head = 1, tail = 0) end n = parse(T, ns) d = parse(T, ds) return n//d end function Rational{T}(x::Rational) where T<:Integer unsafe_rational(T, convert(T, x.num), convert(T, x.den)) end function Rational{T}(x::Integer) where T<:Integer unsafe_rational(T, convert(T, x), one(T)) end Rational(x::Rational) = x Bool(x::Rational) = x==0 ? false : x==1 ? true : throw(InexactError(:Bool, Bool, x)) # to resolve ambiguity (::Type{T})(x::Rational) where {T<:Integer} = (isinteger(x) ? convert(T, x.num)::T : throw(InexactError(nameof(T), T, x))) AbstractFloat(x::Rational) = (float(x.num)/float(x.den))::AbstractFloat function (::Type{T})(x::Rational{S}) where T<:AbstractFloat where S P = promote_type(T,S) convert(T, convert(P,x.num)/convert(P,x.den))::T end function Rational{T}(x::AbstractFloat) where T<:Integer r = rationalize(T, x, tol=0) x == convert(typeof(x), r) || throw(InexactError(:Rational, Rational{T}, x)) r end Rational(x::Float64) = Rational{Int64}(x) Rational(x::Float32) = Rational{Int}(x) big(q::Rational) = unsafe_rational(big(numerator(q)), big(denominator(q))) big(z::Complex{<:Rational{<:Integer}}) = Complex{Rational{BigInt}}(z) promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)} promote_rule(::Type{Rational{T}}, ::Type{Rational{S}}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)} promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:AbstractFloat} = promote_type(T,S) widen(::Type{Rational{T}}) where {T} = Rational{widen(T)} @noinline __throw_negate_unsigned() = throw(OverflowError("cannot negate unsigned number")) """ rationalize([T<:Integer=Int,] x; tol::Real=eps(x)) Approximate floating point number `x` as a [`Rational`](@ref) number with components of the given integer type. The result will differ from `x` by no more than `tol`. # Examples ```jldoctest julia> rationalize(5.6) 28//5 julia> a = rationalize(BigInt, 10.3) 103//10 julia> typeof(numerator(a)) BigInt ``` """ function rationalize(::Type{T}, x::Union{AbstractFloat, Rational}, tol::Real) where T<:Integer if tol < 0 throw(ArgumentError("negative tolerance $tol")) end T<:Unsigned && x < 0 && __throw_negate_unsigned() isnan(x) && return T(x)//one(T) isinf(x) && return unsafe_rational(x < 0 ? -one(T) : one(T), zero(T)) p, q = (x < 0 ? -one(T) : one(T)), zero(T) pp, qq = zero(T), one(T) x = abs(x) a = trunc(x) r = x-a y = one(x) tolx = oftype(x, tol) nt, t, tt = tolx, zero(tolx), tolx ia = np = nq = zero(T) # compute the successive convergents of the continued fraction # np // nq = (p*a + pp) // (q*a + qq) while r > nt try ia = convert(T,a) np = checked_add(checked_mul(ia,p),pp) nq = checked_add(checked_mul(ia,q),qq) p, pp = np, p q, qq = nq, q catch e isa(e,InexactError) || isa(e,OverflowError) || rethrow() return p // q end # naive approach of using # x = 1/r; a = trunc(x); r = x - a # is inexact, so we store x as x/y x, y = y, r a, r = divrem(x,y) # maintain # x0 = (p + (-1)^i * r) / q t, tt = nt, t nt = a*t+tt end # find optimal semiconvergent # smallest a such that x-a*y < a*t+tt a = cld(x-tt,y+t) try ia = convert(T,a) np = checked_add(checked_mul(ia,p),pp) nq = checked_add(checked_mul(ia,q),qq) return np // nq catch e isa(e,InexactError) || isa(e,OverflowError) || rethrow() return p // q end end rationalize(::Type{T}, x::AbstractFloat; tol::Real = eps(x)) where {T<:Integer} = rationalize(T, x, tol) rationalize(x::AbstractFloat; kvs...) = rationalize(Int, x; kvs...) rationalize(::Type{T}, x::Complex; kvs...) where {T<:Integer} = Complex(rationalize(T, x.re; kvs...), rationalize(T, x.im; kvs...)) rationalize(x::Complex; kvs...) = Complex(rationalize(Int, x.re; kvs...), rationalize(Int, x.im; kvs...)) rationalize(::Type{T}, x::Rational; tol::Real = 0) where {T<:Integer} = rationalize(T, x, tol) rationalize(x::Rational; kvs...) = x rationalize(x::Integer; kvs...) = Rational(x) function rationalize(::Type{T}, x::Integer; kvs...) where {T<:Integer} if Base.hastypemax(T) # BigInt doesn't x < typemin(T) && return unsafe_rational(-one(T), zero(T)) x > typemax(T) && return unsafe_rational(one(T), zero(T)) end return Rational{T}(x) end """ numerator(x) Numerator of the rational representation of `x`. # Examples ```jldoctest julia> numerator(2//3) 2 julia> numerator(4) 4 ``` """ numerator(x::Integer) = x numerator(x::Rational) = x.num """ denominator(x) Denominator of the rational representation of `x`. # Examples ```jldoctest julia> denominator(2//3) 3 julia> denominator(4) 1 ``` """ denominator(x::Integer) = one(x) denominator(x::Rational) = x.den sign(x::Rational) = oftype(x, sign(x.num)) signbit(x::Rational) = signbit(x.num) copysign(x::Rational, y::Real) = unsafe_rational(copysign(x.num, y), x.den) copysign(x::Rational, y::Rational) = unsafe_rational(copysign(x.num, y.num), x.den) abs(x::Rational) = unsafe_rational(checked_abs(x.num), x.den) typemin(::Type{Rational{T}}) where {T<:Signed} = unsafe_rational(T, -one(T), zero(T)) typemin(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, zero(T), one(T)) typemax(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, one(T), zero(T)) isinteger(x::Rational) = x.den == 1 ispow2(x::Rational) = ispow2(x.num) & ispow2(x.den) +(x::Rational) = unsafe_rational(+x.num, x.den) -(x::Rational) = unsafe_rational(-x.num, x.den) function -(x::Rational{T}) where T<:BitSigned x.num == typemin(T) && __throw_rational_numerator_typemin(T) unsafe_rational(-x.num, x.den) end @noinline __throw_rational_numerator_typemin(T) = throw(OverflowError("rational numerator is typemin($T)")) function -(x::Rational{T}) where T<:Unsigned x.num != zero(T) && __throw_negate_unsigned() x end function +(x::Rational, y::Rational) xp, yp = promote(x, y)::NTuple{2,Rational} if isinf(x) && x == y return xp end xd, yd = divgcd(promote(x.den, y.den)...) Rational(checked_add(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) end function -(x::Rational, y::Rational) xp, yp = promote(x, y)::NTuple{2,Rational} if isinf(x) && x == -y return xp end xd, yd = divgcd(promote(x.den, y.den)...) Rational(checked_sub(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) end for (op,chop) in ((:rem,:rem), (:mod,:mod)) @eval begin function ($op)(x::Rational, y::Rational) xd, yd = divgcd(promote(x.den, y.den)...) Rational(($chop)(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) end end end for (op,chop) in ((:+,:checked_add), (:-,:checked_sub), (:rem,:rem), (:mod,:mod)) @eval begin function ($op)(x::Rational, y::Integer) unsafe_rational(($chop)(x.num, checked_mul(x.den, y)), x.den) end end end for (op,chop) in ((:+,:checked_add), (:-,:checked_sub)) @eval begin function ($op)(y::Integer, x::Rational) unsafe_rational(($chop)(checked_mul(x.den, y), x.num), x.den) end end end for (op,chop) in ((:rem,:rem), (:mod,:mod)) @eval begin function ($op)(y::Integer, x::Rational) Rational(($chop)(checked_mul(x.den, y), x.num), x.den) end end end function *(x::Rational, y::Rational) xn, yd = divgcd(promote(x.num, y.den)...) xd, yn = divgcd(promote(x.den, y.num)...) unsafe_rational(checked_mul(xn, yn), checked_mul(xd, yd)) end function *(x::Rational, y::Integer) xd, yn = divgcd(promote(x.den, y)...) unsafe_rational(checked_mul(x.num, yn), xd) end function *(y::Integer, x::Rational) yn, xd = divgcd(promote(y, x.den)...) unsafe_rational(checked_mul(yn, x.num), xd) end /(x::Rational, y::Union{Rational, Integer, Complex{<:Union{Integer,Rational}}}) = x//y /(x::Union{Integer, Complex{<:Union{Integer,Rational}}}, y::Rational) = x//y inv(x::Rational{T}) where {T} = checked_den(x.den, x.num) fma(x::Rational, y::Rational, z::Rational) = x*y+z ==(x::Rational, y::Rational) = (x.den == y.den) & (x.num == y.num) <( x::Rational, y::Rational) = x.den == y.den ? x.num < y.num : widemul(x.num,y.den) < widemul(x.den,y.num) <=(x::Rational, y::Rational) = x.den == y.den ? x.num <= y.num : widemul(x.num,y.den) <= widemul(x.den,y.num) ==(x::Rational, y::Integer ) = (x.den == 1) & (x.num == y) ==(x::Integer , y::Rational) = y == x <( x::Rational, y::Integer ) = x.num < widemul(x.den,y) <( x::Integer , y::Rational) = widemul(x,y.den) < y.num <=(x::Rational, y::Integer ) = x.num <= widemul(x.den,y) <=(x::Integer , y::Rational) = widemul(x,y.den) <= y.num function ==(x::AbstractFloat, q::Rational) if isfinite(x) (count_ones(q.den) == 1) & (x*q.den == q.num) else x == q.num/q.den end end ==(q::Rational, x::AbstractFloat) = x == q for rel in (:<,:<=,:cmp) for (Tx,Ty) in ((Rational,AbstractFloat), (AbstractFloat,Rational)) @eval function ($rel)(x::$Tx, y::$Ty) if isnan(x) $(rel === :cmp ? :(return isnan(y) ? 0 : 1) : :(return false)) end if isnan(y) $(rel === :cmp ? :(return -1) : :(return false)) end xn, xp, xd = decompose(x) yn, yp, yd = decompose(y) if xd < 0 xn = -xn xd = -xd end if yd < 0 yn = -yn yd = -yd end xc, yc = widemul(xn,yd), widemul(yn,xd) xs, ys = sign(xc), sign(yc) if xs != ys return ($rel)(xs,ys) elseif xs == 0 # both are zero or ยฑInf return ($rel)(xn,yn) end xb, yb = ndigits0z(xc,2) + xp, ndigits0z(yc,2) + yp if xb == yb xc, yc = promote(xc,yc) if xp > yp xc = (xc<<(xp-yp)) else yc = (yc<<(yp-xp)) end return ($rel)(xc,yc) else return xc > 0 ? ($rel)(xb,yb) : ($rel)(yb,xb) end end end end # needed to avoid ambiguity between ==(x::Real, z::Complex) and ==(x::Rational, y::Number) ==(z::Complex , x::Rational) = isreal(z) & (real(z) == x) ==(x::Rational, z::Complex ) = isreal(z) & (real(z) == x) function div(x::Rational, y::Integer, r::RoundingMode) xn,yn = divgcd(x.num,y) div(xn, checked_mul(x.den,yn), r) end function div(x::Integer, y::Rational, r::RoundingMode) xn,yn = divgcd(x,y.num) div(checked_mul(xn,y.den), yn, r) end function div(x::Rational, y::Rational, r::RoundingMode) xn,yn = divgcd(x.num,y.num) xd,yd = divgcd(x.den,y.den) div(checked_mul(xn,yd), checked_mul(xd,yn), r) end # For compatibility - to be removed in 2.0 when the generic fallbacks # are removed from div.jl div(x::T, y::T, r::RoundingMode) where {T<:Rational} = invoke(div, Tuple{Rational, Rational, RoundingMode}, x, y, r) for (S, T) in ((Rational, Integer), (Integer, Rational), (Rational, Rational)) @eval begin div(x::$S, y::$T) = div(x, y, RoundToZero) fld(x::$S, y::$T) = div(x, y, RoundDown) cld(x::$S, y::$T) = div(x, y, RoundUp) end end trunc(::Type{T}, x::Rational) where {T} = round(T, x, RoundToZero) floor(::Type{T}, x::Rational) where {T} = round(T, x, RoundDown) ceil(::Type{T}, x::Rational) where {T} = round(T, x, RoundUp) round(x::Rational, r::RoundingMode=RoundNearest) = round(typeof(x), x, r) function round(::Type{T}, x::Rational{Tr}, r::RoundingMode=RoundNearest) where {T,Tr} if iszero(denominator(x)) && !(T <: Integer) return convert(T, copysign(unsafe_rational(one(Tr), zero(Tr)), numerator(x))) end convert(T, div(numerator(x), denominator(x), r)) end function round(::Type{T}, x::Rational{Bool}, ::RoundingMode=RoundNearest) where T if denominator(x) == false && (T <: Integer) throw(DivideError()) end convert(T, x) end function ^(x::Rational, n::Integer) n >= 0 ? power_by_squaring(x,n) : power_by_squaring(inv(x),-n) end ^(x::Number, y::Rational) = x^(y.num/y.den) ^(x::T, y::Rational) where {T<:AbstractFloat} = x^convert(T,y) ^(z::Complex{T}, p::Rational) where {T<:Real} = z^convert(typeof(one(T)^p), p) ^(z::Complex{<:Rational}, n::Bool) = n ? z : one(z) # to resolve ambiguity function ^(z::Complex{<:Rational}, n::Integer) n >= 0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n) end iszero(x::Rational) = iszero(numerator(x)) isone(x::Rational) = isone(numerator(x)) & isone(denominator(x)) function lerpi(j::Integer, d::Integer, a::Rational, b::Rational) ((d-j)*a)/d + (j*b)/d end float(::Type{Rational{T}}) where {T<:Integer} = float(T) gcd(x::Rational, y::Rational) = unsafe_rational(gcd(x.num, y.num), lcm(x.den, y.den)) lcm(x::Rational, y::Rational) = unsafe_rational(lcm(x.num, y.num), gcd(x.den, y.den)) function gcdx(x::Rational, y::Rational) c = gcd(x, y) if iszero(c.num) a, b = one(c.num), c.num elseif iszero(c.den) a = ifelse(iszero(x.den), one(c.den), c.den) b = ifelse(iszero(y.den), one(c.den), c.den) else idiv(x, c) = div(x.num, c.num) * div(c.den, x.den) _, a, b = gcdx(idiv(x, c), idiv(y, c)) end c, a, b end ## streamlined hashing for smallish rational types ## decompose(x::Rational) = numerator(x), 0, denominator(x) function hash(x::Rational{<:BitInteger64}, h::UInt) num, den = Base.numerator(x), Base.denominator(x) den == 1 && return hash(num, h) den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) if isodd(den) # since den != 1, this rational can't be a Float64 pow = trailing_zeros(num) num >>= pow h = hash_integer(den, h) else pow = trailing_zeros(den) den >>= pow pow = -pow if den == 1 if uabs(num) < UInt64(maxintfloat(Float64)) return hash(ldexp(Float64(num),pow),h) end else h = hash_integer(den, h) end end h = hash_integer(pow, h) h = hash_integer(num, h) return h end # These methods are only needed for performance. Since `first(r)` and `last(r)` have the # same denominator (because their difference is an integer), `length(r)` can be calculated # without calling `gcd`. function length(r::AbstractUnitRange{T}) where T<:Rational @inline f = first(r) l = last(r) return div(l.num - f.num + f.den, f.den) end function checked_length(r::AbstractUnitRange{T}) where T<:Rational f = first(r) l = last(r) if isempty(r) return f.num - f.num end return div(checked_add(checked_sub(l.num, f.num), f.den), f.den) end S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/multinverses.jl&# This file is a part of Julia. License is MIT: https://julialang.org/license module MultiplicativeInverses import Base: div, divrem, rem, unsigned using Base: IndexLinear, IndexCartesian, tail export multiplicativeinverse unsigned(::Type{Bool}) = UInt unsigned(::Type{Int8}) = UInt8 unsigned(::Type{Int16}) = UInt16 unsigned(::Type{Int32}) = UInt32 unsigned(::Type{Int64}) = UInt64 unsigned(::Type{Int128}) = UInt128 unsigned(::Type{T}) where {T<:Unsigned} = T abstract type MultiplicativeInverse{T} <: Number end # Computes integer division by a constant using multiply, add, and bitshift. # The idea here is to compute floor(n/d) as floor(m*n/2^p) and then # implement division by 2^p as a right bitshift. The trick is finding # m (the "magic number") and p. Roughly speaking, one can think of this as # floor(n/d) = floor((n/2^p) * (2^p/d)) # so that m is effectively 2^p/d. # # A few examples are illustrative: # Division of Int32 by 3: # floor((2^32+2)/3 * n/2^32) = floor(n/3 + 2n/(3*2^32)) # The correction term, 2n/(3*2^32), is strictly less than 1/3 for any # nonnegative n::Int32, so this divides any nonnegative Int32 by 3. # (When n < 0, we add 1, and one can show that this computes # ceil(n/d) = -floor(abs(n)/d).) # # Division of Int32 by 5 uses a magic number (2^33+3)/5 and then # right-shifts by 33 rather than 32. Consequently, the size of the # shift depends on the specific denominator. # # Division of Int32 by 7 would be problematic, because a viable magic # number of (2^34+5)/7 is too big to represent as an Int32 (the # unsigned representation needs 32 bits). We can exploit wrap-around # and use (2^34+5)/7 - 2^32 (an Int32 < 0), and then correct the # 64-bit product with an add (the `addmul` field below). # # Further details can be found in Hacker's Delight, Chapter 10. struct SignedMultiplicativeInverse{T<:Signed} <: MultiplicativeInverse{T} divisor::T multiplier::T addmul::Int8 shift::UInt8 function SignedMultiplicativeInverse{T}(d::T) where T<:Signed d == 0 && throw(ArgumentError("cannot compute magic for d == $d")) signedmin = unsigned(typemin(T)) UT = unsigned(T) # Algorithm from Hacker's Delight, section 10-4 ad = unsigned(abs(d)) t = signedmin + signbit(d) anc = t - one(UT) - rem(t, ad) # absolute value of nc p = sizeof(d)*8 - 1 q1, r1 = divrem(signedmin, anc) q2, r2 = divrem(signedmin, ad) while true p += 1 # loop until we find a satisfactory p # update q1, r1 = divrem(2^p, abs(nc)) q1 = q1<<1 r1 = r1<<1 if r1 >= anc # must be unsigned comparison q1 += one(UT) r1 -= anc end # update q2, r2 = divrem(2^p, abs(d)) q2 = q2<<1 r2 = r2<<1 if r2 >= ad q2 += one(UT) r2 -= ad end delta = ad - r2 (q1 < delta || (q1 == delta && r1 == 0)) || break end m = flipsign((q2 + one(UT)) % T, d) # resulting magic number s = p - sizeof(d)*8 # resulting shift new(d, m, d > 0 && m < 0 ? Int8(1) : d < 0 && m > 0 ? Int8(-1) : Int8(0), UInt8(s)) end end SignedMultiplicativeInverse(x::Signed) = SignedMultiplicativeInverse{typeof(x)}(x) struct UnsignedMultiplicativeInverse{T<:Unsigned} <: MultiplicativeInverse{T} divisor::T multiplier::T add::Bool shift::UInt8 function UnsignedMultiplicativeInverse{T}(d::T) where T<:Unsigned d == 0 && throw(ArgumentError("cannot compute magic for d == $d")) add = false signedmin = one(d) << (sizeof(d)*8-1) signedmax = signedmin - one(T) allones = (zero(d) - 1) % T nc = allones - rem(convert(T, allones - d), d) p = 8*sizeof(d) - 1 q1, r1 = divrem(signedmin, nc) q2, r2 = divrem(signedmax, d) while true p += 1 if r1 >= convert(T, nc - r1) q1 = q1 + q1 + one(T) r1 = r1 + r1 - nc else q1 = q1 + q1 r1 = r1 + r1 end if convert(T, r2 + one(T)) >= convert(T, d - r2) add |= q2 >= signedmax q2 = q2 + q2 + one(T) r2 = r2 + r2 + one(T) - d else add |= q2 >= signedmin q2 = q2 + q2 r2 = r2 + r2 + one(T) end delta = d - one(T) - r2 (p < sizeof(d)*16 && (q1 < delta || (q1 == delta && r1 == 0))) || break end m = q2 + one(T) # resulting magic number s = p - sizeof(d)*8 - add # resulting shift new(d, m, add, s % UInt8) end end UnsignedMultiplicativeInverse(x::Unsigned) = UnsignedMultiplicativeInverse{typeof(x)}(x) # Returns the higher half of the product a*b function _mul_high(a::T, b::T) where {T<:Union{Signed, Unsigned}} ((widen(a)*b) >>> (sizeof(a)*8)) % T end function _mul_high(a::UInt128, b::UInt128) shift = sizeof(a)*4 mask = typemax(UInt128) >> shift a1, a2 = a >>> shift, a & mask b1, b2 = b >>> shift, b & mask a1b1, a1b2, a2b1, a2b2 = a1*b1, a1*b2, a2*b1, a2*b2 carry = ((a1b2 & mask) + (a2b1 & mask) + (a2b2 >>> shift)) >>> shift a1b1 + (a1b2 >>> shift) + (a2b1 >>> shift) + carry end function _mul_high(a::Int128, b::Int128) shift = sizeof(a)*8 - 1 t1, t2 = (a >> shift) & b % UInt128, (b >> shift) & a % UInt128 (_mul_high(a % UInt128, b % UInt128) - t1 - t2) % Int128 end function div(a::T, b::SignedMultiplicativeInverse{T}) where T x = _mul_high(a, b.multiplier) x += (a*b.addmul) % T ifelse(abs(b.divisor) == 1, a*b.divisor, (signbit(x) + (x >> b.shift)) % T) end function div(a::T, b::UnsignedMultiplicativeInverse{T}) where T x = _mul_high(a, b.multiplier) x = ifelse(b.add, convert(T, convert(T, (convert(T, a - x) >>> 1)) + x), x) ifelse(b.divisor == 1, a, x >>> b.shift) end rem(a::T, b::MultiplicativeInverse{T}) where {T} = a - div(a, b)*b.divisor function divrem(a::T, b::MultiplicativeInverse{T}) where T d = div(a, b) (d, a - d*b.divisor) end multiplicativeinverse(x::Signed) = SignedMultiplicativeInverse(x) multiplicativeinverse(x::Unsigned) = UnsignedMultiplicativeInverse(x) end X/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/abstractarraymath.jl’1# This file is a part of Julia. License is MIT: https://julialang.org/license ## Basic functions ## isreal(x::AbstractArray) = all(isreal,x) iszero(x::AbstractArray) = all(iszero,x) isreal(x::AbstractArray{<:Real}) = true ## Constructors ## """ vec(a::AbstractArray) -> AbstractVector Reshape the array `a` as a one-dimensional column vector. Return `a` if it is already an `AbstractVector`. The resulting array shares the same underlying data as `a`, so it will only be mutable if `a` is mutable, in which case modifying one will also modify the other. # Examples ```jldoctest julia> a = [1 2 3; 4 5 6] 2ร—3 Matrix{Int64}: 1 2 3 4 5 6 julia> vec(a) 6-element Vector{Int64}: 1 4 2 5 3 6 julia> vec(1:3) 1:3 ``` See also [`reshape`](@ref), [`dropdims`](@ref). """ vec(a::AbstractArray) = reshape(a,length(a)) vec(a::AbstractVector) = a _sub(::Tuple{}, ::Tuple{}) = () _sub(t::Tuple, ::Tuple{}) = t _sub(t::Tuple, s::Tuple) = _sub(tail(t), tail(s)) """ dropdims(A; dims) Return an array with the same data as `A`, but with the dimensions specified by `dims` removed. `size(A,d)` must equal 1 for every `d` in `dims`, and repeated dimensions or numbers outside `1:ndims(A)` are forbidden. The result shares the same underlying data as `A`, such that the result is mutable if and only if `A` is mutable, and setting elements of one alters the values of the other. See also: [`reshape`](@ref), [`vec`](@ref). # Examples ```jldoctest julia> a = reshape(Vector(1:4),(2,2,1,1)) 2ร—2ร—1ร—1 Array{Int64, 4}: [:, :, 1, 1] = 1 3 2 4 julia> b = dropdims(a; dims=3) 2ร—2ร—1 Array{Int64, 3}: [:, :, 1] = 1 3 2 4 julia> b[1,1,1] = 5; a 2ร—2ร—1ร—1 Array{Int64, 4}: [:, :, 1, 1] = 5 3 2 4 ``` """ dropdims(A; dims) = _dropdims(A, dims) function _dropdims(A::AbstractArray, dims::Dims) for i in eachindex(dims) 1 <= dims[i] <= ndims(A) || throw(ArgumentError("dropped dims must be in range 1:ndims(A)")) length(axes(A, dims[i])) == 1 || throw(ArgumentError("dropped dims must all be size 1")) for j = 1:i-1 dims[j] == dims[i] && throw(ArgumentError("dropped dims must be unique")) end end ax = _foldoneto((ds, d) -> d in dims ? ds : (ds..., axes(A,d)), (), Val(ndims(A))) reshape(A, ax::typeof(_sub(axes(A), dims))) end _dropdims(A::AbstractArray, dim::Integer) = _dropdims(A, (Int(dim),)) ## Unary operators ## """ conj!(A) Transform an array to its complex conjugate in-place. See also [`conj`](@ref). # Examples ```jldoctest julia> A = [1+im 2-im; 2+2im 3+im] 2ร—2 Matrix{Complex{Int64}}: 1+1im 2-1im 2+2im 3+1im julia> conj!(A); julia> A 2ร—2 Matrix{Complex{Int64}}: 1-1im 2+1im 2-2im 3-1im ``` """ conj!(A::AbstractArray{<:Number}) = (@inbounds broadcast!(conj, A, A); A) conj!(x::AbstractArray{<:Real}) = x """ conj(A::AbstractArray) Return an array containing the complex conjugate of each entry in array `A`. Equivalent to `conj.(A)`, except that when `eltype(A) <: Real` `A` is returned without copying, and that when `A` has zero dimensions, a 0-dimensional array is returned (rather than a scalar). # Examples ```jldoctest julia> conj([1, 2im, 3 + 4im]) 3-element Vector{Complex{Int64}}: 1 + 0im 0 - 2im 3 - 4im julia> conj(fill(2 - im)) 0-dimensional Array{Complex{Int64}, 0}: 2 + 1im ``` """ conj(A::AbstractArray) = broadcast_preserving_zero_d(conj, A) conj(A::AbstractArray{<:Real}) = A """ real(A::AbstractArray) Return an array containing the real part of each entry in array `A`. Equivalent to `real.(A)`, except that when `eltype(A) <: Real` `A` is returned without copying, and that when `A` has zero dimensions, a 0-dimensional array is returned (rather than a scalar). # Examples ```jldoctest julia> real([1, 2im, 3 + 4im]) 3-element Vector{Int64}: 1 0 3 julia> real(fill(2 - im)) 0-dimensional Array{Int64, 0}: 2 ``` """ real(A::AbstractArray) = broadcast_preserving_zero_d(real, A) real(A::AbstractArray{<:Real}) = A """ imag(A::AbstractArray) Return an array containing the imaginary part of each entry in array `A`. Equivalent to `imag.(A)`, except that when `A` has zero dimensions, a 0-dimensional array is returned (rather than a scalar). # Examples ```jldoctest julia> imag([1, 2im, 3 + 4im]) 3-element Vector{Int64}: 0 2 4 julia> imag(fill(2 - im)) 0-dimensional Array{Int64, 0}: -1 ``` """ imag(A::AbstractArray) = broadcast_preserving_zero_d(imag, A) imag(A::AbstractArray{<:Real}) = zero(A) """ reim(A::AbstractArray) Return a tuple of two arrays containing respectively the real and the imaginary part of each entry in `A`. Equivalent to `(real.(A), imag.(A))`, except that when `eltype(A) <: Real` `A` is returned without copying to represent the real part, and that when `A` has zero dimensions, a 0-dimensional array is returned (rather than a scalar). # Examples ```jldoctest julia> reim([1, 2im, 3 + 4im]) ([1, 0, 3], [0, 2, 4]) julia> reim(fill(2 - im)) (fill(2), fill(-1)) ``` """ reim(A::AbstractArray) -(A::AbstractArray) = broadcast_preserving_zero_d(-, A) +(x::AbstractArray{<:Number}) = x *(x::AbstractArray{<:Number,2}) = x # index A[:,:,...,i,:,:,...] where "i" is in dimension "d" """ selectdim(A, d::Integer, i) Return a view of all the data of `A` where the index for dimension `d` equals `i`. Equivalent to `view(A,:,:,...,i,:,:,...)` where `i` is in position `d`. See also: [`eachslice`](@ref). # Examples ```jldoctest julia> A = [1 2 3 4; 5 6 7 8] 2ร—4 Matrix{Int64}: 1 2 3 4 5 6 7 8 julia> selectdim(A, 2, 3) 2-element view(::Matrix{Int64}, :, 3) with eltype Int64: 3 7 julia> selectdim(A, 2, 3:4) 2ร—2 view(::Matrix{Int64}, :, 3:4) with eltype Int64: 3 4 7 8 ``` """ @inline selectdim(A::AbstractArray, d::Integer, i) = _selectdim(A, d, i, _setindex(i, d, map(Slice, axes(A))...)) @noinline function _selectdim(A, d, i, idxs) d >= 1 || throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) nd = ndims(A) d > nd && (i == 1 || throw(BoundsError(A, (ntuple(Returns(Colon()),d-1)..., i)))) return view(A, idxs...) end function circshift(a::AbstractArray, shiftamt::Real) circshift!(similar(a), a, (Integer(shiftamt),)) end circshift(a::AbstractArray, shiftamt::DimsInteger) = circshift!(similar(a), a, shiftamt) """ circshift(A, shifts) Circularly shift, i.e. rotate, the data in an array. The second argument is a tuple or vector giving the amount to shift in each dimension, or an integer to shift only in the first dimension. See also: [`circshift!`](@ref), [`circcopy!`](@ref), [`bitrotate`](@ref), [`<<`](@ref). # Examples ```jldoctest julia> b = reshape(Vector(1:16), (4,4)) 4ร—4 Matrix{Int64}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 julia> circshift(b, (0,2)) 4ร—4 Matrix{Int64}: 9 13 1 5 10 14 2 6 11 15 3 7 12 16 4 8 julia> circshift(b, (-1,0)) 4ร—4 Matrix{Int64}: 2 6 10 14 3 7 11 15 4 8 12 16 1 5 9 13 julia> a = BitArray([true, true, false, false, true]) 5-element BitVector: 1 1 0 0 1 julia> circshift(a, 1) 5-element BitVector: 1 1 1 0 0 julia> circshift(a, -1) 5-element BitVector: 1 0 0 1 1 ``` """ function circshift(a::AbstractArray, shiftamt) circshift!(similar(a), a, map(Integer, (shiftamt...,))) end ## Other array functions ## """ repeat(A::AbstractArray, counts::Integer...) Construct an array by repeating array `A` a given number of times in each dimension, specified by `counts`. See also: [`fill`](@ref), [`Iterators.repeated`](@ref), [`Iterators.cycle`](@ref). # Examples ```jldoctest julia> repeat([1, 2, 3], 2) 6-element Vector{Int64}: 1 2 3 1 2 3 julia> repeat([1, 2, 3], 2, 3) 6ร—3 Matrix{Int64}: 1 1 1 2 2 2 3 3 3 1 1 1 2 2 2 3 3 3 ``` """ function repeat(A::AbstractArray, counts...) return _RepeatInnerOuter.repeat(A, outer=counts) end """ repeat(A::AbstractArray; inner=ntuple(Returns(1), ndims(A)), outer=ntuple(Returns(1), ndims(A))) Construct an array by repeating the entries of `A`. The i-th element of `inner` specifies the number of times that the individual entries of the i-th dimension of `A` should be repeated. The i-th element of `outer` specifies the number of times that a slice along the i-th dimension of `A` should be repeated. If `inner` or `outer` are omitted, no repetition is performed. # Examples ```jldoctest julia> repeat(1:2, inner=2) 4-element Vector{Int64}: 1 1 2 2 julia> repeat(1:2, outer=2) 4-element Vector{Int64}: 1 2 1 2 julia> repeat([1 2; 3 4], inner=(2, 1), outer=(1, 3)) 4ร—6 Matrix{Int64}: 1 2 1 2 1 2 1 2 1 2 1 2 3 4 3 4 3 4 3 4 3 4 3 4 ``` """ function repeat(A::AbstractArray; inner = nothing, outer = nothing) return _RepeatInnerOuter.repeat(A, inner=inner, outer=outer) end module _RepeatInnerOuter function repeat(arr; inner=nothing, outer=nothing) check(arr, inner, outer) arr, inner, outer = resolve(arr, inner, outer) repeat_inner_outer(arr, inner, outer) end to_tuple(t::Tuple) = t to_tuple(x::Integer) = (x,) to_tuple(itr) = tuple(itr...) function pad(a, b) N = max(length(a), length(b)) Base.fill_to_length(a, 1, Val(N)), Base.fill_to_length(b, 1, Val(N)) end function pad(a, b, c) N = max(max(length(a), length(b)), length(c)) Base.fill_to_length(a, 1, Val(N)), Base.fill_to_length(b, 1, Val(N)), Base.fill_to_length(c, 1, Val(N)) end function resolve(arr::AbstractArray{<:Any, N}, inner::NTuple{N, Any}, outer::NTuple{N,Any}) where {N} arr, inner, outer end function resolve(arr, inner, outer) dims, inner, outer = pad(size(arr), to_tuple(inner), to_tuple(outer)) reshape(arr, dims), inner, outer end function resolve(arr, inner::Nothing, outer::Nothing) return arr, inner, outer end function resolve(arr, inner::Nothing, outer) dims, outer = pad(size(arr), to_tuple(outer)) reshape(arr, dims), inner, outer end function resolve(arr, inner, outer::Nothing) dims, inner = pad(size(arr), to_tuple(inner)) reshape(arr, dims), inner, outer end function check(arr, inner, outer) if inner !== nothing # TODO: Currently one based indexing is demanded for inner !== nothing, # but not for outer !== nothing. Decide for something consistent. Base.require_one_based_indexing(arr) if any(<(0), inner) throw(ArgumentError("no inner repetition count may be negative; got $inner")) end if length(inner) < ndims(arr) throw(ArgumentError("number of inner repetitions ($(length(inner))) cannot be less than number of dimensions of input array ($(ndims(arr)))")) end end if outer !== nothing if any(<(0), outer) throw(ArgumentError("no outer repetition count may be negative; got $outer")) end if (length(outer) < ndims(arr)) && (inner !== nothing) throw(ArgumentError("number of outer repetitions ($(length(outer))) cannot be less than number of dimensions of input array ($(ndims(arr)))")) end end end repeat_inner_outer(arr, inner::Nothing, outer::Nothing) = arr repeat_inner_outer(arr, ::Nothing, outer) = repeat_outer(arr, outer) repeat_inner_outer(arr, inner, ::Nothing) = repeat_inner(arr, inner) repeat_inner_outer(arr, inner, outer) = repeat_outer(repeat_inner(arr, inner), outer) function repeat_outer(a::AbstractMatrix, (m,n)::NTuple{2, Any}) o, p = size(a,1), size(a,2) b = similar(a, o*m, p*n) for j=1:n d = (j-1)*p+1 R = d:d+p-1 for i=1:m c = (i-1)*o+1 @inbounds b[c:c+o-1, R] = a end end return b end function repeat_outer(a::AbstractVector, (m,)::Tuple{Any}) o = length(a) b = similar(a, o*m) for i=1:m c = (i-1)*o+1 @inbounds b[c:c+o-1] = a end return b end function repeat_outer(arr::AbstractArray{<:Any,N}, dims::NTuple{N,Any}) where {N} insize = size(arr) outsize = map(*, insize, dims) out = similar(arr, outsize) for I in CartesianIndices(arr) for J in CartesianIndices(dims) TIJ = map(Tuple(I), Tuple(J), insize) do i, j, d i + d * (j-1) end IJ = CartesianIndex(TIJ) @inbounds out[IJ] = arr[I] end end return out end function repeat_inner(arr, inner) outsize = map(*, size(arr), inner) out = similar(arr, outsize) for I in CartesianIndices(arr) for J in CartesianIndices(inner) TIJ = map(Tuple(I), Tuple(J), inner) do i, j, d (i-1) * d + j end IJ = CartesianIndex(TIJ) @inbounds out[IJ] = arr[I] end end return out end end#module P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/arraymath.jl# This file is a part of Julia. License is MIT: https://julialang.org/license ## Binary arithmetic operators ## for f in (:+, :-) @eval function ($f)(A::AbstractArray, B::AbstractArray) promote_shape(A, B) # check size compatibility broadcast_preserving_zero_d($f, A, B) end end function +(A::Array, Bs::Array...) for B in Bs promote_shape(A, B) # check size compatibility end broadcast_preserving_zero_d(+, A, Bs...) end for f in (:/, :\, :*) if f !== :/ @eval ($f)(A::Number, B::AbstractArray) = broadcast_preserving_zero_d($f, A, B) end if f !== :\ @eval ($f)(A::AbstractArray, B::Number) = broadcast_preserving_zero_d($f, A, B) end end ## data movement ## """ reverse(A; dims=:) Reverse `A` along dimension `dims`, which can be an integer (a single dimension), a tuple of integers (a tuple of dimensions) or `:` (reverse along all the dimensions, the default). See also [`reverse!`](@ref) for in-place reversal. # Examples ```jldoctest julia> b = Int64[1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> reverse(b, dims=2) 2ร—2 Matrix{Int64}: 2 1 4 3 julia> reverse(b) 2ร—2 Matrix{Int64}: 4 3 2 1 ``` !!! compat "Julia 1.6" Prior to Julia 1.6, only single-integer `dims` are supported in `reverse`. """ reverse(A::AbstractArray; dims=:) = _reverse(A, dims) _reverse(A, dims) = reverse!(copymutable(A); dims) """ reverse!(A; dims=:) Like [`reverse`](@ref), but operates in-place in `A`. !!! compat "Julia 1.6" Multidimensional `reverse!` requires Julia 1.6. """ reverse!(A::AbstractArray; dims=:) = _reverse!(A, dims) _reverse!(A::AbstractArray{<:Any,N}, ::Colon) where {N} = _reverse!(A, ntuple(identity, Val{N}())) _reverse!(A, dim::Integer) = _reverse!(A, (Int(dim),)) _reverse!(A, dims::NTuple{M,Integer}) where {M} = _reverse!(A, Int.(dims)) function _reverse!(A::AbstractArray{<:Any,N}, dims::NTuple{M,Int}) where {N,M} dimrev = ntuple(k -> k in dims, Val{N}()) # boolean tuple indicating reversed dims if N < M || M != sum(dimrev) throw(ArgumentError("invalid dimensions $dims in reverse!")) end M == 0 && return A # nothing to reverse # swapping loop only needs to traverse โ‰ˆhalf of the array halfsz = ntuple(k -> k == dims[1] ? size(A,k) รท 2 : size(A,k), Val{N}()) last1 = ntuple(k -> lastindex(A,k)+firstindex(A,k), Val{N}()) # offset for reversed index for i in CartesianIndices(ntuple(k -> firstindex(A,k):firstindex(A,k)-1+@inbounds(halfsz[k]), Val{N}())) iโ‚œ = Tuple(i) iแตฃ = CartesianIndex(ifelse.(dimrev, last1 .- iโ‚œ, iโ‚œ)) @inbounds A[iแตฃ], A[i] = A[i], A[iแตฃ] end if M > 1 && isodd(size(A, dims[1])) # middle slice for odd dimensions must be recursively flipped mid = firstindex(A, dims[1]) + (size(A, dims[1]) รท 2) midslice = CartesianIndices(ntuple(k -> k == dims[1] ? (mid:mid) : (firstindex(A,k):lastindex(A,k)), Val{N}())) _reverse!(view(A, midslice), dims[2:end]) end return A end # fix ambiguity with array.jl: _reverse!(A::AbstractVector, dim::Tuple{Int}) = _reverse!(A, first(dim)) """ rotl90(A) Rotate matrix `A` left 90 degrees. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rotl90(a) 2ร—2 Matrix{Int64}: 2 4 1 3 ``` """ function rotl90(A::AbstractMatrix) ind1, ind2 = axes(A) B = similar(A, (ind2,ind1)) n = first(ind2)+last(ind2) for i=axes(A,1), j=ind2 B[n-j,i] = A[i,j] end return B end """ rotr90(A) Rotate matrix `A` right 90 degrees. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rotr90(a) 2ร—2 Matrix{Int64}: 3 1 4 2 ``` """ function rotr90(A::AbstractMatrix) ind1, ind2 = axes(A) B = similar(A, (ind2,ind1)) m = first(ind1)+last(ind1) for i=ind1, j=axes(A,2) B[j,m-i] = A[i,j] end return B end """ rot180(A) Rotate matrix `A` 180 degrees. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rot180(a) 2ร—2 Matrix{Int64}: 4 3 2 1 ``` """ function rot180(A::AbstractMatrix) B = similar(A) ind1, ind2 = axes(A,1), axes(A,2) m, n = first(ind1)+last(ind1), first(ind2)+last(ind2) for j=ind2, i=ind1 B[m-i,n-j] = A[i,j] end return B end """ rotl90(A, k) Left-rotate matrix `A` 90 degrees counterclockwise an integer `k` number of times. If `k` is a multiple of four (including zero), this is equivalent to a `copy`. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rotl90(a,1) 2ร—2 Matrix{Int64}: 2 4 1 3 julia> rotl90(a,2) 2ร—2 Matrix{Int64}: 4 3 2 1 julia> rotl90(a,3) 2ร—2 Matrix{Int64}: 3 1 4 2 julia> rotl90(a,4) 2ร—2 Matrix{Int64}: 1 2 3 4 ``` """ function rotl90(A::AbstractMatrix, k::Integer) k = mod(k, 4) k == 1 ? rotl90(A) : k == 2 ? rot180(A) : k == 3 ? rotr90(A) : copy(A) end """ rotr90(A, k) Right-rotate matrix `A` 90 degrees clockwise an integer `k` number of times. If `k` is a multiple of four (including zero), this is equivalent to a `copy`. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rotr90(a,1) 2ร—2 Matrix{Int64}: 3 1 4 2 julia> rotr90(a,2) 2ร—2 Matrix{Int64}: 4 3 2 1 julia> rotr90(a,3) 2ร—2 Matrix{Int64}: 2 4 1 3 julia> rotr90(a,4) 2ร—2 Matrix{Int64}: 1 2 3 4 ``` """ rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) """ rot180(A, k) Rotate matrix `A` 180 degrees an integer `k` number of times. If `k` is even, this is equivalent to a `copy`. # Examples ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rot180(a,1) 2ร—2 Matrix{Int64}: 4 3 2 1 julia> rot180(a,2) 2ร—2 Matrix{Int64}: 1 2 3 4 ``` """ rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/slicearray.jl๑""" AbstractSlices{S,N} <: AbstractArray{S,N} Supertype for arrays of slices into a parent array over some dimension(s), returning views that select all the data from the other dimensions. `parent` will return the parent array. """ abstract type AbstractSlices{T,N} <: AbstractArray{T,N} end """ Slices{P,SM,AX,S,N} <: AbstractSlices{S,N} An `AbstractArray` of slices into a parent array over specified dimension(s), returning views that select all the data from the other dimension(s). These should typically be constructed by [`eachslice`](@ref), [`eachcol`](@ref) or [`eachrow`](@ref). [`parent(s::Slices)`](@ref) will return the parent array. """ struct Slices{P,SM,AX,S,N} <: AbstractSlices{S,N} """ Parent array """ parent::P """ A tuple of length `ndims(parent)`, denoting how each dimension should be handled: - an integer `i`: this is the `i`th dimension of the outer `Slices` object. - `:`: an "inner" dimension """ slicemap::SM """ A tuple of length `N` containing the [`axes`](@ref) of the `Slices` object. """ axes::AX end unitaxis(::AbstractArray) = Base.OneTo(1) function Slices(A::P, slicemap::SM, ax::AX) where {P,SM,AX} N = length(ax) argT = map((a,l) -> l === (:) ? Colon : eltype(a), axes(A), slicemap) S = Base.promote_op(view, P, argT...) Slices{P,SM,AX,S,N}(A, slicemap, ax) end _slice_check_dims(N) = nothing function _slice_check_dims(N, dim, dims...) 1 <= dim <= N || throw(DimensionMismatch("Invalid dimension $dim")) dim in dims && throw(DimensionMismatch("Dimensions $dims are not unique")) _slice_check_dims(N,dims...) end @constprop :aggressive function _eachslice(A::AbstractArray{T,N}, dims::NTuple{M,Integer}, drop::Bool) where {T,N,M} _slice_check_dims(N,dims...) if drop # if N = 4, dims = (3,1) then # axes = (axes(A,3), axes(A,1)) # slicemap = (2, :, 1, :) ax = map(dim -> axes(A,dim), dims) slicemap = ntuple(dim -> something(findfirst(isequal(dim), dims), (:)), N) return Slices(A, slicemap, ax) else # if N = 4, dims = (3,1) then # axes = (axes(A,1), OneTo(1), axes(A,3), OneTo(1)) # slicemap = (1, :, 3, :) ax = ntuple(dim -> dim in dims ? axes(A,dim) : unitaxis(A), N) slicemap = ntuple(dim -> dim in dims ? dim : (:), N) return Slices(A, slicemap, ax) end end @inline function _eachslice(A::AbstractArray, dim::Integer, drop::Bool) _eachslice(A, (dim,), drop) end """ eachslice(A::AbstractArray; dims, drop=true) Create a [`Slices`](@ref) object that is an array of slices over dimensions `dims` of `A`, returning views that select all the data from the other dimensions in `A`. `dims` can either by an integer or a tuple of integers. If `drop = true` (the default), the outer `Slices` will drop the inner dimensions, and the ordering of the dimensions will match those in `dims`. If `drop = false`, then the `Slices` will have the same dimensionality as the underlying array, with inner dimensions having size 1. See [`stack`](@ref)`(slices; dims)` for the inverse of `eachslice(A; dims::Integer)`. See also [`eachrow`](@ref), [`eachcol`](@ref), [`mapslices`](@ref) and [`selectdim`](@ref). !!! compat "Julia 1.1" This function requires at least Julia 1.1. !!! compat "Julia 1.9" Prior to Julia 1.9, this returned an iterator, and only a single dimension `dims` was supported. # Example ```jldoctest julia> m = [1 2 3; 4 5 6; 7 8 9] 3ร—3 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 julia> s = eachslice(m, dims=1) 3-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}: [1, 2, 3] [4, 5, 6] [7, 8, 9] julia> s[1] 3-element view(::Matrix{Int64}, 1, :) with eltype Int64: 1 2 3 julia> eachslice(m, dims=1, drop=false) 3ร—1 Slices{Matrix{Int64}, Tuple{Int64, Colon}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}, 2}: [1, 2, 3] [4, 5, 6] [7, 8, 9] ``` """ @inline function eachslice(A; dims, drop=true) _eachslice(A, dims, drop) end """ eachrow(A::AbstractVecOrMat) <: AbstractVector Create a [`RowSlices`](@ref) object that is a vector of rows of matrix or vector `A`. Row slices are returned as `AbstractVector` views of `A`. For the inverse, see [`stack`](@ref)`(rows; dims=1)`. See also [`eachcol`](@ref), [`eachslice`](@ref) and [`mapslices`](@ref). !!! compat "Julia 1.1" This function requires at least Julia 1.1. !!! compat "Julia 1.9" Prior to Julia 1.9, this returned an iterator. # Example ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> s = eachrow(a) 2-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}: [1, 2] [3, 4] julia> s[1] 2-element view(::Matrix{Int64}, 1, :) with eltype Int64: 1 2 ``` """ eachrow(A::AbstractMatrix) = _eachslice(A, (1,), true) eachrow(A::AbstractVector) = eachrow(reshape(A, size(A,1), 1)) """ eachcol(A::AbstractVecOrMat) <: AbstractVector Create a [`ColumnSlices`](@ref) object that is a vector of columns of matrix or vector `A`. Column slices are returned as `AbstractVector` views of `A`. For the inverse, see [`stack`](@ref)`(cols)` or `reduce(`[`hcat`](@ref)`, cols)`. See also [`eachrow`](@ref), [`eachslice`](@ref) and [`mapslices`](@ref). !!! compat "Julia 1.1" This function requires at least Julia 1.1. !!! compat "Julia 1.9" Prior to Julia 1.9, this returned an iterator. # Example ```jldoctest julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> s = eachcol(a) 2-element ColumnSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, true}}: [1, 3] [2, 4] julia> s[1] 2-element view(::Matrix{Int64}, :, 1) with eltype Int64: 1 3 ``` """ eachcol(A::AbstractMatrix) = _eachslice(A, (2,), true) eachcol(A::AbstractVector) = eachcol(reshape(A, size(A, 1), 1)) """ RowSlices{M,AX,S} A special case of [`Slices`](@ref) that is a vector of row slices of a matrix, as constructed by [`eachrow`](@ref). [`parent`](@ref) can be used to get the underlying matrix. """ const RowSlices{P<:AbstractMatrix,AX,S<:AbstractVector} = Slices{P,Tuple{Int,Colon},AX,S,1} """ ColumnSlices{M,AX,S} A special case of [`Slices`](@ref) that is a vector of column slices of a matrix, as constructed by [`eachcol`](@ref). [`parent`](@ref) can be used to get the underlying matrix. """ const ColumnSlices{P<:AbstractMatrix,AX,S<:AbstractVector} = Slices{P,Tuple{Colon,Int},AX,S,1} IteratorSize(::Type{Slices{P,SM,AX,S,N}}) where {P,SM,AX,S,N} = HasShape{N}() axes(s::Slices) = s.axes size(s::Slices) = map(length, s.axes) @inline function _slice_index(s::Slices, c...) return map(l -> l === (:) ? (:) : c[l], s.slicemap) end @inline function getindex(s::Slices{P,SM,AX,S,N}, I::Vararg{Int,N}) where {P,SM,AX,S,N} @boundscheck checkbounds(s, I...) @inbounds view(s.parent, _slice_index(s, I...)...) end @inline function setindex!(s::Slices{P,SM,AX,S,N}, val, I::Vararg{Int,N}) where {P,SM,AX,S,N} @boundscheck checkbounds(s, I...) @inbounds s.parent[_slice_index(s, I...)...] = val end parent(s::Slices) = s.parent O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/simdloop.jlศ# This file is a part of Julia. License is MIT: https://julialang.org/license # Support for @simd for module SimdLoop export @simd, simd_outer_range, simd_inner_length, simd_index # Error thrown from ill-formed uses of @simd struct SimdError <: Exception msg::String end # Parse iteration space expression # symbol '=' range # symbol 'in' range function parse_iteration_space(x) (isa(x, Expr) && (x.head === :(=) || x.head === :in)) || throw(SimdError("= or in expected")) length(x.args) == 2 || throw(SimdError("simd range syntax is wrong")) isa(x.args[1], Symbol) || throw(SimdError("simd loop index must be a symbol")) x.args # symbol, range end # reject invalid control flow statements in @simd loop body function check_body!(x::Expr) if x.head === :break || x.head === :continue throw(SimdError("$(x.head) is not allowed inside a @simd loop body")) elseif x.head === :macrocall && x.args[1] === Symbol("@goto") throw(SimdError("@goto is not allowed inside a @simd loop body")) end for arg in x.args check_body!(arg) end return true end check_body!(x::QuoteNode) = check_body!(x.value) check_body!(x) = true # @simd splits a for loop into two loops: an outer scalar loop and # an inner loop marked with :loopinfo. The simd_... functions define # the splitting. # Custom iterators that do not support random access cannot support # vectorization. In order to be compatible with `@simd` annotated loops, #they should override `simd_inner_length(v::MyIter, j) = 1`, #`simd_outer_range(v::MyIter) = v`, and `simd_index(v::MyIter, j, i) = j`. # Get range for outer loop. simd_outer_range(r) = 0:0 # Get trip count for inner loop. @inline simd_inner_length(r, j) = Base.length(r) # Construct user-level element from original range, outer loop index j, and inner loop index i. @inline simd_index(r, j, i) = (@inbounds ret = r[i+firstindex(r)]; ret) # Compile Expr x in context of @simd. function compile(x, ivdep) (isa(x, Expr) && x.head === :for) || throw(SimdError("for loop expected")) length(x.args) == 2 || throw(SimdError("1D for loop expected")) check_body!(x) var,range = parse_iteration_space(x.args[1]) r = gensym("r") # Range value j = gensym("i") # Iteration variable for outer loop n = gensym("n") # Trip count for inner loop i = gensym("i") # Trip index for inner loop quote # Evaluate range value once, to enhance type and data flow analysis by optimizers. let $r = $range for $j in Base.simd_outer_range($r) let $n = Base.simd_inner_length($r,$j) if zero($n) < $n # Lower loop in way that seems to work best for LLVM 3.3 vectorizer. let $i = zero($n) while $i < $n local $var = Base.simd_index($r,$j,$i) $(x.args[2]) # Body of loop $i += 1 $(Expr(:loopinfo, Symbol("julia.simdloop"), ivdep)) # Mark loop as SIMD loop end end end end end end nothing end end """ @simd Annotate a `for` loop to allow the compiler to take extra liberties to allow loop re-ordering !!! warning This feature is experimental and could change or disappear in future versions of Julia. Incorrect use of the `@simd` macro may cause unexpected results. The object iterated over in a `@simd for` loop should be a one-dimensional range. By using `@simd`, you are asserting several properties of the loop: * It is safe to execute iterations in arbitrary or overlapping order, with special consideration for reduction variables. * Floating-point operations on reduction variables can be reordered or contracted, possibly causing different results than without `@simd`. In many cases, Julia is able to automatically vectorize inner for loops without the use of `@simd`. Using `@simd` gives the compiler a little extra leeway to make it possible in more situations. In either case, your inner loop should have the following properties to allow vectorization: * The loop must be an innermost loop * The loop body must be straight-line code. Therefore, [`@inbounds`](@ref) is currently needed for all array accesses. The compiler can sometimes turn short `&&`, `||`, and `?:` expressions into straight-line code if it is safe to evaluate all operands unconditionally. Consider using the [`ifelse`](@ref) function instead of `?:` in the loop if it is safe to do so. * Accesses must have a stride pattern and cannot be "gathers" (random-index reads) or "scatters" (random-index writes). * The stride should be unit stride. !!! note The `@simd` does not assert by default that the loop is completely free of loop-carried memory dependencies, which is an assumption that can easily be violated in generic code. If you are writing non-generic code, you can use `@simd ivdep for ... end` to also assert that: * There exists no loop-carried memory dependencies * No iteration ever waits on a previous iteration to make forward progress. """ macro simd(forloop) esc(compile(forloop, nothing)) end macro simd(ivdep, forloop) if ivdep === :ivdep esc(compile(forloop, Symbol("julia.ivdep"))) else throw(SimdError("Only ivdep is valid as the first argument to @simd")) end end end # module SimdLoop M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/reduce.jlTš# This file is a part of Julia. License is MIT: https://julialang.org/license ## reductions ## ###### Generic (map)reduce functions ###### if Int === Int32 const SmallSigned = Union{Int8,Int16} const SmallUnsigned = Union{UInt8,UInt16} else const SmallSigned = Union{Int8,Int16,Int32} const SmallUnsigned = Union{UInt8,UInt16,UInt32} end abstract type AbstractBroadcasted end const AbstractArrayOrBroadcasted = Union{AbstractArray, AbstractBroadcasted} """ Base.add_sum(x, y) The reduction operator used in `sum`. The main difference from [`+`](@ref) is that small integers are promoted to `Int`/`UInt`. """ add_sum(x, y) = x + y add_sum(x::SmallSigned, y::SmallSigned) = Int(x) + Int(y) add_sum(x::SmallUnsigned, y::SmallUnsigned) = UInt(x) + UInt(y) add_sum(x::Real, y::Real)::Real = x + y """ Base.mul_prod(x, y) The reduction operator used in `prod`. The main difference from [`*`](@ref) is that small integers are promoted to `Int`/`UInt`. """ mul_prod(x, y) = x * y mul_prod(x::SmallSigned, y::SmallSigned) = Int(x) * Int(y) mul_prod(x::SmallUnsigned, y::SmallUnsigned) = UInt(x) * UInt(y) mul_prod(x::Real, y::Real)::Real = x * y ## foldl && mapfoldl function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP} opโ€ฒ, itrโ€ฒ = _xfadjoint(BottomRF(op), Generator(f, itr)) return foldl_impl(opโ€ฒ, nt, itrโ€ฒ) end function foldl_impl(op::OP, nt, itr) where {OP} v = _foldl_impl(op, nt, itr) v isa _InitialValue && return reduce_empty_iter(op, itr) return v end function _foldl_impl(op::OP, init, itr) where {OP} # Unroll the while loop once; if init is known, the call to op may # be evaluated at compile time y = iterate(itr) y === nothing && return init v = op(init, y[1]) while true y = iterate(itr, y[2]) y === nothing && break v = op(v, y[1]) end return v end function _foldl_impl(op, init, itr::Union{Tuple,NamedTuple}) length(itr) <= 32 && return afoldl(op, init, itr...) @invoke _foldl_impl(op, init, itr::Any) end struct _InitialValue end """ BottomRF(rf) -> rfโ€ฒ "Bottom" reducing function. This is a thin wrapper around the `op` argument passed to `foldl`-like functions for handling the initial invocation to call [`reduce_first`](@ref). """ struct BottomRF{T} rf::T end @inline (op::BottomRF)(::_InitialValue, x) = reduce_first(op.rf, x) @inline (op::BottomRF)(acc, x) = op.rf(acc, x) """ MappingRF(f, rf) -> rfโ€ฒ Create a mapping reducing function `rfโ€ฒ(acc, x) = rf(acc, f(x))`. """ struct MappingRF{F, T} f::F rf::T MappingRF(f::F, rf::T) where {F,T} = new{F,T}(f, rf) MappingRF(::Type{f}, rf::T) where {f,T} = new{Type{f},T}(f, rf) end @inline (op::MappingRF)(acc, x) = op.rf(acc, op.f(x)) """ FilteringRF(f, rf) -> rfโ€ฒ Create a filtering reducing function `rfโ€ฒ(acc, x) = f(x) ? rf(acc, x) : acc`. """ struct FilteringRF{F, T} f::F rf::T end @inline (op::FilteringRF)(acc, x) = op.f(x) ? op.rf(acc, x) : acc """ FlatteningRF(rf) -> rfโ€ฒ Create a flattening reducing function that is roughly equivalent to `rfโ€ฒ(acc, x) = foldl(rf, x; init=acc)`. """ struct FlatteningRF{T} rf::T end @inline function (op::FlatteningRF)(acc, x) opโ€ฒ, itrโ€ฒ = _xfadjoint(op.rf, x) return _foldl_impl(opโ€ฒ, acc, itrโ€ฒ) end """ _xfadjoint(op, itr) -> opโ€ฒ, itrโ€ฒ Given a pair of reducing function `op` and an iterator `itr`, return a pair `(opโ€ฒ, itrโ€ฒ)` of similar types. If the iterator `itr` is transformed by an iterator transform `ixf` whose adjoint transducer `xf` is known, `opโ€ฒ = xf(op)` and `itrโ€ฒ = ixfโปยน(itr)` is returned. Otherwise, `op` and `itr` are returned as-is. For example, transducer `rf -> MappingRF(f, rf)` is the adjoint of iterator transform `itr -> Generator(f, itr)`. Nested iterator transforms are converted recursively. That is to say, given `op` and itr = (ixfโ‚ โˆ˜ ixfโ‚‚ โˆ˜ ... โˆ˜ ixfโ‚™)(itrโ€ฒ) what is returned is `itrโ€ฒ` and opโ€ฒ = (xfโ‚™ โˆ˜ ... โˆ˜ xfโ‚‚ โˆ˜ xfโ‚)(op) """ function _xfadjoint(op, itr) itrโ€ฒ, wrap = _xfadjoint_unwrap(itr) wrap(op), itrโ€ฒ end _xfadjoint_unwrap(itr) = itr, identity function _xfadjoint_unwrap(itr::Generator) itrโ€ฒ, wrap = _xfadjoint_unwrap(itr.iter) itr.f === identity && return itrโ€ฒ, wrap return itrโ€ฒ, wrap โˆ˜ Fix1(MappingRF, itr.f) end function _xfadjoint_unwrap(itr::Filter) itrโ€ฒ, wrap = _xfadjoint_unwrap(itr.itr) return itrโ€ฒ, wrap โˆ˜ Fix1(FilteringRF, itr.flt) end function _xfadjoint_unwrap(itr::Flatten) itrโ€ฒ, wrap = _xfadjoint_unwrap(itr.it) return itrโ€ฒ, wrap โˆ˜ FlatteningRF end """ mapfoldl(f, op, itr; [init]) Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`](@ref). If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ mapfoldl(f, op, itr; init=_InitialValue()) = mapfoldl_impl(f, op, init, itr) """ foldl(op, itr; [init]) Like [`reduce`](@ref), but with guaranteed left associativity. If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. See also [`mapfoldl`](@ref), [`foldr`](@ref), [`accumulate`](@ref). # Examples ```jldoctest julia> foldl(=>, 1:4) ((1 => 2) => 3) => 4 julia> foldl(=>, 1:4; init=0) (((0 => 1) => 2) => 3) => 4 julia> accumulate(=>, (1,2,3,4)) (1, 1 => 2, (1 => 2) => 3, ((1 => 2) => 3) => 4) ``` """ foldl(op, itr; kw...) = mapfoldl(identity, op, itr; kw...) ## foldr & mapfoldr function mapfoldr_impl(f, op, nt, itr) opโ€ฒ, itrโ€ฒ = _xfadjoint(BottomRF(FlipArgs(op)), Generator(f, itr)) return foldl_impl(opโ€ฒ, nt, _reverse_iter(itrโ€ฒ)) end _reverse_iter(itr) = Iterators.reverse(itr) _reverse_iter(itr::Union{Tuple,NamedTuple}) = length(itr) <= 32 ? reverse(itr) : Iterators.reverse(itr) #33235 struct FlipArgs{F} f::F end @inline (f::FlipArgs)(x, y) = f.f(y, x) """ mapfoldr(f, op, itr; [init]) Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr`](@ref). If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. """ mapfoldr(f, op, itr; init=_InitialValue()) = mapfoldr_impl(f, op, init, itr) """ foldr(op, itr; [init]) Like [`reduce`](@ref), but with guaranteed right associativity. If provided, the keyword argument `init` will be used exactly once. In general, it will be necessary to provide `init` to work with empty collections. # Examples ```jldoctest julia> foldr(=>, 1:4) 1 => (2 => (3 => 4)) julia> foldr(=>, 1:4; init=0) 1 => (2 => (3 => (4 => 0))) ``` """ foldr(op, itr; kw...) = mapfoldr(identity, op, itr; kw...) ## reduce & mapreduce # `mapreduce_impl()` is called by `mapreduce()` (via `_mapreduce()`, when `A` # supports linear indexing) and does actual calculations (for `A[ifirst:ilast]` subset). # For efficiency, no parameter validity checks are done, it's the caller's responsibility. # `ifirst:ilast` range is assumed to be a valid non-empty subset of `A` indices. # This is a generic implementation of `mapreduce_impl()`, # certain `op` (e.g. `min` and `max`) may have their own specialized versions. @noinline function mapreduce_impl(f, op, A::AbstractArrayOrBroadcasted, ifirst::Integer, ilast::Integer, blksize::Int) if ifirst == ilast @inbounds a1 = A[ifirst] return mapreduce_first(f, op, a1) elseif ilast - ifirst < blksize # sequential portion @inbounds a1 = A[ifirst] @inbounds a2 = A[ifirst+1] v = op(f(a1), f(a2)) @simd for i = ifirst + 2 : ilast @inbounds ai = A[i] v = op(v, f(ai)) end return v else # pairwise portion imid = ifirst + (ilast - ifirst) >> 1 v1 = mapreduce_impl(f, op, A, ifirst, imid, blksize) v2 = mapreduce_impl(f, op, A, imid+1, ilast, blksize) return op(v1, v2) end end mapreduce_impl(f, op, A::AbstractArrayOrBroadcasted, ifirst::Integer, ilast::Integer) = mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) """ mapreduce(f, op, itrs...; [init]) Apply function `f` to each element(s) in `itrs`, and then reduce the result using the binary function `op`. If provided, `init` must be a neutral element for `op` that will be returned for empty collections. It is unspecified whether `init` is used for non-empty collections. In general, it will be necessary to provide `init` to work with empty collections. [`mapreduce`](@ref) is functionally equivalent to calling `reduce(op, map(f, itr); init=init)`, but will in general execute faster since no intermediate collection needs to be created. See documentation for [`reduce`](@ref) and [`map`](@ref). !!! compat "Julia 1.2" `mapreduce` with multiple iterators requires Julia 1.2 or later. # Examples ```jldoctest julia> mapreduce(x->x^2, +, [1:3;]) # == 1 + 4 + 9 14 ``` The associativity of the reduction is implementation-dependent. Additionally, some implementations may reuse the return value of `f` for elements that appear multiple times in `itr`. Use [`mapfoldl`](@ref) or [`mapfoldr`](@ref) instead for guaranteed left or right associativity and invocation of `f` for every value. """ mapreduce(f, op, itr; kw...) = mapfoldl(f, op, itr; kw...) mapreduce(f, op, itrs...; kw...) = reduce(op, Generator(f, itrs...); kw...) # Note: sum_seq usually uses four or more accumulators after partial # unrolling, so each accumulator gets at most 256 numbers pairwise_blocksize(f, op) = 1024 # This combination appears to show a benefit from a larger block size pairwise_blocksize(::typeof(abs2), ::typeof(+)) = 4096 # handling empty arrays _empty_reduce_error() = throw(ArgumentError("reducing over an empty collection is not allowed")) _empty_reduce_error(@nospecialize(f), @nospecialize(T::Type)) = throw(ArgumentError(""" reducing with $f over an empty collection of element type $T is not allowed. You may be able to prevent this error by supplying an `init` value to the reducer.""")) """ Base.reduce_empty(op, T) The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref) or [`foldr`](@ref) with reduction `op` over an empty array with element type of `T`. This should only be defined in unambiguous cases; for example, ```julia Base.reduce_empty(::typeof(+), ::Type{T}) where T = zero(T) ``` is justified (the sum of zero elements is zero), whereas `reduce_empty(::typeof(max), ::Type{Any})` is not (the maximum value of an empty collection is generally ambiguous, and especially so when the element type is unknown). As an alternative, consider supplying an `init` value to the reducer. """ reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error(+, Union{}) reduce_empty(::typeof(+), ::Type{T}) where {T} = zero(T) reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error(*, Union{}) reduce_empty(::typeof(*), ::Type{T}) where {T} = one(T) reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = "" reduce_empty(::typeof(&), ::Type{Bool}) = true reduce_empty(::typeof(|), ::Type{Bool}) = false reduce_empty(::typeof(add_sum), ::Type{Union{}}) = _empty_reduce_error(add_sum, Union{}) reduce_empty(::typeof(add_sum), ::Type{T}) where {T} = reduce_empty(+, T) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallSigned} = zero(Int) reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallUnsigned} = zero(UInt) reduce_empty(::typeof(mul_prod), ::Type{Union{}}) = _empty_reduce_error(mul_prod, Union{}) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T} = reduce_empty(*, T) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallSigned} = one(Int) reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallUnsigned} = one(UInt) reduce_empty(op::BottomRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) reduce_empty(op::MappingRF, ::Type{T}) where {T} = mapreduce_empty(op.f, op.rf, T) reduce_empty(op::FilteringRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) reduce_empty(op::FlipArgs, ::Type{T}) where {T} = reduce_empty(op.f, T) """ Base.mapreduce_empty(f, op, T) The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or [`mapfoldr`](@ref) with map `f` and reduction `op` over an empty array with element type of `T`. See [`Base.reduce_empty`](@ref) for more information. """ mapreduce_empty(::typeof(identity), op, T) = reduce_empty(op, T) mapreduce_empty(::typeof(abs), op, T) = abs(reduce_empty(op, T)) mapreduce_empty(::typeof(abs2), op, T) = abs2(reduce_empty(op, T)) mapreduce_empty(f::typeof(abs), ::typeof(max), T) = abs(zero(T)) mapreduce_empty(f::typeof(abs2), ::typeof(max), T) = abs2(zero(T)) # For backward compatibility: mapreduce_empty_iter(f, op, itr, ItrEltype) = reduce_empty_iter(MappingRF(f, op), itr, ItrEltype) @inline reduce_empty_iter(op, itr) = reduce_empty_iter(op, itr, IteratorEltype(itr)) @inline reduce_empty_iter(op, itr, ::HasEltype) = reduce_empty(op, eltype(itr)) reduce_empty_iter(op, itr, ::EltypeUnknown) = throw(ArgumentError(""" reducing over an empty collection of unknown element type is not allowed. You may be able to prevent this error by supplying an `init` value to the reducer.""")) # handling of single-element iterators """ Base.reduce_first(op, x) The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref`) or [`foldr`](@ref) with reduction `op` over an iterator which contains a single element `x`. This value may also be used to initialise the recursion, so that `reduce(op, [x, y])` may call `op(reduce_first(op, x), y)`. The default is `x` for most types. The main purpose is to ensure type stability, so additional methods should only be defined for cases where `op` gives a result with different types than its inputs. """ reduce_first(op, x) = x reduce_first(::typeof(+), x::Bool) = Int(x) reduce_first(::typeof(*), x::AbstractChar) = string(x) reduce_first(::typeof(add_sum), x) = reduce_first(+, x) reduce_first(::typeof(add_sum), x::SmallSigned) = Int(x) reduce_first(::typeof(add_sum), x::SmallUnsigned) = UInt(x) reduce_first(::typeof(mul_prod), x) = reduce_first(*, x) reduce_first(::typeof(mul_prod), x::SmallSigned) = Int(x) reduce_first(::typeof(mul_prod), x::SmallUnsigned) = UInt(x) """ Base.mapreduce_first(f, op, x) The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or [`mapfoldr`](@ref) with map `f` and reduction `op` over an iterator which contains a single element `x`. This value may also be used to initialise the recursion, so that `mapreduce(f, op, [x, y])` may call `op(mapreduce_first(f, op, x), f(y))`. The default is `reduce_first(op, f(x))`. """ mapreduce_first(f, op, x) = reduce_first(op, f(x)) _mapreduce(f, op, A::AbstractArrayOrBroadcasted) = _mapreduce(f, op, IndexStyle(A), A) function _mapreduce(f, op, ::IndexLinear, A::AbstractArrayOrBroadcasted) inds = LinearIndices(A) n = length(inds) if n == 0 return mapreduce_empty_iter(f, op, A, IteratorEltype(A)) elseif n == 1 @inbounds a1 = A[first(inds)] return mapreduce_first(f, op, a1) elseif n < 16 # process short array here, avoid mapreduce_impl() compilation @inbounds i = first(inds) @inbounds a1 = A[i] @inbounds a2 = A[i+=1] s = op(f(a1), f(a2)) while i < last(inds) @inbounds Ai = A[i+=1] s = op(s, f(Ai)) end return s else return mapreduce_impl(f, op, A, first(inds), last(inds)) end end mapreduce(f, op, a::Number) = mapreduce_first(f, op, a) _mapreduce(f, op, ::IndexCartesian, A::AbstractArrayOrBroadcasted) = mapfoldl(f, op, A) """ reduce(op, itr; [init]) Reduce the given collection `itr` with the given binary operator `op`. If provided, the initial value `init` must be a neutral element for `op` that will be returned for empty collections. It is unspecified whether `init` is used for non-empty collections. For empty collections, providing `init` will be necessary, except for some special cases (e.g. when `op` is one of `+`, `*`, `max`, `min`, `&`, `|`) when Julia can determine the neutral element of `op`. Reductions for certain commonly-used operators may have special implementations, and should be used instead: [`maximum`](@ref)`(itr)`, [`minimum`](@ref)`(itr)`, [`sum`](@ref)`(itr)`, [`prod`](@ref)`(itr)`, [`any`](@ref)`(itr)`, [`all`](@ref)`(itr)`. There are efficient methods for concatenating certain arrays of arrays by calling `reduce(`[`vcat`](@ref)`, arr)` or `reduce(`[`hcat`](@ref)`, arr)`. The associativity of the reduction is implementation dependent. This means that you can't use non-associative operations like `-` because it is undefined whether `reduce(-,[1,2,3])` should be evaluated as `(1-2)-3` or `1-(2-3)`. Use [`foldl`](@ref) or [`foldr`](@ref) instead for guaranteed left or right associativity. Some operations accumulate error. Parallelism will be easier if the reduction can be executed in groups. Future versions of Julia might change the algorithm. Note that the elements are not reordered if you use an ordered collection. # Examples ```jldoctest julia> reduce(*, [2; 3; 4]) 24 julia> reduce(*, [2; 3; 4]; init=-1) -24 ``` """ reduce(op, itr; kw...) = mapreduce(identity, op, itr; kw...) reduce(op, a::Number) = a # Do we want this? ###### Specific reduction functions ###### ## sum """ sum(f, itr; [init]) Sum the results of calling function `f` on each element of `itr`. The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. The value returned for empty `itr` can be specified by `init`. It must be the additive identity (i.e. zero) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> sum(abs2, [2; 3; 4]) 29 ``` Note the important difference between `sum(A)` and `reduce(+, A)` for arrays with small integer eltype: ```jldoctest julia> sum(Int8[100, 28]) 128 julia> reduce(+, Int8[100, 28]) -128 ``` In the former case, the integers are widened to system word size and therefore the result is 128. In the latter case, no such widening happens and integer overflow results in -128. """ sum(f, a; kw...) = mapreduce(f, add_sum, a; kw...) """ sum(itr; [init]) Return the sum of all elements in a collection. The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. The value returned for empty `itr` can be specified by `init`. It must be the additive identity (i.e. zero) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. See also: [`reduce`](@ref), [`mapreduce`](@ref), [`count`](@ref), [`union`](@ref). # Examples ```jldoctest julia> sum(1:20) 210 julia> sum(1:20; init = 0.0) 210.0 ``` """ sum(a; kw...) = sum(identity, a; kw...) sum(a::AbstractArray{Bool}; kw...) = isempty(kw) ? count(a) : reduce(add_sum, a; kw...) ## prod """ prod(f, itr; [init]) Return the product of `f` applied to each element of `itr`. The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. The value returned for empty `itr` can be specified by `init`. It must be the multiplicative identity (i.e. one) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> prod(abs2, [2; 3; 4]) 576 ``` """ prod(f, a; kw...) = mapreduce(f, mul_prod, a; kw...) """ prod(itr; [init]) Return the product of all elements of a collection. The return type is `Int` for signed integers of less than system word size, and `UInt` for unsigned integers of less than system word size. For all other arguments, a common return type is found to which all arguments are promoted. The value returned for empty `itr` can be specified by `init`. It must be the multiplicative identity (i.e. one) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. See also: [`reduce`](@ref), [`cumprod`](@ref), [`any`](@ref). # Examples ```jldoctest julia> prod(1:5) 120 julia> prod(1:5; init = 1.0) 120.0 ``` """ prod(a; kw...) = mapreduce(identity, mul_prod, a; kw...) ## maximum, minimum, & extrema _fast(::typeof(min),x,y) = min(x,y) _fast(::typeof(max),x,y) = max(x,y) function _fast(::typeof(max), x::AbstractFloat, y::AbstractFloat) ifelse(isnan(x), x, ifelse(x > y, x, y)) end function _fast(::typeof(min),x::AbstractFloat, y::AbstractFloat) ifelse(isnan(x), x, ifelse(x < y, x, y)) end isbadzero(::typeof(max), x::AbstractFloat) = (x == zero(x)) & signbit(x) isbadzero(::typeof(min), x::AbstractFloat) = (x == zero(x)) & !signbit(x) isbadzero(op, x) = false isgoodzero(::typeof(max), x) = isbadzero(min, x) isgoodzero(::typeof(min), x) = isbadzero(max, x) function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, A::AbstractArrayOrBroadcasted, first::Int, last::Int) a1 = @inbounds A[first] v1 = mapreduce_first(f, op, a1) v2 = v3 = v4 = v1 chunk_len = 256 start = first + 1 simdstop = start + chunk_len - 4 while simdstop <= last - 3 @inbounds for i in start:4:simdstop v1 = _fast(op, v1, f(A[i+0])) v2 = _fast(op, v2, f(A[i+1])) v3 = _fast(op, v3, f(A[i+2])) v4 = _fast(op, v4, f(A[i+3])) end checkbounds(A, simdstop+3) start += chunk_len simdstop += chunk_len end v = op(op(v1,v2),op(v3,v4)) for i in start:last @inbounds ai = A[i] v = op(v, f(ai)) end # enforce correct order of 0.0 and -0.0 # e.g. maximum([0.0, -0.0]) === 0.0 # should hold if isbadzero(op, v) for i in first:last x = @inbounds A[i] isgoodzero(op,x) && return x end end return v end """ maximum(f, itr; [init]) Return the largest result of calling function `f` on each element of `itr`. The value returned for empty `itr` can be specified by `init`. It must be a neutral element for `max` (i.e. which is less than or equal to any other element) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> maximum(length, ["Julion", "Julia", "Jule"]) 6 julia> maximum(length, []; init=-1) -1 julia> maximum(sin, Real[]; init=-1.0) # good, since output of sin is >= -1 -1.0 ``` """ maximum(f, a; kw...) = mapreduce(f, max, a; kw...) """ minimum(f, itr; [init]) Return the smallest result of calling function `f` on each element of `itr`. The value returned for empty `itr` can be specified by `init`. It must be a neutral element for `min` (i.e. which is greater than or equal to any other element) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> minimum(length, ["Julion", "Julia", "Jule"]) 4 julia> minimum(length, []; init=typemax(Int64)) 9223372036854775807 julia> minimum(sin, Real[]; init=1.0) # good, since output of sin is <= 1 1.0 ``` """ minimum(f, a; kw...) = mapreduce(f, min, a; kw...) """ maximum(itr; [init]) Return the largest element in a collection. The value returned for empty `itr` can be specified by `init`. It must be a neutral element for `max` (i.e. which is less than or equal to any other element) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> maximum(-20.5:10) 9.5 julia> maximum([1,2,3]) 3 julia> maximum(()) ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer Stacktrace: [...] julia> maximum((); init=-Inf) -Inf ``` """ maximum(a; kw...) = mapreduce(identity, max, a; kw...) """ minimum(itr; [init]) Return the smallest element in a collection. The value returned for empty `itr` can be specified by `init`. It must be a neutral element for `min` (i.e. which is greater than or equal to any other element) as it is unspecified whether `init` is used for non-empty collections. !!! compat "Julia 1.6" Keyword argument `init` requires Julia 1.6 or later. # Examples ```jldoctest julia> minimum(-20.5:10) -20.5 julia> minimum([1,2,3]) 1 julia> minimum([]) ERROR: MethodError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer Stacktrace: [...] julia> minimum([]; init=Inf) Inf ``` """ minimum(a; kw...) = mapreduce(identity, min, a; kw...) """ extrema(itr; [init]) -> (mn, mx) Compute both the minimum `mn` and maximum `mx` element in a single pass, and return them as a 2-tuple. The value returned for empty `itr` can be specified by `init`. It must be a 2-tuple whose first and second elements are neutral elements for `min` and `max` respectively (i.e. which are greater/less than or equal to any other element). As a consequence, when `itr` is empty the returned `(mn, mx)` tuple will satisfy `mn โ‰ฅ mx`. When `init` is specified it may be used even for non-empty `itr`. !!! compat "Julia 1.8" Keyword argument `init` requires Julia 1.8 or later. # Examples ```jldoctest julia> extrema(2:10) (2, 10) julia> extrema([9,pi,4.5]) (3.141592653589793, 9.0) julia> extrema([]; init = (Inf, -Inf)) (Inf, -Inf) ``` """ extrema(itr; kw...) = extrema(identity, itr; kw...) """ extrema(f, itr; [init]) -> (mn, mx) Compute both the minimum `mn` and maximum `mx` of `f` applied to each element in `itr` and return them as a 2-tuple. Only one pass is made over `itr`. The value returned for empty `itr` can be specified by `init`. It must be a 2-tuple whose first and second elements are neutral elements for `min` and `max` respectively (i.e. which are greater/less than or equal to any other element). It is used for non-empty collections. Note: it implies that, for empty `itr`, the returned value `(mn, mx)` satisfies `mn โ‰ฅ mx` even though for non-empty `itr` it satisfies `mn โ‰ค mx`. This is a "paradoxical" but yet expected result. !!! compat "Julia 1.2" This method requires Julia 1.2 or later. !!! compat "Julia 1.8" Keyword argument `init` requires Julia 1.8 or later. # Examples ```jldoctest julia> extrema(sin, 0:ฯ€) (0.0, 0.9092974268256817) julia> extrema(sin, Real[]; init = (1.0, -1.0)) # good, since -1 โ‰ค sin(::Real) โ‰ค 1 (1.0, -1.0) ``` """ extrema(f, itr; kw...) = mapreduce(ExtremaMap(f), _extrema_rf, itr; kw...) # Not using closure since `extrema(type, itr)` is a very likely use-case and it's better # to avoid type-instability (#23618). struct ExtremaMap{F} <: Function f::F end ExtremaMap(::Type{T}) where {T} = ExtremaMap{Type{T}}(T) @inline (f::ExtremaMap)(x) = (y = f.f(x); (y, y)) @inline _extrema_rf((min1, max1), (min2, max2)) = (min(min1, min2), max(max1, max2)) # optimization for IEEEFloat function _extrema_rf(x::NTuple{2,T}, y::NTuple{2,T}) where {T<:IEEEFloat} (x1, x2), (y1, y2) = x, y anynan = isnan(x1)|isnan(y1) z1 = ifelse(anynan, x1-y1, ifelse(signbit(x1-y1), x1, y1)) z2 = ifelse(anynan, x1-y1, ifelse(signbit(x2-y2), y2, x2)) z1, z2 end ## findmax, findmin, argmax & argmin """ findmax(f, domain) -> (f(x), index) Return a pair of a value in the codomain (outputs of `f`) and the index of the corresponding value in the `domain` (inputs to `f`) such that `f(x)` is maximised. If there are multiple maximal points, then the first one will be returned. `domain` must be a non-empty iterable. Values are compared with `isless`. !!! compat "Julia 1.7" This method requires Julia 1.7 or later. # Examples ```jldoctest julia> findmax(identity, 5:9) (9, 5) julia> findmax(-, 1:10) (-1, 1) julia> findmax(first, [(1, :a), (3, :b), (3, :c)]) (3, 2) julia> findmax(cos, 0:ฯ€/2:2ฯ€) (1.0, 1) ``` """ findmax(f, domain) = _findmax(f, domain, :) _findmax(f, domain, ::Colon) = mapfoldl( ((k, v),) -> (f(v), k), _rf_findmax, pairs(domain) ) _rf_findmax((fm, im), (fx, ix)) = isless(fm, fx) ? (fx, ix) : (fm, im) """ findmax(itr) -> (x, index) Return the maximal element of the collection `itr` and its index or key. If there are multiple maximal elements, then the first one will be returned. Values are compared with `isless`. See also: [`findmin`](@ref), [`argmax`](@ref), [`maximum`](@ref). # Examples ```jldoctest julia> findmax([8, 0.1, -9, pi]) (8.0, 1) julia> findmax([1, 7, 7, 6]) (7, 2) julia> findmax([1, 7, 7, NaN]) (NaN, 4) ``` """ findmax(itr) = _findmax(itr, :) _findmax(a, ::Colon) = findmax(identity, a) """ findmin(f, domain) -> (f(x), index) Return a pair of a value in the codomain (outputs of `f`) and the index of the corresponding value in the `domain` (inputs to `f`) such that `f(x)` is minimised. If there are multiple minimal points, then the first one will be returned. `domain` must be a non-empty iterable. `NaN` is treated as less than all other values except `missing`. !!! compat "Julia 1.7" This method requires Julia 1.7 or later. # Examples ```jldoctest julia> findmin(identity, 5:9) (5, 1) julia> findmin(-, 1:10) (-10, 10) julia> findmin(first, [(2, :a), (2, :b), (3, :c)]) (2, 1) julia> findmin(cos, 0:ฯ€/2:2ฯ€) (-1.0, 3) ``` """ findmin(f, domain) = _findmin(f, domain, :) _findmin(f, domain, ::Colon) = mapfoldl( ((k, v),) -> (f(v), k), _rf_findmin, pairs(domain) ) _rf_findmin((fm, im), (fx, ix)) = isgreater(fm, fx) ? (fx, ix) : (fm, im) """ findmin(itr) -> (x, index) Return the minimal element of the collection `itr` and its index or key. If there are multiple minimal elements, then the first one will be returned. `NaN` is treated as less than all other values except `missing`. See also: [`findmax`](@ref), [`argmin`](@ref), [`minimum`](@ref). # Examples ```jldoctest julia> findmin([8, 0.1, -9, pi]) (-9.0, 3) julia> findmin([1, 7, 7, 6]) (1, 1) julia> findmin([1, 7, 7, NaN]) (NaN, 4) ``` """ findmin(itr) = _findmin(itr, :) _findmin(a, ::Colon) = findmin(identity, a) """ argmax(f, domain) Return a value `x` from `domain` for which `f(x)` is maximised. If there are multiple maximal values for `f(x)` then the first one will be found. `domain` must be a non-empty iterable. Values are compared with `isless`. !!! compat "Julia 1.7" This method requires Julia 1.7 or later. See also [`argmin`](@ref), [`findmax`](@ref). # Examples ```jldoctest julia> argmax(abs, -10:5) -10 julia> argmax(cos, 0:ฯ€/2:2ฯ€) 0.0 ``` """ argmax(f, domain) = mapfoldl(x -> (f(x), x), _rf_findmax, domain)[2] """ argmax(itr) Return the index or key of the maximal element in a collection. If there are multiple maximal elements, then the first one will be returned. The collection must not be empty. Values are compared with `isless`. See also: [`argmin`](@ref), [`findmax`](@ref). # Examples ```jldoctest julia> argmax([8, 0.1, -9, pi]) 1 julia> argmax([1, 7, 7, 6]) 2 julia> argmax([1, 7, 7, NaN]) 4 ``` """ argmax(itr) = findmax(itr)[2] """ argmin(f, domain) Return a value `x` from `domain` for which `f(x)` is minimised. If there are multiple minimal values for `f(x)` then the first one will be found. `domain` must be a non-empty iterable. `NaN` is treated as less than all other values except `missing`. !!! compat "Julia 1.7" This method requires Julia 1.7 or later. See also [`argmax`](@ref), [`findmin`](@ref). # Examples ```jldoctest julia> argmin(sign, -10:5) -10 julia> argmin(x -> -x^3 + x^2 - 10, -5:5) 5 julia> argmin(acos, 0:0.1:1) 1.0 ``` """ argmin(f, domain) = mapfoldl(x -> (f(x), x), _rf_findmin, domain)[2] """ argmin(itr) Return the index or key of the minimal element in a collection. If there are multiple minimal elements, then the first one will be returned. The collection must not be empty. `NaN` is treated as less than all other values except `missing`. See also: [`argmax`](@ref), [`findmin`](@ref). # Examples ```jldoctest julia> argmin([8, 0.1, -9, pi]) 3 julia> argmin([7, 1, 1, 6]) 2 julia> argmin([7, 1, 1, NaN]) 4 ``` """ argmin(itr) = findmin(itr)[2] ## all & any """ any(itr) -> Bool Test whether any elements of a boolean collection are `true`, returning `true` as soon as the first `true` value in `itr` is encountered (short-circuiting). To short-circuit on `false`, use [`all`](@ref). If the input contains [`missing`](@ref) values, return `missing` if all non-missing values are `false` (or equivalently, if the input contains no `true` value), following [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). See also: [`all`](@ref), [`count`](@ref), [`sum`](@ref), [`|`](@ref), , [`||`](@ref). # Examples ```jldoctest julia> a = [true,false,false,true] 4-element Vector{Bool}: 1 0 0 1 julia> any(a) true julia> any((println(i); v) for (i, v) in enumerate(a)) 1 true julia> any([missing, true]) true julia> any([false, missing]) missing ``` """ any(itr) = any(identity, itr) """ all(itr) -> Bool Test whether all elements of a boolean collection are `true`, returning `false` as soon as the first `false` value in `itr` is encountered (short-circuiting). To short-circuit on `true`, use [`any`](@ref). If the input contains [`missing`](@ref) values, return `missing` if all non-missing values are `true` (or equivalently, if the input contains no `false` value), following [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). See also: [`all!`](@ref), [`any`](@ref), [`count`](@ref), [`&`](@ref), , [`&&`](@ref), [`allunique`](@ref). # Examples ```jldoctest julia> a = [true,false,false,true] 4-element Vector{Bool}: 1 0 0 1 julia> all(a) false julia> all((println(i); v) for (i, v) in enumerate(a)) 1 2 false julia> all([missing, false]) false julia> all([true, missing]) missing ``` """ all(itr) = all(identity, itr) """ any(p, itr) -> Bool Determine whether predicate `p` returns `true` for any elements of `itr`, returning `true` as soon as the first item in `itr` for which `p` returns `true` is encountered (short-circuiting). To short-circuit on `false`, use [`all`](@ref). If the input contains [`missing`](@ref) values, return `missing` if all non-missing values are `false` (or equivalently, if the input contains no `true` value), following [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). # Examples ```jldoctest julia> any(i->(4<=i<=6), [3,5,7]) true julia> any(i -> (println(i); i > 3), 1:10) 1 2 3 4 true julia> any(i -> i > 0, [1, missing]) true julia> any(i -> i > 0, [-1, missing]) missing julia> any(i -> i > 0, [-1, 0]) false ``` """ any(f, itr) = _any(f, itr, :) function _any(f, itr, ::Colon) anymissing = false for x in itr v = f(x) if ismissing(v) anymissing = true elseif v return true end end return anymissing ? missing : false end # Specialized versions of any(f, ::Tuple) # We fall back to the for loop implementation all elements have the same type or # if the tuple is too large. function any(f, itr::Tuple) if itr isa NTuple || length(itr) > 32 return _any(f, itr, :) end _any_tuple(f, false, itr...) end @inline function _any_tuple(f, anymissing, x, rest...) v = f(x) if ismissing(v) anymissing = true elseif v return true end return _any_tuple(f, anymissing, rest...) end @inline _any_tuple(f, anymissing) = anymissing ? missing : false """ all(p, itr) -> Bool Determine whether predicate `p` returns `true` for all elements of `itr`, returning `false` as soon as the first item in `itr` for which `p` returns `false` is encountered (short-circuiting). To short-circuit on `true`, use [`any`](@ref). If the input contains [`missing`](@ref) values, return `missing` if all non-missing values are `true` (or equivalently, if the input contains no `false` value), following [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). # Examples ```jldoctest julia> all(i->(4<=i<=6), [4,5,6]) true julia> all(i -> (println(i); i < 3), 1:10) 1 2 3 false julia> all(i -> i > 0, [1, missing]) missing julia> all(i -> i > 0, [-1, missing]) false julia> all(i -> i > 0, [1, 2]) true ``` """ all(f, itr) = _all(f, itr, :) function _all(f, itr, ::Colon) anymissing = false for x in itr v = f(x) if ismissing(v) anymissing = true # this syntax allows throwing a TypeError for non-Bool, for consistency with any elseif v continue else return false end end return anymissing ? missing : true end # Specialized versions of all(f, ::Tuple), # This is similar to any(f, ::Tuple) defined above. function all(f, itr::Tuple) if itr isa NTuple || length(itr) > 32 return _all(f, itr, :) end _all_tuple(f, false, itr...) end @inline function _all_tuple(f, anymissing, x, rest...) v = f(x) if ismissing(v) anymissing = true # this syntax allows throwing a TypeError for non-Bool, for consistency with any elseif v nothing else return false end return _all_tuple(f, anymissing, rest...) end @inline _all_tuple(f, anymissing) = anymissing ? missing : true ## count _bool(f) = x->f(x)::Bool """ count([f=identity,] itr; init=0) -> Integer Count the number of elements in `itr` for which the function `f` returns `true`. If `f` is omitted, count the number of `true` elements in `itr` (which should be a collection of boolean values). `init` optionally specifies the value to start counting from and therefore also determines the output type. !!! compat "Julia 1.6" `init` keyword was added in Julia 1.6. See also: [`any`](@ref), [`sum`](@ref). # Examples ```jldoctest julia> count(i->(4<=i<=6), [2,3,4,5,6]) 3 julia> count([true, false, true, true]) 3 julia> count(>(3), 1:7, init=0x03) 0x07 ``` """ count(itr; init=0) = count(identity, itr; init) count(f, itr; init=0) = _simple_count(f, itr, init) _simple_count(pred, itr, init) = sum(_bool(pred), itr; init) function _simple_count(::typeof(identity), x::Array{Bool}, init::T=0) where {T} n::T = init chunks = length(x) รท sizeof(UInt) mask = 0x0101010101010101 % UInt GC.@preserve x begin ptr = Ptr{UInt}(pointer(x)) for i in 1:chunks n = (n + count_ones(unsafe_load(ptr, i) & mask)) % T end end for i in sizeof(UInt)*chunks+1:length(x) n = (n + x[i]) % T end return n end T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/reshapedarray.jlb8# This file is a part of Julia. License is MIT: https://julialang.org/license using Base.MultiplicativeInverses: SignedMultiplicativeInverse struct ReshapedArray{T,N,P<:AbstractArray,MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int}}}} <: AbstractArray{T,N} parent::P dims::NTuple{N,Int} mi::MI end ReshapedArray(parent::AbstractArray{T}, dims::NTuple{N,Int}, mi) where {T,N} = ReshapedArray{T,N,typeof(parent),typeof(mi)}(parent, dims, mi) # IndexLinear ReshapedArray const ReshapedArrayLF{T,N,P<:AbstractArray} = ReshapedArray{T,N,P,Tuple{}} # Fast iteration on ReshapedArrays: use the parent iterator struct ReshapedArrayIterator{I,M} iter::I mi::NTuple{M,SignedMultiplicativeInverse{Int}} end ReshapedArrayIterator(A::ReshapedArray) = _rs_iterator(parent(A), A.mi) function _rs_iterator(P, mi::NTuple{M}) where M iter = eachindex(P) ReshapedArrayIterator{typeof(iter),M}(iter, mi) end struct ReshapedIndex{T} parentindex::T end # eachindex(A::ReshapedArray) = ReshapedArrayIterator(A) # TODO: uncomment this line @inline function iterate(R::ReshapedArrayIterator, i...) item, inext = iterate(R.iter, i...) ReshapedIndex(item), inext end length(R::ReshapedArrayIterator) = length(R.iter) eltype(::Type{<:ReshapedArrayIterator{I}}) where {I} = @isdefined(I) ? ReshapedIndex{eltype(I)} : Any ## reshape(::Array, ::Dims) returns an Array, except for isbitsunion eltypes (issue #28611) # reshaping to same # of dimensions function reshape(a::Array{T,M}, dims::NTuple{N,Int}) where {T,N,M} throw_dmrsa(dims, len) = throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $len")) if prod(dims) != length(a) throw_dmrsa(dims, length(a)) end isbitsunion(T) && return ReshapedArray(a, dims, ()) if N == M && dims == size(a) return a end ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims) end """ reshape(A, dims...) -> AbstractArray reshape(A, dims) -> AbstractArray Return an array with the same data as `A`, but with different dimension sizes or number of dimensions. The two arrays share the same underlying data, so that the result is mutable if and only if `A` is mutable, and setting elements of one alters the values of the other. The new dimensions may be specified either as a list of arguments or as a shape tuple. At most one dimension may be specified with a `:`, in which case its length is computed such that its product with all the specified dimensions is equal to the length of the original array `A`. The total number of elements must not change. # Examples ```jldoctest julia> A = Vector(1:16) 16-element Vector{Int64}: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 julia> reshape(A, (4, 4)) 4ร—4 Matrix{Int64}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 julia> reshape(A, 2, :) 2ร—8 Matrix{Int64}: 1 3 5 7 9 11 13 15 2 4 6 8 10 12 14 16 julia> reshape(1:6, 2, 3) 2ร—3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64: 1 3 5 2 4 6 ``` """ reshape reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims) reshape(parent::AbstractArray, shp::Tuple{Union{Integer,OneTo}, Vararg{Union{Integer,OneTo}}}) = reshape(parent, to_shape(shp)) reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims) # Allow missing dimensions with Colon(): reshape(parent::AbstractVector, ::Colon) = parent reshape(parent::AbstractVector, ::Tuple{Colon}) = parent reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims) reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims) reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = reshape(parent, _reshape_uncolon(parent, dims)) @inline function _reshape_uncolon(A, dims) @noinline throw1(dims) = throw(DimensionMismatch(string("new dimensions $(dims) ", "may have at most one omitted dimension specified by `Colon()`"))) @noinline throw2(A, dims) = throw(DimensionMismatch(string("array size $(length(A)) ", "must be divisible by the product of the new dimensions $dims"))) pre = _before_colon(dims...) post = _after_colon(dims...) _any_colon(post...) && throw1(dims) sz, remainder = divrem(length(A), prod(pre)*prod(post)) remainder == 0 || throw2(A, dims) (pre..., Int(sz), post...) end @inline _any_colon() = false @inline _any_colon(dim::Colon, tail...) = true @inline _any_colon(dim::Any, tail...) = _any_colon(tail...) @inline _before_colon(dim::Any, tail...) = (dim, _before_colon(tail...)...) @inline _before_colon(dim::Colon, tail...) = () @inline _after_colon(dim::Any, tail...) = _after_colon(tail...) @inline _after_colon(dim::Colon, tail...) = tail reshape(parent::AbstractArray{T,N}, ndims::Val{N}) where {T,N} = parent function reshape(parent::AbstractArray, ndims::Val{N}) where N reshape(parent, rdims(Val(N), axes(parent))) end # Move elements from inds to out until out reaches the desired # dimensionality N, either filling with OneTo(1) or collapsing the # product of trailing dims into the last element rdims_trailing(l, inds...) = length(l) * rdims_trailing(inds...) rdims_trailing(l) = length(l) rdims(out::Val{N}, inds::Tuple) where {N} = rdims(ntuple(Returns(OneTo(1)), Val(N)), inds) rdims(out::Tuple{}, inds::Tuple{}) = () # N == 0, M == 0 rdims(out::Tuple{}, inds::Tuple{Any}) = () rdims(out::Tuple{}, inds::NTuple{M,Any}) where {M} = () rdims(out::Tuple{Any}, inds::Tuple{}) = out # N == 1, M == 0 rdims(out::NTuple{N,Any}, inds::Tuple{}) where {N} = out # N > 1, M == 0 rdims(out::Tuple{Any}, inds::Tuple{Any}) = inds # N == 1, M == 1 rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (oneto(rdims_trailing(inds...)),) # N == 1, M > 1 rdims(out::NTuple{N,Any}, inds::NTuple{N,Any}) where {N} = inds # N > 1, M == N rdims(out::NTuple{N,Any}, inds::NTuple{M,Any}) where {N,M} = (first(inds), rdims(tail(out), tail(inds))...) # N > 1, M > 1, M != N # _reshape on Array returns an Array _reshape(parent::Vector, dims::Dims{1}) = parent _reshape(parent::Array, dims::Dims{1}) = reshape(parent, dims) _reshape(parent::Array, dims::Dims) = reshape(parent, dims) # When reshaping Vector->Vector, don't wrap with a ReshapedArray function _reshape(v::AbstractVector, dims::Dims{1}) require_one_based_indexing(v) len = dims[1] len == length(v) || _throw_dmrs(length(v), "length", len) v end # General reshape function _reshape(parent::AbstractArray, dims::Dims) n = length(parent) prod(dims) == n || _throw_dmrs(n, "size", dims) __reshape((parent, IndexStyle(parent)), dims) end @noinline function _throw_dmrs(n, str, dims) throw(DimensionMismatch("parent has $n elements, which is incompatible with $str $dims")) end # Reshaping a ReshapedArray _reshape(v::ReshapedArray{<:Any,1}, dims::Dims{1}) = _reshape(v.parent, dims) _reshape(R::ReshapedArray, dims::Dims) = _reshape(R.parent, dims) function __reshape(p::Tuple{AbstractArray,IndexStyle}, dims::Dims) parent = p[1] strds = front(size_to_strides(map(length, axes(parent))..., 1)) strds1 = map(s->max(1,Int(s)), strds) # for resizing empty arrays mi = map(SignedMultiplicativeInverse, strds1) ReshapedArray(parent, dims, reverse(mi)) end function __reshape(p::Tuple{AbstractArray{<:Any,0},IndexCartesian}, dims::Dims) parent = p[1] ReshapedArray(parent, dims, ()) end function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims) parent = p[1] ReshapedArray(parent, dims, ()) end size(A::ReshapedArray) = A.dims length(A::ReshapedArray) = length(parent(A)) similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear() parent(A::ReshapedArray) = A.parent parentindices(A::ReshapedArray) = map(oneto, size(parent(A))) reinterpret(::Type{T}, A::ReshapedArray, dims::Dims) where {T} = reinterpret(T, parent(A), dims) elsize(::Type{<:ReshapedArray{<:Any,<:Any,P}}) where {P} = elsize(P) unaliascopy(A::ReshapedArray) = typeof(A)(unaliascopy(A.parent), A.dims, A.mi) dataids(A::ReshapedArray) = dataids(A.parent) @inline ind2sub_rs(ax, ::Tuple{}, i::Int) = (i,) @inline ind2sub_rs(ax, strds, i) = _ind2sub_rs(ax, strds, i - 1) @inline _ind2sub_rs(ax, ::Tuple{}, ind) = (ind + first(ax[end]),) @inline function _ind2sub_rs(ax, strds, ind) d, r = divrem(ind, strds[1]) (_ind2sub_rs(front(ax), tail(strds), r)..., d + first(ax[end])) end offset_if_vec(i::Integer, axs::Tuple{<:AbstractUnitRange}) = i + first(axs[1]) - 1 offset_if_vec(i::Integer, axs::Tuple) = i @inline function isassigned(A::ReshapedArrayLF, index::Int) @boundscheck checkbounds(Bool, A, index) || return false indexparent = index - firstindex(A) + firstindex(parent(A)) @inbounds ret = isassigned(parent(A), indexparent) ret end @inline function isassigned(A::ReshapedArray{T,N}, indices::Vararg{Int, N}) where {T,N} @boundscheck checkbounds(Bool, A, indices...) || return false axp = axes(A.parent) i = offset_if_vec(_sub2ind(size(A), indices...), axp) I = ind2sub_rs(axp, A.mi, i) @inbounds isassigned(A.parent, I...) end @inline function getindex(A::ReshapedArrayLF, index::Int) @boundscheck checkbounds(A, index) indexparent = index - firstindex(A) + firstindex(parent(A)) @inbounds ret = parent(A)[indexparent] ret end @inline function getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N} @boundscheck checkbounds(A, indices...) _unsafe_getindex(A, indices...) end @inline function getindex(A::ReshapedArray, index::ReshapedIndex) @boundscheck checkbounds(parent(A), index.parentindex) @inbounds ret = parent(A)[index.parentindex] ret end @inline function _unsafe_getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N} axp = axes(A.parent) i = offset_if_vec(_sub2ind(size(A), indices...), axp) I = ind2sub_rs(axp, A.mi, i) _unsafe_getindex_rs(parent(A), I) end @inline _unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret) @inline _unsafe_getindex_rs(A, I) = (@inbounds ret = A[I...]; ret) @inline function setindex!(A::ReshapedArrayLF, val, index::Int) @boundscheck checkbounds(A, index) indexparent = index - firstindex(A) + firstindex(parent(A)) @inbounds parent(A)[indexparent] = val val end @inline function setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N} @boundscheck checkbounds(A, indices...) _unsafe_setindex!(A, val, indices...) end @inline function setindex!(A::ReshapedArray, val, index::ReshapedIndex) @boundscheck checkbounds(parent(A), index.parentindex) @inbounds parent(A)[index.parentindex] = val val end @inline function _unsafe_setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N} axp = axes(A.parent) i = offset_if_vec(_sub2ind(size(A), indices...), axp) @inbounds parent(A)[ind2sub_rs(axes(A.parent), A.mi, i)...] = val val end # helpful error message for a common failure case const ReshapedRange{T,N,A<:AbstractRange} = ReshapedArray{T,N,A,Tuple{}} setindex!(A::ReshapedRange, val, index::Int) = _rs_setindex!_err() setindex!(A::ReshapedRange{T,N}, val, indices::Vararg{Int,N}) where {T,N} = _rs_setindex!_err() setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err() @noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect") unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, parent(a)) # Add a few handy specializations to further speed up views of reshaped ranges const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}} viewindexing(I::Tuple{Slice, ReshapedUnitRange, Vararg{ScalarIndex}}) = IndexLinear() viewindexing(I::Tuple{ReshapedRange, Vararg{ScalarIndex}}) = IndexLinear() compute_stride1(s, inds, I::Tuple{ReshapedRange, Vararg{Any}}) = s*step(I[1].parent) compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{ReshapedRange}) = (@inline; first(I[1]) - first(axes1(I[1]))*stride1) substrides(strds::NTuple{N,Int}, I::Tuple{ReshapedUnitRange, Vararg{Any}}) where N = (size_to_strides(strds[1], size(I[1])...)..., substrides(tail(strds), tail(I))...) unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} = unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T) _checkcontiguous(::Type{Bool}, A::AbstractArray) = false # `strides(A::DenseArray)` calls `size_to_strides` by default. # Thus it's OK to assume all `DenseArray`s are contiguously stored. _checkcontiguous(::Type{Bool}, A::DenseArray) = true _checkcontiguous(::Type{Bool}, A::ReshapedArray) = _checkcontiguous(Bool, parent(A)) _checkcontiguous(::Type{Bool}, A::FastContiguousSubArray) = _checkcontiguous(Bool, parent(A)) function strides(a::ReshapedArray) _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...) apsz::Dims = size(a.parent) apst::Dims = strides(a.parent) msz, mst, n = merge_adjacent_dim(apsz, apst) # Try to perform "lazy" reshape n == ndims(a.parent) && return size_to_strides(mst, size(a)...) # Parent is stridevector like return _reshaped_strides(size(a), 1, msz, mst, n, apsz, apst) end function _reshaped_strides(::Dims{0}, reshaped::Int, msz::Int, ::Int, ::Int, ::Dims, ::Dims) reshaped == msz && return () throw(ArgumentError("Input is not strided.")) end function _reshaped_strides(sz::Dims, reshaped::Int, msz::Int, mst::Int, n::Int, apsz::Dims, apst::Dims) st = reshaped * mst reshaped = reshaped * sz[1] if length(sz) > 1 && reshaped == msz && sz[2] != 1 msz, mst, n = merge_adjacent_dim(apsz, apst, n + 1) reshaped = 1 end sts = _reshaped_strides(tail(sz), reshaped, msz, mst, n, apsz, apst) return (st, sts...) end merge_adjacent_dim(::Dims{0}, ::Dims{0}) = 1, 1, 0 merge_adjacent_dim(apsz::Dims{1}, apst::Dims{1}) = apsz[1], apst[1], 1 function merge_adjacent_dim(apsz::Dims{N}, apst::Dims{N}, n::Int = 1) where {N} sz, st = apsz[n], apst[n] while n < N szโ‚™, stโ‚™ = apsz[n+1], apst[n+1] if sz == 1 sz, st = szโ‚™, stโ‚™ elseif stโ‚™ == st * sz || szโ‚™ == 1 sz *= szโ‚™ else break end n += 1 end return sz, st, n end W/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/reinterpretarray.jl‡# This file is a part of Julia. License is MIT: https://julialang.org/license """ Gives a reinterpreted view (of element type T) of the underlying array (of element type S). If the size of `T` differs from the size of `S`, the array will be compressed/expanded in the first dimension. The variant `reinterpret(reshape, T, a)` instead adds or consumes the first dimension depending on the ratio of element sizes. """ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T, N} parent::A readable::Bool writable::Bool function throwbits(S::Type, T::Type, U::Type) @noinline throw(ArgumentError("cannot reinterpret `$(S)` as `$(T)`, type `$(U)` is not a bits type")) end function throwsize0(S::Type, T::Type, msg) @noinline throw(ArgumentError("cannot reinterpret a zero-dimensional `$(S)` array to `$(T)` which is of a $msg size")) end function throwsingleton(S::Type, T::Type) @noinline throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` which is a singleton type")) end global reinterpret @doc """ reinterpret(T::DataType, A::AbstractArray) Construct a view of the array with the same binary data as the given array, but with `T` as element type. This function also works on "lazy" array whose elements are not computed until they are explicitly retrieved. For instance, `reinterpret` on the range `1:6` works similarly as on the dense vector `collect(1:6)`: ```jldoctest julia> reinterpret(Float32, UInt32[1 2 3 4 5]) 1ร—5 reinterpret(Float32, ::Matrix{UInt32}): 1.0f-45 3.0f-45 4.0f-45 6.0f-45 7.0f-45 julia> reinterpret(Complex{Int}, 1:6) 3-element reinterpret(Complex{$Int}, ::UnitRange{$Int}): 1 + 2im 3 + 4im 5 + 6im ``` """ function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}} function thrownonint(S::Type, T::Type, dim) @noinline throw(ArgumentError(""" cannot reinterpret an `$(S)` array to `$(T)` whose first dimension has size `$(dim)`. The resulting array would have non-integral first dimension. """)) end function throwaxes1(S::Type, T::Type, ax1) @noinline throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` when the first axis is $ax1. Try reshaping first.")) end isbitstype(T) || throwbits(S, T, T) isbitstype(S) || throwbits(S, T, S) (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T, "different") if N != 0 && sizeof(S) != sizeof(T) ax1 = axes(a)[1] dim = length(ax1) if issingletontype(T) issingletontype(S) || throwsingleton(S, T) else rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim) end first(ax1) == 1 || throwaxes1(S, T, ax1) end readable = array_subpadding(T, S) writable = array_subpadding(S, T) new{T, N, S, A, false}(a, readable, writable) end reinterpret(::Type{T}, a::AbstractArray{T}) where {T} = a # With reshaping function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}} function throwintmult(S::Type, T::Type) @noinline throw(ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got $(sizeof(T))) and `sizeof(eltype(a))` (got $(sizeof(S))) be an integer multiple of the other")) end function throwsize1(a::AbstractArray, T::Type) @noinline throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $(eltype(a)) requires that `axes(a, 1)` (got $(axes(a, 1))) be equal to 1:$(sizeof(T) รท sizeof(eltype(a))) (from the ratio of element sizes)")) end function throwfromsingleton(S, T) @noinline throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $S requires that $T be a singleton type, since $S is one")) end isbitstype(T) || throwbits(S, T, T) isbitstype(S) || throwbits(S, T, S) if sizeof(S) == sizeof(T) N = ndims(a) elseif sizeof(S) > sizeof(T) issingletontype(T) && throwsingleton(S, T) rem(sizeof(S), sizeof(T)) == 0 || throwintmult(S, T) N = ndims(a) + 1 else issingletontype(S) && throwfromsingleton(S, T) rem(sizeof(T), sizeof(S)) == 0 || throwintmult(S, T) N = ndims(a) - 1 N > -1 || throwsize0(S, T, "larger") axes(a, 1) == OneTo(sizeof(T) รท sizeof(S)) || throwsize1(a, T) end readable = array_subpadding(T, S) writable = array_subpadding(S, T) new{T, N, S, A, true}(a, readable, writable) end reinterpret(::typeof(reshape), ::Type{T}, a::AbstractArray{T}) where {T} = a end ReshapedReinterpretArray{T,N,S,A<:AbstractArray{S}} = ReinterpretArray{T,N,S,A,true} NonReshapedReinterpretArray{T,N,S,A<:AbstractArray{S, N}} = ReinterpretArray{T,N,S,A,false} """ reinterpret(reshape, T, A::AbstractArray{S}) -> B Change the type-interpretation of `A` while consuming or adding a "channel dimension." If `sizeof(T) = n*sizeof(S)` for `n>1`, `A`'s first dimension must be of size `n` and `B` lacks `A`'s first dimension. Conversely, if `sizeof(S) = n*sizeof(T)` for `n>1`, `B` gets a new first dimension of size `n`. The dimensionality is unchanged if `sizeof(T) == sizeof(S)`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{$Int}: 1 2 3 4 julia> reinterpret(reshape, Complex{Int}, A) # the result is a vector 2-element reinterpret(reshape, Complex{$Int}, ::Matrix{$Int}) with eltype Complex{$Int}: 1 + 3im 2 + 4im julia> a = [(1,2,3), (4,5,6)] 2-element Vector{Tuple{$Int, $Int, $Int}}: (1, 2, 3) (4, 5, 6) julia> reinterpret(reshape, Int, a) # the result is a matrix 3ร—2 reinterpret(reshape, $Int, ::Vector{Tuple{$Int, $Int, $Int}}) with eltype $Int: 1 4 2 5 3 6 ``` """ reinterpret(::typeof(reshape), T::Type, a::AbstractArray) reinterpret(::Type{T}, a::NonReshapedReinterpretArray) where {T} = reinterpret(T, a.parent) reinterpret(::typeof(reshape), ::Type{T}, a::ReshapedReinterpretArray) where {T} = reinterpret(reshape, T, a.parent) # Definition of StridedArray StridedFastContiguousSubArray{T,N,A<:DenseArray} = FastContiguousSubArray{T,N,A} StridedReinterpretArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray},IsReshaped} = ReinterpretArray{T,N,S,A,IsReshaped} where S StridedReshapedArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray,StridedReinterpretArray}} = ReshapedArray{T,N,A} StridedSubArray{T,N,A<:Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, I<:Tuple{Vararg{Union{RangeIndex, ReshapedUnitRange, AbstractCartesianIndex}}}} = SubArray{T,N,A,I} StridedArray{T,N} = Union{DenseArray{T,N}, StridedSubArray{T,N}, StridedReshapedArray{T,N}, StridedReinterpretArray{T,N}} StridedVector{T} = StridedArray{T,1} StridedMatrix{T} = StridedArray{T,2} StridedVecOrMat{T} = Union{StridedVector{T}, StridedMatrix{T}} strides(a::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}) = size_to_strides(1, size(a)...) stride(A::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, k::Integer) = k โ‰ค ndims(A) ? strides(A)[k] : length(A) function strides(a::ReinterpretArray{T,<:Any,S,<:AbstractArray{S},IsReshaped}) where {T,S,IsReshaped} _checkcontiguous(Bool, a) && return size_to_strides(1, size(a)...) stp = strides(parent(a)) els, elp = sizeof(T), sizeof(S) els == elp && return stp # 0dim parent is also handled here. IsReshaped && els < elp && return (1, _checked_strides(stp, els, elp)...) stp[1] == 1 || throw(ArgumentError("Parent must be contiguous in the 1st dimension!")) stโ€ฒ = _checked_strides(tail(stp), els, elp) return IsReshaped ? stโ€ฒ : (1, stโ€ฒ...) end @inline function _checked_strides(stp::Tuple, els::Integer, elp::Integer) if elp > els && rem(elp, els) == 0 N = div(elp, els) return map(i -> N * i, stp) end drs = map(i -> divrem(elp * i, els), stp) all(i->iszero(i[2]), drs) || throw(ArgumentError("Parent's strides could not be exactly divided!")) map(first, drs) end _checkcontiguous(::Type{Bool}, A::ReinterpretArray) = _checkcontiguous(Bool, parent(A)) similar(a::ReinterpretArray, T::Type, d::Dims) = similar(a.parent, T, d) function check_readable(a::ReinterpretArray{T, N, S} where N) where {T,S} # See comment in check_writable if !a.readable && !array_subpadding(T, S) throw(PaddingError(T, S)) end end function check_writable(a::ReinterpretArray{T, N, S} where N) where {T,S} # `array_subpadding` is relatively expensive (compared to a simple arrayref), # so it is cached in the array. However, it is computable at compile time if, # inference has the types available. By using this form of the check, we can # get the best of both worlds for the success case. If the types were not # available to inference, we simply need to check the field (relatively cheap) # and if they were we should be able to fold this check away entirely. if !a.writable && !array_subpadding(S, T) throw(PaddingError(T, S)) end end ## IndexStyle specializations # For `reinterpret(reshape, T, a)` where we're adding a channel dimension and with # `IndexStyle(a) == IndexLinear()`, it's advantageous to retain pseudo-linear indexing. struct IndexSCartesian2{K} <: IndexStyle end # K = sizeof(S) รท sizeof(T), a static-sized 2d cartesian iterator IndexStyle(::Type{ReinterpretArray{T,N,S,A,false}}) where {T,N,S,A<:AbstractArray{S,N}} = IndexStyle(A) function IndexStyle(::Type{ReinterpretArray{T,N,S,A,true}}) where {T,N,S,A<:AbstractArray{S}} if sizeof(T) < sizeof(S) IndexStyle(A) === IndexLinear() && return IndexSCartesian2{sizeof(S) รท sizeof(T)}() return IndexCartesian() end return IndexStyle(A) end IndexStyle(::IndexSCartesian2{K}, ::IndexSCartesian2{K}) where {K} = IndexSCartesian2{K}() struct SCartesianIndex2{K} # can't make <:AbstractCartesianIndex without N, and 2 would be a bit misleading i::Int j::Int end to_index(i::SCartesianIndex2) = i struct SCartesianIndices2{K,R<:AbstractUnitRange{Int}} <: AbstractMatrix{SCartesianIndex2{K}} indices2::R end SCartesianIndices2{K}(indices2::AbstractUnitRange{Int}) where {K} = (@assert K::Int > 1; SCartesianIndices2{K,typeof(indices2)}(indices2)) eachindex(::IndexSCartesian2{K}, A::ReshapedReinterpretArray) where {K} = SCartesianIndices2{K}(eachindex(IndexLinear(), parent(A))) @inline function eachindex(style::IndexSCartesian2{K}, A::AbstractArray, B::AbstractArray...) where {K} iter = eachindex(style, A) _all_match_first(C->eachindex(style, C), iter, B...) || throw_eachindex_mismatch_indices(IndexSCartesian2{K}(), axes(A), axes.(B)...) return iter end size(iter::SCartesianIndices2{K}) where K = (K, length(iter.indices2)) axes(iter::SCartesianIndices2{K}) where K = (OneTo(K), iter.indices2) first(iter::SCartesianIndices2{K}) where {K} = SCartesianIndex2{K}(1, first(iter.indices2)) last(iter::SCartesianIndices2{K}) where {K} = SCartesianIndex2{K}(K, last(iter.indices2)) @inline function getindex(iter::SCartesianIndices2{K}, i::Int, j::Int) where {K} @boundscheck checkbounds(iter, i, j) return SCartesianIndex2{K}(i, iter.indices2[j]) end function iterate(iter::SCartesianIndices2{K}) where {K} ret = iterate(iter.indices2) ret === nothing && return nothing item2, state2 = ret return SCartesianIndex2{K}(1, item2), (1, item2, state2) end function iterate(iter::SCartesianIndices2{K}, (state1, item2, state2)) where {K} if state1 < K item1 = state1 + 1 return SCartesianIndex2{K}(item1, item2), (item1, item2, state2) end ret = iterate(iter.indices2, state2) ret === nothing && return nothing item2, state2 = ret return SCartesianIndex2{K}(1, item2), (1, item2, state2) end SimdLoop.simd_outer_range(iter::SCartesianIndices2) = iter.indices2 SimdLoop.simd_inner_length(::SCartesianIndices2{K}, ::Any) where K = K @inline function SimdLoop.simd_index(::SCartesianIndices2{K}, Ilast::Int, I1::Int) where {K} SCartesianIndex2{K}(I1+1, Ilast) end _maybe_reshape(::IndexSCartesian2, A::ReshapedReinterpretArray, I...) = A # fallbacks function _getindex(::IndexSCartesian2, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N} @_propagate_inbounds_meta getindex(A, I...) end function _setindex!(::IndexSCartesian2, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N} @_propagate_inbounds_meta setindex!(A, v, I...) end # fallbacks for array types that use "pass-through" indexing (e.g., `IndexStyle(A) = IndexStyle(parent(A))`) # but which don't handle SCartesianIndex2 function _getindex(::IndexSCartesian2, A::AbstractArray{T,N}, ind::SCartesianIndex2) where {T,N} @_propagate_inbounds_meta J = _ind2sub(tail(axes(A)), ind.j) getindex(A, ind.i, J...) end function _setindex!(::IndexSCartesian2, A::AbstractArray{T,N}, v, ind::SCartesianIndex2) where {T,N} @_propagate_inbounds_meta J = _ind2sub(tail(axes(A)), ind.j) setindex!(A, v, ind.i, J...) end eachindex(style::IndexSCartesian2, A::AbstractArray) = eachindex(style, parent(A)) ## AbstractArray interface parent(a::ReinterpretArray) = a.parent dataids(a::ReinterpretArray) = dataids(a.parent) unaliascopy(a::NonReshapedReinterpretArray{T}) where {T} = reinterpret(T, unaliascopy(a.parent)) unaliascopy(a::ReshapedReinterpretArray{T}) where {T} = reinterpret(reshape, T, unaliascopy(a.parent)) function size(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S} psize = size(a.parent) size1 = issingletontype(T) ? psize[1] : div(psize[1]*sizeof(S), sizeof(T)) tuple(size1, tail(psize)...) end function size(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S} psize = size(a.parent) sizeof(S) > sizeof(T) && return (div(sizeof(S), sizeof(T)), psize...) sizeof(S) < sizeof(T) && return tail(psize) return psize end size(a::NonReshapedReinterpretArray{T,0}) where {T} = () function axes(a::NonReshapedReinterpretArray{T,N,S} where {N}) where {T,S} paxs = axes(a.parent) f, l = first(paxs[1]), length(paxs[1]) size1 = issingletontype(T) ? l : div(l*sizeof(S), sizeof(T)) tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...) end function axes(a::ReshapedReinterpretArray{T,N,S} where {N}) where {T,S} paxs = axes(a.parent) sizeof(S) > sizeof(T) && return (OneTo(div(sizeof(S), sizeof(T))), paxs...) sizeof(S) < sizeof(T) && return tail(paxs) return paxs end axes(a::NonReshapedReinterpretArray{T,0}) where {T} = () has_offset_axes(a::ReinterpretArray) = has_offset_axes(a.parent) elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T) unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent)) @inline @propagate_inbounds function getindex(a::NonReshapedReinterpretArray{T,0,S}) where {T,S} if isprimitivetype(T) && isprimitivetype(S) reinterpret(T, a.parent[]) else a[firstindex(a)] end end @inline @propagate_inbounds getindex(a::ReinterpretArray) = a[firstindex(a)] @inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} check_readable(a) _getindex_ra(a, inds[1], tail(inds)) end @inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} check_readable(a) if isa(IndexStyle(a), IndexLinear) return _getindex_ra(a, i, ()) end # Convert to full indices here, to avoid needing multiple conversions in # the loop in _getindex_ra inds = _to_subscript_indices(a, i) isempty(inds) ? _getindex_ra(a, 1, ()) : _getindex_ra(a, inds[1], tail(inds)) end @inline @propagate_inbounds function getindex(a::ReshapedReinterpretArray{T,N,S}, ind::SCartesianIndex2) where {T,N,S} check_readable(a) s = Ref{S}(a.parent[ind.j]) GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) return unsafe_load(tptr, ind.i) end end @inline @propagate_inbounds function _getindex_ra(a::NonReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @boundscheck checkbounds(a, i1, tailinds...) return T.instance end return reinterpret(T, a.parent[i1, tailinds...]) else @boundscheck checkbounds(a, i1, tailinds...) ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S)) # Optimizations that avoid branches if sizeof(T) % sizeof(S) == 0 # T is bigger than S and contains an integer number of them n = sizeof(T) รท sizeof(S) t = Ref{T}() GC.@preserve t begin sptr = Ptr{S}(unsafe_convert(Ref{T}, t)) for i = 1:n s = a.parent[ind_start + i, tailinds...] unsafe_store!(sptr, s, i) end end return t[] elseif sizeof(S) % sizeof(T) == 0 # S is bigger than T and contains an integer number of them s = Ref{S}(a.parent[ind_start + 1, tailinds...]) GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) return unsafe_load(tptr + sidx) end else i = 1 nbytes_copied = 0 # This is a bit complicated to deal with partial elements # at both the start and the end. LLVM will fold as appropriate, # once it knows the data layout s = Ref{S}() t = Ref{T}() GC.@preserve s t begin sptr = Ptr{S}(unsafe_convert(Ref{S}, s)) tptr = Ptr{T}(unsafe_convert(Ref{T}, t)) while nbytes_copied < sizeof(T) s[] = a.parent[ind_start + i, tailinds...] nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied) memcpy(tptr + nbytes_copied, sptr + sidx, nb) nbytes_copied += nb sidx = 0 i += 1 end end return t[] end end end @inline @propagate_inbounds function _getindex_ra(a::ReshapedReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @boundscheck checkbounds(a, i1, tailinds...) return T.instance end return reinterpret(T, a.parent[i1, tailinds...]) end @boundscheck checkbounds(a, i1, tailinds...) if sizeof(T) >= sizeof(S) t = Ref{T}() GC.@preserve t begin sptr = Ptr{S}(unsafe_convert(Ref{T}, t)) if sizeof(T) > sizeof(S) # Extra dimension in the parent array n = sizeof(T) รท sizeof(S) if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear() offset = n * (i1 - firstindex(a)) for i = 1:n s = a.parent[i + offset] unsafe_store!(sptr, s, i) end else for i = 1:n s = a.parent[i, i1, tailinds...] unsafe_store!(sptr, s, i) end end else # No extra dimension s = a.parent[i1, tailinds...] unsafe_store!(sptr, s) end end return t[] end # S is bigger than T and contains an integer number of them # n = sizeof(S) รท sizeof(T) s = Ref{S}() GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) s[] = a.parent[tailinds...] return unsafe_load(tptr, i1) end end @inline @propagate_inbounds function setindex!(a::NonReshapedReinterpretArray{T,0,S}, v) where {T,S} if isprimitivetype(S) && isprimitivetype(T) a.parent[] = reinterpret(S, v) return a end setindex!(a, v, firstindex(a)) end @inline @propagate_inbounds setindex!(a::ReinterpretArray, v) = setindex!(a, v, firstindex(a)) @inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} check_writable(a) _setindex_ra!(a, v, inds[1], tail(inds)) end @inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} check_writable(a) if isa(IndexStyle(a), IndexLinear) return _setindex_ra!(a, v, i, ()) end inds = _to_subscript_indices(a, i) _setindex_ra!(a, v, inds[1], tail(inds)) end @inline @propagate_inbounds function setindex!(a::ReshapedReinterpretArray{T,N,S}, v, ind::SCartesianIndex2) where {T,N,S} check_writable(a) v = convert(T, v)::T s = Ref{S}(a.parent[ind.j]) GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) unsafe_store!(tptr, v, ind.i) end a.parent[ind.j] = s[] return a end @inline @propagate_inbounds function _setindex_ra!(a::NonReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} v = convert(T, v)::T # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @boundscheck checkbounds(a, i1, tailinds...) # setindex! is a noop except for the index check else setindex!(a.parent, reinterpret(S, v), i1, tailinds...) end else @boundscheck checkbounds(a, i1, tailinds...) ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S)) # Optimizations that avoid branches if sizeof(T) % sizeof(S) == 0 # T is bigger than S and contains an integer number of them t = Ref{T}(v) GC.@preserve t begin sptr = Ptr{S}(unsafe_convert(Ref{T}, t)) n = sizeof(T) รท sizeof(S) for i = 1:n s = unsafe_load(sptr, i) a.parent[ind_start + i, tailinds...] = s end end elseif sizeof(S) % sizeof(T) == 0 # S is bigger than T and contains an integer number of them s = Ref{S}(a.parent[ind_start + 1, tailinds...]) GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) unsafe_store!(tptr + sidx, v) a.parent[ind_start + 1, tailinds...] = s[] end else t = Ref{T}(v) s = Ref{S}() GC.@preserve t s begin tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t)) sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s)) nbytes_copied = 0 i = 1 # Deal with any partial elements at the start. We'll have to copy in the # element from the original array and overwrite the relevant parts if sidx != 0 s[] = a.parent[ind_start + i, tailinds...] nb = min((sizeof(S) - sidx) % UInt, sizeof(T) % UInt) memcpy(sptr + sidx, tptr, nb) nbytes_copied += nb a.parent[ind_start + i, tailinds...] = s[] i += 1 sidx = 0 end # Deal with the main body of elements while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S) nb = min(sizeof(S), sizeof(T) - nbytes_copied) memcpy(sptr, tptr + nbytes_copied, nb) nbytes_copied += nb a.parent[ind_start + i, tailinds...] = s[] i += 1 end # Deal with trailing partial elements if nbytes_copied < sizeof(T) s[] = a.parent[ind_start + i, tailinds...] nb = min(sizeof(S), sizeof(T) - nbytes_copied) memcpy(sptr, tptr + nbytes_copied, nb) a.parent[ind_start + i, tailinds...] = s[] end end end end return a end @inline @propagate_inbounds function _setindex_ra!(a::ReshapedReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} v = convert(T, v)::T # Make sure to match the scalar reinterpret if that is applicable if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 if issingletontype(T) # singleton types @boundscheck checkbounds(a, i1, tailinds...) # setindex! is a noop except for the index check else setindex!(a.parent, reinterpret(S, v), i1, tailinds...) end end @boundscheck checkbounds(a, i1, tailinds...) if sizeof(T) >= sizeof(S) t = Ref{T}(v) GC.@preserve t begin sptr = Ptr{S}(unsafe_convert(Ref{T}, t)) if sizeof(T) > sizeof(S) # Extra dimension in the parent array n = sizeof(T) รท sizeof(S) if isempty(tailinds) && IndexStyle(a.parent) === IndexLinear() offset = n * (i1 - firstindex(a)) for i = 1:n s = unsafe_load(sptr, i) a.parent[i + offset] = s end else for i = 1:n s = unsafe_load(sptr, i) a.parent[i, i1, tailinds...] = s end end else # sizeof(T) == sizeof(S) # No extra dimension s = unsafe_load(sptr) a.parent[i1, tailinds...] = s end end else # S is bigger than T and contains an integer number of them s = Ref{S}() GC.@preserve s begin tptr = Ptr{T}(unsafe_convert(Ref{S}, s)) s[] = a.parent[tailinds...] unsafe_store!(tptr, v, i1) a.parent[tailinds...] = s[] end end return a end # Padding struct Padding offset::Int # 0-indexed offset of the next valid byte; sizeof(T) indicates trailing padding size::Int # bytes of padding before a valid byte end function intersect(p1::Padding, p2::Padding) start = max(p1.offset, p2.offset) stop = min(p1.offset + p1.size, p2.offset + p2.size) Padding(start, max(0, stop-start)) end struct PaddingError <: Exception S::Type T::Type end function showerror(io::IO, p::PaddingError) print(io, "Padding of type $(p.S) is not compatible with type $(p.T).") end """ CyclePadding(padding, total_size) Cylces an iterator of `Padding` structs, restarting the padding at `total_size`. E.g. if `padding` is all the padding in a struct and `total_size` is the total aligned size of that array, `CyclePadding` will correspond to the padding in an infinite vector of such structs. """ struct CyclePadding{P} padding::P total_size::Int end eltype(::Type{<:CyclePadding}) = Padding IteratorSize(::Type{<:CyclePadding}) = IsInfinite() isempty(cp::CyclePadding) = isempty(cp.padding) function iterate(cp::CyclePadding) y = iterate(cp.padding) y === nothing && return nothing y[1], (0, y[2]) end function iterate(cp::CyclePadding, state::Tuple) y = iterate(cp.padding, tail(state)...) y === nothing && return iterate(cp, (state[1]+cp.total_size,)) Padding(y[1].offset+state[1], y[1].size), (state[1], tail(y)...) end """ Compute the location of padding in an isbits datatype. Recursive over the fields of that type. """ @assume_effects :foldable function padding(T::DataType, baseoffset::Int = 0) pads = Padding[] last_end::Int = baseoffset for i = 1:fieldcount(T) offset = baseoffset + Int(fieldoffset(T, i)) fT = fieldtype(T, i) append!(pads, padding(fT, offset)) if offset != last_end push!(pads, Padding(offset, offset-last_end)) end last_end = offset + sizeof(fT) end if 0 < last_end - baseoffset < sizeof(T) push!(pads, Padding(baseoffset + sizeof(T), sizeof(T) - last_end + baseoffset)) end return Core.svec(pads...) end function CyclePadding(T::DataType) a, s = datatype_alignment(T), sizeof(T) as = s + (a - (s % a)) % a pad = padding(T) if s != as pad = Core.svec(pad..., Padding(s, as - s)) end CyclePadding(pad, as) end @assume_effects :total function array_subpadding(S, T) lcm_size = lcm(sizeof(S), sizeof(T)) s, t = CyclePadding(S), CyclePadding(T) checked_size = 0 # use of Stateful harms inference and makes this vulnerable to invalidation (pad, tstate) = let it = iterate(t) it === nothing && return true it end (ps, sstate) = let it = iterate(s) it === nothing && return false it end while checked_size < lcm_size while true # See if there's corresponding padding in S ps.offset > pad.offset && return false intersect(ps, pad) == pad && break ps, sstate = iterate(s, sstate) end checked_size = pad.offset + pad.size pad, tstate = iterate(t, tstate) end return true end @assume_effects :foldable function struct_subpadding(::Type{Out}, ::Type{In}) where {Out, In} padding(Out) == padding(In) end @assume_effects :foldable function packedsize(::Type{T}) where T pads = padding(T) return sizeof(T) - sum((p.size for p โˆˆ pads), init = 0) end @assume_effects :foldable ispacked(::Type{T}) where T = isempty(padding(T)) function _copytopacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In} writeoffset = 0 for i โˆˆ 1:fieldcount(In) readoffset = fieldoffset(In, i) fT = fieldtype(In, i) if ispacked(fT) readsize = sizeof(fT) memcpy(ptr_out + writeoffset, ptr_in + readoffset, readsize) writeoffset += readsize else # nested padded type _copytopacked!(ptr_out + writeoffset, Ptr{fT}(ptr_in + readoffset)) writeoffset += packedsize(fT) end end end function _copyfrompacked!(ptr_out::Ptr{Out}, ptr_in::Ptr{In}) where {Out, In} readoffset = 0 for i โˆˆ 1:fieldcount(Out) writeoffset = fieldoffset(Out, i) fT = fieldtype(Out, i) if ispacked(fT) writesize = sizeof(fT) memcpy(ptr_out + writeoffset, ptr_in + readoffset, writesize) readoffset += writesize else # nested padded type _copyfrompacked!(Ptr{fT}(ptr_out + writeoffset), ptr_in + readoffset) readoffset += packedsize(fT) end end end @inline function _reinterpret(::Type{Out}, x::In) where {Out, In} # handle non-primitive types isbitstype(Out) || throw(ArgumentError("Target type for `reinterpret` must be isbits")) isbitstype(In) || throw(ArgumentError("Source type for `reinterpret` must be isbits")) inpackedsize = packedsize(In) outpackedsize = packedsize(Out) inpackedsize == outpackedsize || throw(ArgumentError("Packed sizes of types $Out and $In do not match; got $outpackedsize \ and $inpackedsize, respectively.")) in = Ref{In}(x) out = Ref{Out}() if struct_subpadding(Out, In) # if packed the same, just copy GC.@preserve in out begin ptr_in = unsafe_convert(Ptr{In}, in) ptr_out = unsafe_convert(Ptr{Out}, out) memcpy(ptr_out, ptr_in, sizeof(Out)) end return out[] else # mismatched padding GC.@preserve in out begin ptr_in = unsafe_convert(Ptr{In}, in) ptr_out = unsafe_convert(Ptr{Out}, out) if fieldcount(In) > 0 && ispacked(Out) _copytopacked!(ptr_out, ptr_in) elseif fieldcount(Out) > 0 && ispacked(In) _copyfrompacked!(ptr_out, ptr_in) else packed = Ref{NTuple{inpackedsize, UInt8}}() GC.@preserve packed begin ptr_packed = unsafe_convert(Ptr{NTuple{inpackedsize, UInt8}}, packed) _copytopacked!(ptr_packed, ptr_in) _copyfrompacked!(ptr_out, ptr_packed) end end end return out[] end end # Reductions with IndexSCartesian2 function _mapreduce(f::F, op::OP, style::IndexSCartesian2{K}, A::AbstractArrayOrBroadcasted) where {F,OP,K} inds = eachindex(style, A) n = size(inds)[2] if n == 0 return mapreduce_empty_iter(f, op, A, IteratorEltype(A)) else return mapreduce_impl(f, op, A, first(inds), last(inds)) end end @noinline function mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted, ifirst::SCI, ilast::SCI, blksize::Int) where {F,OP,SCI<:SCartesianIndex2{K}} where K if ilast.j - ifirst.j < blksize # sequential portion @inbounds a1 = A[ifirst] @inbounds a2 = A[SCI(2,ifirst.j)] v = op(f(a1), f(a2)) @simd for i = ifirst.i + 2 : K @inbounds ai = A[SCI(i,ifirst.j)] v = op(v, f(ai)) end # Remaining columns for j = ifirst.j+1 : ilast.j @simd for i = 1:K @inbounds ai = A[SCI(i,j)] v = op(v, f(ai)) end end return v else # pairwise portion jmid = ifirst.j + (ilast.j - ifirst.j) >> 1 v1 = mapreduce_impl(f, op, A, ifirst, SCI(K,jmid), blksize) v2 = mapreduce_impl(f, op, A, SCI(1,jmid+1), ilast, blksize) return op(v1, v2) end end mapreduce_impl(f::F, op::OP, A::AbstractArrayOrBroadcasted, ifirst::SCartesianIndex2, ilast::SCartesianIndex2) where {F,OP} = mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/bitarray.jlฮ# This file is a part of Julia. License is MIT: https://julialang.org/license ## BitArray # notes: bits are stored in contiguous chunks # unused bits must always be set to 0 """ BitArray{N} <: AbstractArray{Bool, N} Space-efficient `N`-dimensional boolean array, using just one bit for each boolean value. `BitArray`s pack up to 64 values into every 8 bytes, resulting in an 8x space efficiency over `Array{Bool, N}` and allowing some operations to work on 64 values at once. By default, Julia returns `BitArrays` from [broadcasting](@ref Broadcasting) operations that generate boolean elements (including dotted-comparisons like `.==`) as well as from the functions [`trues`](@ref) and [`falses`](@ref). !!! note Due to its packed storage format, concurrent access to the elements of a `BitArray` where at least one of them is a write is not thread-safe. """ mutable struct BitArray{N} <: AbstractArray{Bool, N} chunks::Vector{UInt64} len::Int dims::NTuple{N,Int} function BitArray{N}(::UndefInitializer, dims::Vararg{Int,N}) where N n = 1 i = 1 for d in dims d >= 0 || throw(ArgumentError("dimension size must be โ‰ฅ 0, got $d for dimension $i")) n *= d i += 1 end nc = num_bit_chunks(n) chunks = Vector{UInt64}(undef, nc) nc > 0 && (chunks[end] = UInt64(0)) b = new(chunks, n) N != 1 && (b.dims = dims) return b end end # note: the docs for the two signatures are unified, but only # the first one is recognized by the help system; it would be nice # to fix this. """ BitArray(undef, dims::Integer...) BitArray{N}(undef, dims::NTuple{N,Int}) Construct an undef [`BitArray`](@ref) with the given dimensions. Behaves identically to the [`Array`](@ref) constructor. See [`undef`](@ref). # Examples ```julia-repl julia> BitArray(undef, 2, 2) 2ร—2 BitMatrix: 0 0 0 0 julia> BitArray(undef, (3, 1)) 3ร—1 BitMatrix: 0 0 0 ``` """ BitArray(::UndefInitializer, dims::Integer...) = BitArray(undef, map(Int,dims)) BitArray{N}(::UndefInitializer, dims::Integer...) where {N} = BitArray{N}(undef, map(Int,dims))::BitArray{N} BitArray(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...) BitArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...) const BitVector = BitArray{1} const BitMatrix = BitArray{2} BitVector() = BitVector(undef, 0) """ BitVector(nt::Tuple{Vararg{Bool}}) Construct a `BitVector` from a tuple of `Bool`. # Examples ```julia-repl julia> nt = (true, false, true, false) (true, false, true, false) julia> BitVector(nt) 4-element BitVector: 1 0 1 0 ``` """ function BitVector(nt::Tuple{Vararg{Bool}}) bv = BitVector(undef, length(nt)) bv .= nt end ## utility functions ## length(B::BitArray) = B.len size(B::BitVector) = (B.len,) size(B::BitArray) = B.dims @inline function size(B::BitVector, d::Integer) d < 1 && throw_boundserror(size(B), d) ifelse(d == 1, B.len, 1) end isassigned(B::BitArray, i::Int) = 1 <= i <= length(B) IndexStyle(::Type{<:BitArray}) = IndexLinear() ## aux functions ## const _msk64 = ~UInt64(0) @inline _div64(l) = l >> 6 @inline _mod64(l) = l & 63 @inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl @inline _msk_end(l::Int) = _msk64 >>> _mod64(-l) @inline _msk_end(B::BitArray) = _msk_end(length(B)) num_bit_chunks(n::Int) = _div64(n+63) @inline get_chunks_id(i::Int) = _div64(i-1)+1, _mod64(i-1) function glue_src_bitchunks(src::Vector{UInt64}, k::Int, ks1::Int, msk_s0::UInt64, ls0::Int) @inbounds begin chunk = ((src[k] & msk_s0) >>> ls0) if ks1 > k && ls0 > 0 chunk_n = (src[k + 1] & ~msk_s0) chunk |= (chunk_n << (64 - ls0)) end end return chunk end function copy_chunks!(dest::Vector{UInt64}, pos_d::Int, src::Vector{UInt64}, pos_s::Int, numbits::Int) numbits == 0 && return if dest === src && pos_d > pos_s return copy_chunks_rtol!(dest, pos_d, pos_s, numbits) end kd0, ld0 = get_chunks_id(pos_d) kd1, ld1 = get_chunks_id(pos_d + numbits - 1) ks0, ls0 = get_chunks_id(pos_s) ks1, ls1 = get_chunks_id(pos_s + numbits - 1) delta_kd = kd1 - kd0 delta_ks = ks1 - ks0 u = _msk64 if delta_kd == 0 msk_d0 = ~(u << ld0) | (u << (ld1+1)) else msk_d0 = ~(u << ld0) msk_d1 = (u << (ld1+1)) end if delta_ks == 0 msk_s0 = (u << ls0) & ~(u << (ls1+1)) else msk_s0 = (u << ls0) end chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0) dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) delta_kd == 0 && return for i = 1 : kd1 - kd0 - 1 chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0) chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) dest[kd0 + i] = chunk_s chunk_s0 = chunk_s1 end if ks1 >= ks0 + delta_kd chunk_s1 = glue_src_bitchunks(src, ks0 + delta_kd, ks1, msk_s0, ls0) else chunk_s1 = UInt64(0) end chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1) return end function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Int, pos_s::Int, numbits::Int) pos_d == pos_s && return pos_d < pos_s && return copy_chunks!(chunks, pos_d, chunks, pos_s, numbits) left = numbits s = min(left, 64) b = left - s ps = pos_s + b pd = pos_d + b u = _msk64 while left > 0 kd0, ld0 = get_chunks_id(pd) kd1, ld1 = get_chunks_id(pd + s - 1) ks0, ls0 = get_chunks_id(ps) ks1, ls1 = get_chunks_id(ps + s - 1) delta_kd = kd1 - kd0 delta_ks = ks1 - ks0 if delta_kd == 0 msk_d0 = ~(u << ld0) | (u << (ld1+1)) else msk_d0 = ~(u << ld0) msk_d1 = (u << (ld1+1)) end if delta_ks == 0 msk_s0 = (u << ls0) & ~(u << (ls1+1)) else msk_s0 = (u << ls0) end chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s) chunks[kd0] = (chunks[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) if delta_kd != 0 chunk_s = (chunk_s0 >>> (64 - ld0)) chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1) end left -= s s = min(left, 64) b = left - s ps = pos_s + b pd = pos_d + b end end function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Int, numbits::Int) numbits <= 0 && return k0, l0 = get_chunks_id(pos) k1, l1 = get_chunks_id(pos+numbits-1) u = _msk64 if k1 == k0 msk0 = (u << l0) & ~(u << (l1+1)) else msk0 = (u << l0) msk1 = ~(u << (l1+1)) end @inbounds if x Bc[k0] |= msk0 for k = k0+1:k1-1 Bc[k] = u end k1 > k0 && (Bc[k1] |= msk1) else Bc[k0] &= ~msk0 for k = k0+1:k1-1 Bc[k] = 0 end k1 > k0 && (Bc[k1] &= ~msk1) end end copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) = copy_chunks!(dest, pos_d, src.chunks, pos_s, numbits) # pack 8 Bools encoded as one contiguous UIn64 into a single byte, e.g.: # 0000001:0000001:00000000:00000000:00000001:00000000:00000000:00000001 โ†’ 11001001 โ†’ 0xc9 function pack8bools(z::UInt64) z |= z >>> 7 z |= z >>> 14 z |= z >>> 28 z &= 0xFF return z end function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array{Bool}, pos_s::Int, numbits::Int) kd0, ld0 = get_chunks_id(pos_d) kd1, ld1 = get_chunks_id(pos_d + numbits - 1) delta_kd = kd1 - kd0 u = _msk64 if delta_kd == 0 msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) lt0 = ld1 else msk_d0 = ~(u << ld0) msk_d1 = (u << (ld1+1)) lt0 = 63 end bind = kd0 ind = pos_s @inbounds if ld0 > 0 c = UInt64(0) for j = ld0:lt0 c |= (UInt64(C[ind]) << j) ind += 1 end Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) bind += 1 end nc = _div64(numbits - ind + pos_s) nc8 = (nc >>> 3) << 3 if nc8 > 0 ind8 = 1 P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer @inbounds for i = 1:nc8 c = UInt64(0) for j = 0:7 # unaligned load c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3)) ind8 += 1 end Bc[bind] = c bind += 1 end ind += (ind8-1) << 3 end @inbounds for i = (nc8+1):nc c = UInt64(0) for j = 0:63 c |= (UInt64(C[ind]) << j) ind += 1 end Bc[bind] = c bind += 1 end @inbounds if bind โ‰ค kd1 @assert bind == kd1 c = UInt64(0) for j = 0:ld1 c |= (UInt64(C[ind]) << j) ind += 1 end Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) end end ## More definitions in multidimensional.jl # auxiliary definitions used when filling a BitArray via a Vector{Bool} cache # (e.g. when constructing from an iterable, or in broadcast!) const bitcache_chunks = 64 # this can be changed const bitcache_size = 64 * bitcache_chunks # do not change this dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) = copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6)) ## custom iterator ## function iterate(B::BitArray, i::Int=0) i >= length(B) && return nothing (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1) end ## similar, fill!, copy! etc ## similar(B::BitArray) = BitArray(undef, size(B)) similar(B::BitArray, dims::Int...) = BitArray(undef, dims) similar(B::BitArray, dims::Dims) = BitArray(undef, dims...) similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(undef, dims) # changing type to a non-Bool returns an Array # (this triggers conversions like float(bitvector) etc.) similar(B::BitArray, T::Type, dims::Dims) = Array{T}(undef, dims) function fill!(B::BitArray, x) y = convert(Bool, x) isempty(B) && return B Bc = B.chunks if !y fill!(Bc, 0) else fill!(Bc, _msk64) Bc[end] &= _msk_end(B) end return B end """ falses(dims) Create a `BitArray` with all values set to `false`. # Examples ```jldoctest julia> falses(2,3) 2ร—3 BitMatrix: 0 0 0 0 0 0 ``` """ falses(dims::DimOrInd...) = falses(dims) falses(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = falses(map(to_dim, dims)) falses(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), false) falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false) """ trues(dims) Create a `BitArray` with all values set to `true`. # Examples ```jldoctest julia> trues(2,3) 2ร—3 BitMatrix: 1 1 1 1 1 1 ``` """ trues(dims::DimOrInd...) = trues(dims) trues(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = trues(map(to_dim, dims)) trues(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), true) trues(dims::Tuple{}) = fill!(BitArray(undef, dims), true) function one(x::BitMatrix) m, n = size(x) m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) a = falses(n, n) for i = 1:n a[i,i] = true end return a end function copyto!(dest::BitArray, src::BitArray) length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) destc = dest.chunks; srcc = src.chunks nc = min(length(destc), length(srcc)) nc == 0 && return dest @inbounds begin for i = 1 : nc - 1 destc[i] = srcc[i] end if length(src) == length(dest) destc[nc] = srcc[nc] else msk_s = _msk_end(src) msk_d = ~msk_s destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc]) end end return dest end function unsafe_copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) copy_to_bitarray_chunks!(dest.chunks, Int(doffs), src, Int(soffs), Int(n)) return dest end copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) = _copyto_int!(dest, Int(doffs), src, Int(soffs), Int(n)) function _copyto_int!(dest::BitArray, doffs::Int, src::Union{BitArray,Array}, soffs::Int, n::Int) n == 0 && return dest n < 0 && throw(ArgumentError("Number of elements to copy must be nonnegative.")) soffs < 1 && throw(BoundsError(src, soffs)) doffs < 1 && throw(BoundsError(dest, doffs)) soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1)) doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1)) return unsafe_copyto!(dest, doffs, src, soffs, n) end function copyto!(dest::BitArray, src::Array) length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) length(src) == 0 && return dest return unsafe_copyto!(dest, 1, src, 1, length(src)) end function reshape(B::BitArray{N}, dims::NTuple{N,Int}) where N return dims == size(B) ? B : _bitreshape(B, dims) end reshape(B::BitArray, dims::Tuple{Vararg{Int}}) = _bitreshape(B, dims) function _bitreshape(B::BitArray, dims::NTuple{N,Int}) where N prod(dims) == length(B) || throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))")) Br = BitArray{N}(undef, ntuple(i->0,Val(N))...) Br.chunks = B.chunks Br.len = prod(dims) N != 1 && (Br.dims = dims) return Br end ## Constructors ## function Array{T,N}(B::BitArray{N}) where {T,N} A = Array{T,N}(undef, size(B)) Bc = B.chunks @inbounds for i = 1:length(A) A[i] = unsafe_bitgetindex(Bc, i) end return A end BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A) function BitArray{N}(A::AbstractArray{T,N}) where N where T B = BitArray(undef, convert(Dims{N}, size(A)::Dims{N})) _checkaxs(axes(B), axes(A)) _copyto_bitarray!(B, A) return B::BitArray{N} end function _copyto_bitarray!(B::BitArray, A::AbstractArray) l = length(A) l == 0 && return B l > length(B) && throw(BoundsError(B, length(B)+1)) Bc = B.chunks nc = num_bit_chunks(l) Ai = first(eachindex(A)) @inbounds begin for i = 1:nc-1 c = UInt64(0) for j = 0:63 c |= (UInt64(convert(Bool, A[Ai])::Bool) << j) Ai = nextind(A, Ai) end Bc[i] = c end c = UInt64(0) tail = _mod64(l - 1) + 1 for j = 0:tail-1 c |= (UInt64(convert(Bool, A[Ai])::Bool) << j) Ai = nextind(A, Ai) end msk = _msk_end(tail) Bc[nc] = (c & msk) | (Bc[nc] & ~msk) end return B end reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims) reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims) if nameof(@__MODULE__) === :Base # avoid method overwrite (::Type{T})(x::T) where {T<:BitArray} = copy(x)::T BitArray(x::BitArray) = copy(x) end """ BitArray(itr) Construct a [`BitArray`](@ref) generated by the given iterable object. The shape is inferred from the `itr` object. # Examples ```jldoctest julia> BitArray([1 0; 0 1]) 2ร—2 BitMatrix: 1 0 0 1 julia> BitArray(x+y == 3 for x = 1:2, y = 1:3) 2ร—3 BitMatrix: 0 1 0 1 0 0 julia> BitArray(x+y == 3 for x = 1:2 for y = 1:3) 6-element BitVector: 0 1 0 1 0 0 ``` """ BitArray(itr) = gen_bitarray(IteratorSize(itr), itr) BitArray{N}(itr) where N = gen_bitarrayN(BitArray{N}, IteratorSize(itr), itr) convert(::Type{T}, a::AbstractArray) where {T<:BitArray} = a isa T ? a : T(a)::T # generic constructor from an iterable without compile-time info # (we pass start(itr) explicitly to avoid a type-instability with filters) gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr) # generic iterable with known shape function gen_bitarray(::HasShape, itr) B = BitArray(undef, size(itr)) for (I,x) in zip(CartesianIndices(axes(itr)), itr) B[I] = x end return B end # generator with known shape or length function gen_bitarray(::HasShape, itr::Generator) B = BitArray(undef, size(itr)) return fill_bitarray_from_itr!(B, itr) end function gen_bitarray(::HasLength, itr) b = BitVector(undef, length(itr)) return fill_bitarray_from_itr!(b, itr) end gen_bitarray(::IsInfinite, itr) = throw(ArgumentError("infinite-size iterable used in BitArray constructor")) gen_bitarrayN(::Type{BitVector}, itsz, itr) = gen_bitarray(itsz, itr) gen_bitarrayN(::Type{BitVector}, itsz::HasShape{1}, itr) = gen_bitarray(itsz, itr) gen_bitarrayN(::Type{BitArray{N}}, itsz::HasShape{N}, itr) where N = gen_bitarray(itsz, itr) # The first of these is just for ambiguity resolution gen_bitarrayN(::Type{BitVector}, itsz::HasShape{N}, itr) where N = throw(DimensionMismatch("cannot create a BitVector from a $N-dimensional iterator")) gen_bitarrayN(@nospecialize(T::Type), itsz::HasShape{N}, itr) where N = throw(DimensionMismatch("cannot create a $T from a $N-dimensional iterator")) gen_bitarrayN(@nospecialize(T::Type), itsz, itr) = throw(DimensionMismatch("cannot create a $T from a generic iterator")) # The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both # use a Vector{Bool} cache for performance reasons function gen_bitarray_from_itr(itr) B = empty!(BitVector(undef, bitcache_size)) C = Vector{Bool}(undef, bitcache_size) Bc = B.chunks ind = 1 cind = 1 y = iterate(itr) while y !== nothing x, st = y @inbounds C[ind] = x ind += 1 if ind > bitcache_size resize!(B, length(B) + bitcache_size) dumpbitcache(Bc, cind, C) cind += bitcache_chunks ind = 1 end y = iterate(itr, st) end if ind > 1 @inbounds C[ind:bitcache_size] .= false resize!(B, length(B) + ind - 1) dumpbitcache(Bc, cind, C) end return B end function fill_bitarray_from_itr!(B::BitArray, itr) n = length(B) C = Vector{Bool}(undef, bitcache_size) Bc = B.chunks ind = 1 cind = 1 y = iterate(itr) while y !== nothing x, st = y @inbounds C[ind] = x ind += 1 if ind > bitcache_size dumpbitcache(Bc, cind, C) cind += bitcache_chunks ind = 1 end y = iterate(itr, st) end if ind > 1 @inbounds C[ind:bitcache_size] .= false dumpbitcache(Bc, cind, C) end return B end ## Indexing: getindex ## @inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int) i1, i2 = get_chunks_id(i) u = UInt64(1) << i2 @inbounds r = (Bc[i1] & u) != 0 return r end @inline function getindex(B::BitArray, i::Int) @boundscheck checkbounds(B, i) unsafe_bitgetindex(B.chunks, i) end ## Indexing: setindex! ## @inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int) i1, i2 = get_chunks_id(i) _unsafe_bitsetindex!(Bc, x, i1, i2) end @inline function _unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i1::Int, i2::Int) u = UInt64(1) << i2 @inbounds begin c = Bc[i1] Bc[i1] = ifelse(x, c | u, c & ~u) end end @inline function setindex!(B::BitArray, x, i::Int) @boundscheck checkbounds(B, i) unsafe_bitsetindex!(B.chunks, convert(Bool, x), i) return B end indexoffset(i) = first(i)-1 indexoffset(::Colon) = 0 @propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, J0::Union{Colon,AbstractUnitRange{Int}}) _setindex!(IndexStyle(B), B, X, to_indices(B, (J0,))[1]) end # Assigning an array of bools is more complicated, but we can still do some # work on chunks by combining X and I 64 bits at a time to improve perf by ~40% @inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray) @boundscheck checkbounds(B, I) _unsafe_setindex!(B, X, I) end function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) Bc = B.chunks Ic = I.chunks length(Bc) == length(Ic) || throw_boundserror(B, I) lc = length(Bc) lx = length(X) last_chunk_len = _mod64(length(B)-1)+1 Xi = first(eachindex(X)) lastXi = last(eachindex(X)) for i = 1:lc @inbounds Imsk = Ic[i] @inbounds C = Bc[i] u = UInt64(1) for j = 1:(i < lc ? 64 : last_chunk_len) if Imsk & u != 0 Xi > lastXi && throw_setindex_mismatch(X, count(I)) @inbounds x = convert(Bool, X[Xi]) C = ifelse(x, C | u, C & ~u) Xi = nextind(X, Xi) end u <<= 1 end @inbounds Bc[i] = C end if Xi != nextind(X, lastXi) throw_setindex_mismatch(X, count(I)) end return B end ## Dequeue functionality ## function push!(B::BitVector, item) # convert first so we don't grow the bitarray if the assignment won't work item = convert(Bool, item) Bc = B.chunks l = _mod64(length(B)) if l == 0 _growend!(Bc, 1) Bc[end] = UInt64(0) end B.len += 1 if item B[end] = true end return B end function append!(B::BitVector, items::BitVector) n0 = length(B) n1 = length(items) n1 == 0 && return B Bc = B.chunks k0 = length(Bc) k1 = num_bit_chunks(n0 + n1) if k1 > k0 _growend!(Bc, k1 - k0) Bc[end] = UInt64(0) end B.len += n1 copy_chunks!(Bc, n0+1, items.chunks, 1, n1) return B end append!(B::BitVector, items) = append!(B, BitVector(items)) append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items)) function prepend!(B::BitVector, items::BitVector) n0 = length(B) n1 = length(items) n1 == 0 && return B Bc = B.chunks k0 = length(Bc) k1 = num_bit_chunks(n0 + n1) if k1 > k0 _growend!(Bc, k1 - k0) Bc[end] = UInt64(0) end B.len += n1 copy_chunks!(Bc, 1 + n1, Bc, 1, n0) copy_chunks!(Bc, 1, items.chunks, 1, n1) return B end prepend!(B::BitVector, items) = prepend!(B, BitArray(items)) prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items)) function sizehint!(B::BitVector, sz::Integer) ccall(:jl_array_sizehint, Cvoid, (Any, UInt), B.chunks, num_bit_chunks(sz)) return B end resize!(B::BitVector, n::Integer) = _resize_int!(B, Int(n)) function _resize_int!(B::BitVector, n::Int) n0 = length(B) n == n0 && return B n >= 0 || throw(BoundsError(B, n)) if n < n0 deleteat!(B, n+1:n0) return B end Bc = B.chunks k0 = length(Bc) k1 = num_bit_chunks(n) if k1 > k0 _growend!(Bc, k1 - k0) Bc[end] = UInt64(0) end B.len = n return B end function pop!(B::BitVector) isempty(B) && throw(ArgumentError("argument must not be empty")) item = B[end] B[end] = false l = _mod64(length(B)) l == 1 && _deleteend!(B.chunks, 1) B.len -= 1 return item end function pushfirst!(B::BitVector, item) item = convert(Bool, item) Bc = B.chunks l = _mod64(length(B)) if l == 0 _growend!(Bc, 1) Bc[end] = UInt64(0) end B.len += 1 if B.len == 1 Bc[1] = item return B end for i = length(Bc) : -1 : 2 Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63) end Bc[1] = UInt64(item) | (Bc[1] << 1) return B end function popfirst!(B::BitVector) isempty(B) && throw(ArgumentError("argument must not be empty")) @inbounds begin item = B[1] Bc = B.chunks for i = 1 : length(Bc) - 1 Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63) end l = _mod64(length(B)) if l == 1 _deleteend!(Bc, 1) else Bc[end] >>>= 1 end B.len -= 1 end return item end insert!(B::BitVector, i::Integer, item) = _insert_int!(B, Int(i), item) function _insert_int!(B::BitVector, i::Int, item) i = Int(i) n = length(B) 1 <= i <= n+1 || throw(BoundsError(B, i)) item = convert(Bool, item) Bc = B.chunks k, j = get_chunks_id(i) l = _mod64(length(B)) if l == 0 _growend!(Bc, 1) Bc[end] = UInt64(0) end B.len += 1 for t = length(Bc) : -1 : k + 1 Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63) end msk_aft = (_msk64 << j) msk_bef = ~msk_aft Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1) B[i] = item B end function _deleteat!(B::BitVector, i::Int) k, j = get_chunks_id(i) msk_bef = _msk64 >>> (63 - j) msk_aft = ~msk_bef msk_bef >>>= 1 Bc = B.chunks @inbounds begin Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1) if length(Bc) > k Bc[k] |= (Bc[k + 1] << 63) end for t = k + 1 : length(Bc) - 1 Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63) end l = _mod64(length(B)) if l == 1 _deleteend!(Bc, 1) elseif length(Bc) > k Bc[end] >>>= 1 end end B.len -= 1 return B end function deleteat!(B::BitVector, i::Integer) i isa Bool && depwarn("passing Bool as an index is deprecated", :deleteat!) i = Int(i) n = length(B) 1 <= i <= n || throw(BoundsError(B, i)) return _deleteat!(B, i) end function deleteat!(B::BitVector, r::AbstractUnitRange{Int}) n = length(B) i_f = first(r) i_l = last(r) 1 <= i_f || throw(BoundsError(B, i_f)) i_l <= n || throw(BoundsError(B, n+1)) Bc = B.chunks new_l = length(B) - length(r) delta_k = num_bit_chunks(new_l) - length(Bc) copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l) delta_k < 0 && _deleteend!(Bc, -delta_k) B.len = new_l if new_l > 0 Bc[end] &= _msk_end(new_l) end return B end function deleteat!(B::BitVector, inds) n = new_l = length(B) y = iterate(inds) y === nothing && return B Bc = B.chunks (p, s) = y checkbounds(B, p) p isa Bool && throw(ArgumentError("invalid index $p of type Bool")) q = p+1 new_l -= 1 y = iterate(inds, s) while y !== nothing (i, s) = y if !(q <= i <= n) i isa Bool && throw(ArgumentError("invalid index $i of type Bool")) i < q && throw(ArgumentError("indices must be unique and sorted")) throw(BoundsError(B, i)) end new_l -= 1 if i > q copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q)) p += i-q end q = i+1 y = iterate(inds, s) end q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n-q+1)) delta_k = num_bit_chunks(new_l) - length(Bc) delta_k < 0 && _deleteend!(Bc, -delta_k) B.len = new_l if new_l > 0 Bc[end] &= _msk_end(new_l) end return B end function deleteat!(B::BitVector, inds::AbstractVector{Bool}) length(inds) == length(B) || throw(BoundsError(B, inds)) n = new_l = length(B) y = findfirst(inds) y === nothing && return B Bc = B.chunks p = y s = y + 1 checkbounds(B, p) q = p + 1 new_l -= 1 y = findnext(inds, s) while y !== nothing i = y s = y + 1 new_l -= 1 if i > q copy_chunks!(Bc, Int(p), Bc, Int(q), Int(i-q)) p += i - q end q = i + 1 y = findnext(inds, s) end q <= n && copy_chunks!(Bc, Int(p), Bc, Int(q), Int(n - q + 1)) delta_k = num_bit_chunks(new_l) - length(Bc) delta_k < 0 && _deleteend!(Bc, -delta_k) B.len = new_l if new_l > 0 Bc[end] &= _msk_end(new_l) end return B end keepat!(B::BitVector, inds) = _keepat!(B, inds) keepat!(B::BitVector, inds::AbstractVector{Bool}) = _keepat!(B, inds) function splice!(B::BitVector, i::Integer) # TODO: after deprecation remove the four lines below # as v = B[i] is enough to do both bounds checking # and Bool check then just pass Int(i) to _deleteat! i isa Bool && depwarn("passing Bool as an index is deprecated", :splice!) i = Int(i) n = length(B) 1 <= i <= n || throw(BoundsError(B, i)) v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView _deleteat!(B, i) return v end const _default_bit_splice = BitVector() function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice) r isa Bool && depwarn("passing Bool as an index is deprecated", :splice!) _splice_int!(B, isa(r, AbstractUnitRange{Int}) ? r : Int(r), ins) end function _splice_int!(B::BitVector, r, ins) n = length(B) i_f, i_l = first(r), last(r) 1 <= i_f <= n+1 || throw(BoundsError(B, i_f)) i_l <= n || throw(BoundsError(B, n+1)) Bins = convert(BitArray, ins) if (i_f > n) append!(B, Bins) return BitVector() end v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView Bc = B.chunks lins = length(Bins) ldel = length(r) new_l = length(B) + lins - ldel delta_k = num_bit_chunks(new_l) - length(Bc) delta_k > 0 && _growend!(Bc, delta_k) copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l) copy_chunks!(Bc, i_f, Bins.chunks, 1, lins) delta_k < 0 && _deleteend!(Bc, -delta_k) B.len = new_l if new_l > 0 Bc[end] &= _msk_end(new_l) end return v end function splice!(B::BitVector, r::Union{AbstractUnitRange{Int}, Integer}, ins) Bins = BitVector(undef, length(ins)) i = 1 for x in ins Bins[i] = Bool(x) i += 1 end return splice!(B, r, Bins) end function empty!(B::BitVector) _deleteend!(B.chunks, length(B.chunks)) B.len = 0 return B end ## Unary operators ## function (-)(B::BitArray) A = zeros(Int, size(B)) l = length(B) l == 0 && return A Bc = B.chunks ind = 1 for i = 1:length(Bc)-1 u = UInt64(1) c = Bc[i] for j = 1:64 if c & u != 0 A[ind] = -1 end ind += 1 u <<= 1 end end u = UInt64(1) c = Bc[end] for j = 0:_mod64(l-1) if c & u != 0 A[ind] = -1 end ind += 1 u <<= 1 end return A end ## Binary arithmetic operators ## for f in (:+, :-) @eval function ($f)(A::BitArray, B::BitArray) r = Array{Int}(undef, promote_shape(size(A), size(B))) ay, by = iterate(A), iterate(B) ri = 1 # promote_shape guarantees that A and B have the # same iteration space while ay !== nothing @inbounds r[ri] = ($f)(ay[1], by[1]) ri += 1 ay, by = iterate(A, ay[2]), iterate(B, by[2]) end return r end end for f in (:/, :\) @eval begin ($f)(A::Union{BitMatrix,BitVector}, B::Union{BitMatrix,BitVector}) = ($f)(Array(A), Array(B)) end end (/)(B::BitArray, x::Number) = (/)(Array(B), x) (/)(x::Number, B::BitArray) = (/)(x, Array(B)) ## promotion to complex ## # TODO? ## comparison operators ## function (==)(A::BitArray, B::BitArray) size(A) != size(B) && return false return A.chunks == B.chunks end ## Data movement ## # TODO some of this could be optimized _reverse(A::BitArray, d::Tuple{Integer}) = _reverse(A, d[1]) function _reverse(A::BitArray, d::Int) nd = ndims(A) 1 โ‰ค d โ‰ค nd || throw(ArgumentError("dimension $d is not 1 โ‰ค $d โ‰ค $nd")) sd = size(A, d) sd == 1 && return copy(A) B = similar(A) nnd = 0 for i = 1:nd nnd += size(A,i)==1 || i==d end if nnd == nd # reverse along the only non-singleton dimension for i = 1:sd B[i] = A[sd+1-i] end return B end d_in = size(A) leading = d_in[1:(d-1)] M = prod(leading) N = length(A) stride = M * sd if M == 1 for j = 0:stride:(N-stride) for i = 1:sd ri = sd+1-i B[j + ri] = A[j + i] end end else for i = 1:sd ri = sd+1-i for j=0:stride:(N-stride) offs = j + 1 + (i-1)*M boffs = j + 1 + (ri-1)*M copy_chunks!(B.chunks, boffs, A.chunks, offs, M) end end end return B end function _reverse!(B::BitVector, ::Colon) # Basic idea: each chunk is divided into two blocks of size k = n % 64, and # h = 64 - k. Walk from either end (with indices i and j) reversing chunks # and separately ORing their two blocks into place. # # chunk 3 chunk 2 chunk 1 # โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” # โ”‚000000000000000โ”‚ E โ”‚โ”‚ D โ”‚ C โ”‚โ”‚ B โ”‚ A โ”‚ # โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ # k h k h k # yielding; # โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” # โ”‚000000000000000โ”‚ A' โ”‚โ”‚ B' โ”‚ C' โ”‚โ”‚ D' โ”‚ E' โ”‚ # โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ n = length(B) n == 0 && return B k = _mod64(n+63) + 1 h = 64 - k i, j = 0, length(B.chunks) u = UInt64(0) v = bitreverse(B.chunks[j]) B.chunks[j] = 0 @inbounds while true i += 1 if i == j break end u = bitreverse(B.chunks[i]) B.chunks[i] = 0 B.chunks[j] |= u >>> h B.chunks[i] |= v >>> h j -= 1 if i == j break end v = bitreverse(B.chunks[j]) B.chunks[j] = 0 B.chunks[i] |= v << k B.chunks[j] |= u << k end if isodd(length(B.chunks)) B.chunks[i] |= v >>> h else B.chunks[i] |= u << k end return B end function (<<)(B::BitVector, i::UInt) n = length(B) i == 0 && return copy(B) A = falses(n) i < n && copy_chunks!(A.chunks, 1, B.chunks, Int(i+1), Int(n-i)) return A end function (>>>)(B::BitVector, i::UInt) n = length(B) i == 0 && return copy(B) A = falses(n) i < n && copy_chunks!(A.chunks, Int(i+1), B.chunks, 1, Int(n-i)) return A end """ >>(B::BitVector, n) -> BitVector Right bit shift operator, `B >> n`. For `n >= 0`, the result is `B` with elements shifted `n` positions forward, filling with `false` values. If `n < 0`, elements are shifted backwards. Equivalent to `B << -n`. # Examples ```jldoctest julia> B = BitVector([true, false, true, false, false]) 5-element BitVector: 1 0 1 0 0 julia> B >> 1 5-element BitVector: 0 1 0 1 0 julia> B >> -1 5-element BitVector: 0 1 0 0 0 ``` """ (>>)(B::BitVector, i::Union{Int, UInt}) = B >>> i # signed integer version of shift operators with handling of negative values """ <<(B::BitVector, n) -> BitVector Left bit shift operator, `B << n`. For `n >= 0`, the result is `B` with elements shifted `n` positions backwards, filling with `false` values. If `n < 0`, elements are shifted forwards. Equivalent to `B >> -n`. # Examples ```jldoctest julia> B = BitVector([true, false, true, false, false]) 5-element BitVector: 1 0 1 0 0 julia> B << 1 5-element BitVector: 0 1 0 0 0 julia> B << -1 5-element BitVector: 0 1 0 1 0 ``` """ (<<)(B::BitVector, i::Int) = (i >=0 ? B << unsigned(i) : B >> unsigned(-i)) """ >>>(B::BitVector, n) -> BitVector Unsigned right bitshift operator, `B >>> n`. Equivalent to `B >> n`. See [`>>`](@ref) for details and examples. """ (>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i)) circshift!(dest::BitVector, src::BitVector, i::Integer) = _circshift_int!(dest, src, Int(i)) function _circshift_int!(dest::BitVector, src::BitVector, i::Int) i = Int(i) length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size")) n = length(dest) i %= n i == 0 && return (src === dest ? src : copyto!(dest, src)) Bc = (src === dest ? copy(src.chunks) : src.chunks) if i > 0 # right copy_chunks!(dest.chunks, i+1, Bc, 1, n-i) copy_chunks!(dest.chunks, 1, Bc, n-i+1, i) else # left i = -i copy_chunks!(dest.chunks, 1, Bc, i+1, n-i) copy_chunks!(dest.chunks, n-i+1, Bc, 1, i) end return dest end circshift!(B::BitVector, i::Integer) = circshift!(B, B, i) ## count & find ## function bitcount(Bc::Vector{UInt64}; init::T=0) where {T} n::T = init @inbounds for i = 1:length(Bc) n = (n + count_ones(Bc[i])) % T end return n end _count(::typeof(identity), B::BitArray, ::Colon, init) = bitcount(B.chunks; init) function unsafe_bitfindnext(Bc::Vector{UInt64}, start::Int) chunk_start = _div64(start-1)+1 within_chunk_start = _mod64(start-1) mask = _msk64 << within_chunk_start @inbounds begin if Bc[chunk_start] & mask != 0 return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1 end for i = chunk_start+1:length(Bc) if Bc[i] != 0 return (i-1) << 6 + trailing_zeros(Bc[i]) + 1 end end end return nothing end # returns the index of the next true element, or nothing if all false function findnext(B::BitArray, start::Integer) start = Int(start) start > 0 || throw(BoundsError(B, start)) start > length(B) && return nothing unsafe_bitfindnext(B.chunks, start) end #findfirst(B::BitArray) = findnext(B, 1) ## defined in array.jl # aux function: same as findnext(~B, start), but performed without temporaries function findnextnot(B::BitArray, start::Int) start > 0 || throw(BoundsError(B, start)) start > length(B) && return nothing Bc = B.chunks l = length(Bc) l == 0 && return nothing chunk_start = _div64(start-1)+1 within_chunk_start = _mod64(start-1) mask = ~(_msk64 << within_chunk_start) @inbounds if chunk_start < l if Bc[chunk_start] | mask != _msk64 return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1 end for i = chunk_start+1:l-1 if Bc[i] != _msk64 return (i-1) << 6 + trailing_ones(Bc[i]) + 1 end end if Bc[l] != _msk_end(B) return (l-1) << 6 + trailing_ones(Bc[l]) + 1 end elseif Bc[l] | mask != _msk_end(B) return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1 end return nothing end findfirstnot(B::BitArray) = findnextnot(B,1) # returns the index of the first matching element function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool}, B::BitArray, start::Integer) v = pred.x v == false && return findnextnot(B, Int(start)) v == true && return findnext(B, start) return nothing end #findfirst(B::BitArray, v) = findnext(B, 1, v) ## defined in array.jl # returns the index of the first element for which the function returns true findnext(testf::Function, B::BitArray, start::Integer) = _findnext_int(testf, B, Int(start)) function _findnext_int(testf::Function, B::BitArray, start::Int) f0::Bool = testf(false) f1::Bool = testf(true) !f0 && f1 && return findnext(B, start) f0 && !f1 && return findnextnot(B, start) start > 0 || throw(BoundsError(B, start)) start > length(B) && return nothing f0 && f1 && return start return nothing # last case: !f0 && !f1 end #findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1) ## defined in array.jl function unsafe_bitfindprev(Bc::Vector{UInt64}, start::Int) chunk_start = _div64(start-1)+1 mask = _msk_end(start) @inbounds begin if Bc[chunk_start] & mask != 0 return (chunk_start-1) << 6 + (top_set_bit(Bc[chunk_start] & mask)) end for i = (chunk_start-1):-1:1 if Bc[i] != 0 return (i-1) << 6 + (top_set_bit(Bc[i])) end end end return nothing end # returns the index of the previous true element, or nothing if all false function findprev(B::BitArray, start::Integer) start = Int(start) start > 0 || return nothing start > length(B) && throw(BoundsError(B, start)) unsafe_bitfindprev(B.chunks, start) end function findprevnot(B::BitArray, start::Int) start = Int(start) start > 0 || return nothing start > length(B) && throw(BoundsError(B, start)) Bc = B.chunks chunk_start = _div64(start-1)+1 mask = ~_msk_end(start) @inbounds begin if Bc[chunk_start] | mask != _msk64 return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask)) end for i = chunk_start-1:-1:1 if Bc[i] != _msk64 return (i-1) << 6 + (64 - leading_ones(Bc[i])) end end end return nothing end findlastnot(B::BitArray) = findprevnot(B, length(B)) # returns the index of the previous matching element function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool}, B::BitArray, start::Integer) v = pred.x v == false && return findprevnot(B, Int(start)) v == true && return findprev(B, start) return nothing end #findlast(B::BitArray, v) = findprev(B, 1, v) ## defined in array.jl # returns the index of the previous element for which the function returns true findprev(testf::Function, B::BitArray, start::Integer) = _findprev_int(testf, B, Int(start)) function _findprev_int(testf::Function, B::BitArray, start::Int) f0::Bool = testf(false) f1::Bool = testf(true) !f0 && f1 && return findprev(B, start) f0 && !f1 && return findprevnot(B, start) start > 0 || return nothing start > length(B) && throw(BoundsError(B, start)) f0 && f1 && return start return nothing # last case: !f0 && !f1 end #findlast(testf::Function, B::BitArray) = findprev(testf, B, 1) ## defined in array.jl function findmax(a::BitArray) isempty(a) && throw(ArgumentError("BitArray must be non-empty")) m, mi = false, 1 ti = 1 ac = a.chunks for i = 1:length(ac) @inbounds k = trailing_zeros(ac[i]) ti += k k == 64 || return (true, @inbounds keys(a)[ti]) end return m, @inbounds keys(a)[mi] end function findmin(a::BitArray) isempty(a) && throw(ArgumentError("BitArray must be non-empty")) m, mi = true, 1 ti = 1 ac = a.chunks for i = 1:length(ac)-1 @inbounds k = trailing_ones(ac[i]) ti += k k == 64 || return (false, @inbounds keys(a)[ti]) end l = Base._mod64(length(a)-1) + 1 @inbounds k = trailing_ones(ac[end] & Base._msk_end(l)) ti += k k == l || return (false, @inbounds keys(a)[ti]) return (m, @inbounds keys(a)[mi]) end # findall helper functions # Generic case (>2 dimensions) function allindices!(I, B::BitArray) ind = first(keys(B)) for k = 1:length(B) I[k] = ind ind = nextind(B, ind) end end # Optimized case for vector function allindices!(I, B::BitVector) I[:] .= 1:length(B) end # Optimized case for matrix function allindices!(I, B::BitMatrix) k = 1 for c = 1:size(B,2), r = 1:size(B,1) I[k] = CartesianIndex(r, c) k += 1 end end @inline _overflowind(i1, irest::Tuple{}, size) = (i1, irest) @inline function _overflowind(i1, irest, size) i2 = irest[1] while i1 > size[1] i1 -= size[1] i2 += 1 end i2, irest = _overflowind(i2, tail(irest), tail(size)) return (i1, (i2, irest...)) end @inline _toind(i1, irest::Tuple{}) = i1 @inline _toind(i1, irest) = CartesianIndex(i1, irest...) function findall(B::BitArray) nnzB = count(B) I = Vector{eltype(keys(B))}(undef, nnzB) nnzB == 0 && return I nnzB == length(B) && (allindices!(I, B); return I) Bc = B.chunks Bs = size(B) Bi = i1 = i = 1 irest = ntuple(one, ndims(B) - 1) c = Bc[1] @inbounds while true while c == 0 Bi == length(Bc) && return I i1 += 64 Bi += 1 c = Bc[Bi] end tz = trailing_zeros(c) c = _blsr(c) i1, irest = _overflowind(i1 + tz, irest, Bs) I[i] = _toind(i1, irest) i += 1 i1 -= tz end end # For performance findall(::typeof(!iszero), B::BitArray) = findall(B) ## Reductions ## _sum(A::BitArray, dims) = reduce(+, A, dims=dims) _sum(B::BitArray, ::Colon) = count(B) function all(B::BitArray) isempty(B) && return true Bc = B.chunks @inbounds begin for i = 1:length(Bc)-1 Bc[i] == _msk64 || return false end Bc[end] == _msk_end(B) || return false end return true end function any(B::BitArray) isempty(B) && return false Bc = B.chunks @inbounds begin for i = 1:length(Bc) Bc[i] == 0 || return true end end return false end minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B) maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B) ## map over bitarrays ## # Specializing map is even more important for bitarrays than it is for generic # arrays since there can be a 64x speedup by working at the level of Int64 # instead of looping bit-by-bit. map(::Union{typeof(~), typeof(!)}, A::BitArray) = bit_map!(~, similar(A), A) map(::typeof(zero), A::BitArray) = fill!(similar(A), false) map(::typeof(one), A::BitArray) = fill!(similar(A), true) map(::typeof(identity), A::BitArray) = copy(A) map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A) map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false) map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true) map!(::typeof(identity), dest::BitArray, A::BitArray) = copyto!(dest, A) for (T, f) in ((:(Union{typeof(&), typeof(*), typeof(min)}), :(&)), (:(Union{typeof(|), typeof(max)}), :(|)), (:(Union{typeof(xor), typeof(!=)}), :xor), (:(typeof(nand)), :nand), (:(typeof(nor)), :nor), (:(Union{typeof(>=), typeof(^)}), :((p, q) -> p | ~q)), (:(typeof(<=)), :((p, q) -> ~p | q)), (:(typeof(==)), :((p, q) -> ~xor(p, q))), (:(typeof(<)), :((p, q) -> ~p & q)), (:(typeof(>)), :((p, q) -> p & ~q))) @eval map(::$T, A::BitArray, B::BitArray) = bit_map!($f, similar(A), A, B) @eval map!(::$T, dest::BitArray, A::BitArray, B::BitArray) = bit_map!($f, dest, A, B) end # If we were able to specialize the function to a known bitwise operation, # map across the chunks. Otherwise, fall-back to the AbstractArray method that # iterates bit-by-bit. function bit_map!(f::F, dest::BitArray, A::BitArray) where F length(A) <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of collection")) isempty(A) && return dest destc = dest.chunks Ac = A.chunks len_Ac = length(Ac) for i = 1:(len_Ac-1) destc[i] = f(Ac[i]) end # the last effected UInt64's original content dest_last = destc[len_Ac] _msk = _msk_end(A) # first zero out the bits mask is going to change # then update bits by `or`ing with a masked RHS # DO NOT SEPARATE ONTO TO LINES. # Otherwise there will be bugs when Ac aliases destc destc[len_Ac] = (dest_last & (~_msk)) | f(Ac[len_Ac]) & _msk dest end function bit_map!(f::F, dest::BitArray, A::BitArray, B::BitArray) where F min_bitlen = min(length(A), length(B)) min_bitlen <= length(dest) || throw(DimensionMismatch("length of destination must be >= length of smallest input collection")) isempty(A) && return dest isempty(B) && return dest destc = dest.chunks Ac = A.chunks Bc = B.chunks len_Ac = min(length(Ac), length(Bc)) for i = 1:len_Ac-1 destc[i] = f(Ac[i], Bc[i]) end # the last effected UInt64's original content dest_last = destc[len_Ac] _msk = _msk_end(min_bitlen) # first zero out the bits mask is going to change # then update bits by `or`ing with a masked RHS # DO NOT SEPARATE ONTO TO LINES. # Otherwise there will be bugs when Ac or Bc aliases destc destc[len_Ac] = (dest_last & ~(_msk)) | f(Ac[end], Bc[end]) & _msk dest end ## Filter ## function filter(f, Bs::BitArray) boolmap::Array{Bool} = map(f, Bs) Bs[boolmap] end ## Concatenation ## function hcat(B::BitVector...) height = length(B[1]) for j = 2:length(B) length(B[j]) == height || throw(DimensionMismatch("dimensions must match: $j-th argument has length $(length(B[j])), should have $height")) end M = BitMatrix(undef, height, length(B)) for j = 1:length(B) copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height) end return M end function vcat(V::BitVector...) n = 0 for Vk in V n += length(Vk) end B = BitVector(undef, n) j = 1 for Vk in V copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk)) j += length(Vk) end return B end function hcat(A::Union{BitMatrix,BitVector}...) nargs = length(A) nrows = size(A[1], 1) ncols = 0 dense = true for j = 1:nargs Aj = A[j] nd = ndims(Aj) ncols += (nd==2 ? size(Aj,2) : 1) size(Aj, 1) == nrows || throw(DimensionMismatch("row lengths must match: $j-th element has first dim $(size(Aj, 1)), should have $nrows")) end B = BitMatrix(undef, nrows, ncols) pos = 1 for k = 1:nargs Ak = A[k] n = length(Ak) copy_chunks!(B.chunks, pos, Ak.chunks, 1, n) pos += n end return B end function vcat(A::BitMatrix...) nargs = length(A) nrows, nrowsA = 0, sizehint!(Int[], nargs) for a in A sz1 = size(a, 1) nrows += sz1 push!(nrowsA, sz1) end ncols = size(A[1], 2) for j = 2:nargs size(A[j], 2) == ncols || throw(DimensionMismatch("column lengths must match: $j-th element has second dim $(size(A[j], 2)), should have $ncols")) end B = BitMatrix(undef, nrows, ncols) Bc = B.chunks pos_d = 1 pos_s = fill(1, nargs) for j = 1:ncols, k = 1:nargs copy_chunks!(Bc, pos_d, A[k].chunks, pos_s[k], nrowsA[k]) pos_s[k] += nrowsA[k] pos_d += nrowsA[k] end return B end # general case, specialized for BitArrays and Integers _cat(dims::Integer, X::Union{BitArray, Bool}...) = _cat(Int(dims)::Int, X...) function _cat(dims::Int, X::Union{BitArray, Bool}...) dims = Int(dims) catdims = dims2cat(dims) shape = cat_shape(catdims, map(cat_size, X)) A = falses(shape) return __cat(A, shape, catdims, X...) end # hvcat -> use fallbacks in abstractarray.jl # BitArray I/O write(s::IO, B::BitArray) = write(s, B.chunks) function read!(s::IO, B::BitArray) n = length(B) Bc = B.chunks nc = length(read!(s, Bc)) if length(Bc) > 0 && Bc[end] & _msk_end(n) โ‰  Bc[end] Bc[end] &= _msk_end(n) # ensure that the BitArray is not broken throw(DimensionMismatch("read mismatch, found non-zero bits after BitArray length")) end return B end sizeof(B::BitArray) = sizeof(B.chunks) function _split_rest(a::Union{Vector, BitVector}, n::Int) _check_length_split_rest(length(a), n) last_n = a[end-n+1:end] resize!(a, length(a) - n) return a, last_n end M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/bitset.jl๚0# This file is a part of Julia. License is MIT: https://julialang.org/license const Bits = Vector{UInt64} const CHK0 = zero(UInt64) const NO_OFFSET = Int === Int64 ? -one(Int) << 60 : -one(Int) << 29 # + NO_OFFSET must be small enough to stay < 0 when added with any offset. # An offset is in the range -2^57:2^57 (64-bits architectures) # or -2^26:2^26 (32-bits architectures) # + when the offset is NO_OFFSET, the bits field *must* be empty # + NO_OFFSET could be made to be > 0, but a negative one allows # a small optimization in the in(x, ::BitSet) method mutable struct BitSet <: AbstractSet{Int} const bits::Vector{UInt64} # 1st stored Int equals 64*offset offset::Int BitSet() = new(resize!(Vector{UInt64}(undef, 4), 0), NO_OFFSET) end """ BitSet([itr]) Construct a sorted set of `Int`s generated by the given iterable object, or an empty set. Implemented as a bit string, and therefore designed for dense integer sets. If the set will be sparse (for example, holding a few very large integers), use [`Set`](@ref) instead. """ BitSet(itr) = union!(BitSet(), itr) # Special implementation for BitSet, which lacks a fast `length` method. function union!(s::BitSet, itr) for x in itr push!(s, x) end return s end @inline intoffset(s::BitSet) = s.offset << 6 empty(s::BitSet, ::Type{Int}=Int) = BitSet() emptymutable(s::BitSet, ::Type{Int}=Int) = BitSet() copy(s1::BitSet) = copy!(BitSet(), s1) copymutable(s::BitSet) = copy(s) function copy!(dest::BitSet, src::BitSet) resize!(dest.bits, length(src.bits)) copyto!(dest.bits, src.bits) dest.offset = src.offset dest end sizehint!(s::BitSet, n::Integer) = (sizehint!(s.bits, (n+63) >> 6); s) function _bits_getindex(b::Bits, n::Int, offset::Int) ci = _div64(n) - offset + 1 1 <= ci <= length(b) || return false @inbounds r = (b[ci] & (one(UInt64) << _mod64(n))) != 0 r end function _bits_findnext(b::Bits, start::Int) # start is 0-based # @assert start >= 0 _div64(start) + 1 > length(b) && return -1 ind = unsafe_bitfindnext(b, start+1) ind === nothing ? -1 : ind - 1 end function _bits_findprev(b::Bits, start::Int) # start is 0-based # @assert start <= 64 * length(b) - 1 start >= 0 || return -1 ind = unsafe_bitfindprev(b, start+1) ind === nothing ? -1 : ind - 1 end # An internal function for setting the inclusion bit for a given integer @inline function _setint!(s::BitSet, idx::Int, b::Bool) cidx = _div64(idx) len = length(s.bits) diff = cidx - s.offset if diff >= len b || return s # setting a bit to zero outside the set's bits is a no-op # we put the following test within one of the two branches, # with the NO_OFFSET trick, to avoid having to perform it at # each and every call to _setint! if s.offset == NO_OFFSET # initialize the offset # we assume isempty(s.bits) s.offset = cidx diff = 0 end _growend0!(s.bits, diff - len + 1) elseif diff < 0 b || return s _growbeg0!(s.bits, -diff) s.offset += diff diff = 0 end _unsafe_bitsetindex!(s.bits, b, diff+1, _mod64(idx)) s end # An internal function to resize a Bits object and ensure the newly allocated # elements are zeroed (will become unnecessary if this behavior changes) @inline function _growend0!(b::Bits, nchunks::Int) len = length(b) _growend!(b, nchunks) for i in len+1:length(b) @inbounds b[i] = CHK0 # resize! gives dirty memory end end @inline function _growbeg0!(b::Bits, nchunks::Int) _growbeg!(b, nchunks) for i in 1:nchunks @inbounds b[i] = CHK0 end end function union!(s::BitSet, r::AbstractUnitRange{<:Integer}) isempty(r) && return s a, b = Int(first(r)), Int(last(r)) cidxa = _div64(a) cidxb = _div64(b) if s.offset == NO_OFFSET s.offset = cidxa end len = length(s.bits) diffa = cidxa - s.offset diffb = cidxb - s.offset # grow s.bits as necessary if diffb >= len _growend0!(s.bits, diffb - len + 1) end if diffa < 0 _growbeg0!(s.bits, -diffa) s.offset = cidxa # s.offset += diffa diffb -= diffa diffa = 0 end # update s.bits i = _mod64(a) j = _mod64(b) @inbounds if diffa == diffb s.bits[diffa + 1] |= (((~CHK0) >> i) << (i+63-j)) >> (63-j) else s.bits[diffa + 1] |= ((~CHK0) >> i) << i s.bits[diffb + 1] |= (~CHK0 << (63-j)) >> (63-j) for n = diffa+1:diffb-1 s.bits[n+1] = ~CHK0 end end s end function _matched_map!(f, s1::BitSet, s2::BitSet) left_false_is_false = f(false, false) == f(false, true) == false right_false_is_false = f(false, false) == f(true, false) == false # we must first handle the NO_OFFSET case; we could test for # isempty(s1) but it can be costly, so the user has to call # empty!(s1) herself before-hand to re-initialize to NO_OFFSET if s1.offset == NO_OFFSET return left_false_is_false ? s1 : copy!(s1, s2) elseif s2.offset == NO_OFFSET return right_false_is_false ? empty!(s1) : s1 end s1.offset = _matched_map!(f, s1.bits, s1.offset, s2.bits, s2.offset, left_false_is_false, right_false_is_false) s1 end # An internal function that takes a pure function `f` and maps across two BitArrays # allowing the lengths and offsets to be different and altering b1 with the result # WARNING: the assumptions written in the else clauses must hold function _matched_map!(f, a1::Bits, b1::Int, a2::Bits, b2::Int, left_false_is_false::Bool, right_false_is_false::Bool) l1, l2 = length(a1), length(a2) bdiff = b2 - b1 e1, e2 = l1+b1, l2+b2 ediff = e2 - e1 # map! over the common indices @inbounds for i = max(1, 1+bdiff):min(l1, l2+bdiff) a1[i] = f(a1[i], a2[i-bdiff]) end if ediff > 0 if left_false_is_false # We don't need to worry about the trailing bits โ€” they're all false else # @assert f(false, x) == x _growend!(a1, ediff) # if a1 and a2 are not overlapping, we infer implied "false" values from a2 for outer l1 = l1+1:bdiff @inbounds a1[l1] = CHK0 end # update ediff in case l1 was updated ediff = e2 - l1 - b1 # copy actual chunks from a2 unsafe_copyto!(a1, l1+1, a2, l2+1-ediff, ediff) l1 = length(a1) end elseif ediff < 0 if right_false_is_false # We don't need to worry about the trailing bits โ€” they're all false _deleteend!(a1, min(l1, -ediff)) # no need to update l1, as if bdiff > 0 (case below), then bdiff will # be smaller anyway than an updated l1 else # @assert f(x, false) == x # We don't need to worry about the trailing bits โ€” they already have the # correct value end end if bdiff < 0 if left_false_is_false # We don't need to worry about the leading bits โ€” they're all false else # @assert f(false, x) == x _growbeg!(a1, -bdiff) # if a1 and a2 are not overlapping, we infer implied "false" values from a2 for i = l2+1:-bdiff @inbounds a1[i] = CHK0 end b1 += bdiff # updated return value # copy actual chunks from a2 unsafe_copyto!(a1, 1, a2, 1, min(-bdiff, l2)) end elseif bdiff > 0 if right_false_is_false # We don't need to worry about the trailing bits โ€” they're all false _deletebeg!(a1, min(l1, bdiff)) b1 += bdiff else # @assert f(x, false) == x # We don't need to worry about the trailing bits โ€” they already have the # correct value end end b1 # the new offset end @inline push!(s::BitSet, n::Integer) = _setint!(s, Int(n), true) push!(s::BitSet, ns::Integer...) = (for n in ns; push!(s, n); end; s) @inline pop!(s::BitSet) = pop!(s, last(s)) @inline function pop!(s::BitSet, n::Integer) if n in s delete!(s, n) n else throw(KeyError(n)) end end @inline function pop!(s::BitSet, n::Integer, default) if n in s delete!(s, n) n else default end end @inline _is_convertible_Int(n) = typemin(Int) <= n <= typemax(Int) @inline delete!(s::BitSet, n::Int) = _setint!(s, n, false) @inline delete!(s::BitSet, n::Integer) = _is_convertible_Int(n) ? delete!(s, Int(n)) : s popfirst!(s::BitSet) = pop!(s, first(s)) function empty!(s::BitSet) empty!(s.bits) s.offset = NO_OFFSET s end isempty(s::BitSet) = _check0(s.bits, 1, length(s.bits)) # Mathematical set functions: union!, intersect!, setdiff!, symdiff! union(s::BitSet, sets...) = union!(copy(s), sets...) union!(s1::BitSet, s2::BitSet) = _matched_map!(|, s1, s2) intersect(s1::BitSet, s2::BitSet) = length(s1.bits) < length(s2.bits) ? intersect!(copy(s1), s2) : intersect!(copy(s2), s1) intersect!(s1::BitSet, s2::BitSet) = _matched_map!(&, s1, s2) setdiff!(s1::BitSet, s2::BitSet) = _matched_map!((p, q) -> p & ~q, s1, s2) function symdiff!(s::BitSet, ns) for x in ns int_symdiff!(s, x) end return s end function symdiff!(s::BitSet, ns::AbstractSet) for x in ns int_symdiff!(s, x) end return s end function int_symdiff!(s::BitSet, n::Integer) n0 = Int(n) val = !(n0 in s) _setint!(s, n0, val) s end symdiff!(s1::BitSet, s2::BitSet) = _matched_map!(xor, s1, s2) filter!(f, s::BitSet) = unsafe_filter!(f, s) @inline in(n::Int, s::BitSet) = _bits_getindex(s.bits, n, s.offset) @inline in(n::Integer, s::BitSet) = _is_convertible_Int(n) ? in(Int(n), s) : false function iterate(s::BitSet, (word, idx) = (CHK0, 0)) while word == 0 idx == length(s.bits) && return nothing idx += 1 word = @inbounds s.bits[idx] end trailing_zeros(word) + (idx - 1 + s.offset) << 6, (_blsr(word), idx) end @noinline _throw_bitset_notempty_error() = throw(ArgumentError("collection must be non-empty")) function first(s::BitSet) idx = _bits_findnext(s.bits, 0) idx == -1 ? _throw_bitset_notempty_error() : idx + intoffset(s) end function last(s::BitSet) idx = _bits_findprev(s.bits, (length(s.bits) << 6) - 1) idx == -1 ? _throw_bitset_notempty_error() : idx + intoffset(s) end length(s::BitSet) = bitcount(s.bits) # = mapreduce(count_ones, +, s.bits; init=0) function show(io::IO, s::BitSet) print(io, "BitSet([") first = true for n in s !first && print(io, ", ") print(io, n) first = false end print(io, "])") end function _check0(a::Vector{UInt64}, b::Int, e::Int) @inbounds for i in b:e a[i] == CHK0 || return false end true end function ==(s1::BitSet, s2::BitSet) # Swap so s1 has always the smallest offset if s1.offset > s2.offset s1, s2 = s2, s1 end a1 = s1.bits a2 = s2.bits b1, b2 = s1.offset, s2.offset l1, l2 = length(a1), length(a2) e1 = l1+b1 overlap0 = max(0, e1 - b2) included = overlap0 >= l2 # whether a2's indices are included in a1's overlap = included ? l2 : overlap0 # Ensure non-overlap chunks are zero (unlikely) _check0(a1, 1, l1-overlap0) || return false if included _check0(a1, b2-b1+l2+1, l1) || return false else _check0(a2, 1+overlap, l2) || return false end # compare overlap values if overlap > 0 t1 = @_gc_preserve_begin a1 t2 = @_gc_preserve_begin a2 memcmp(pointer(a1, b2-b1+1), pointer(a2), overlap<<3) == 0 || return false @_gc_preserve_end t2 @_gc_preserve_end t1 end return true end function issubset(a::BitSet, b::BitSet) n = length(a.bits) shift = b.offset - a.offset i, j = shift, shift + length(b.bits) f(a, b) = a == a & b return ( all(@inbounds iszero(a.bits[i]) for i in 1:min(n, i)) && all(@inbounds f(a.bits[i], b.bits[i - shift]) for i in max(1, i+1):min(n, j)) && all(@inbounds iszero(a.bits[i]) for i in max(1, j+1):n)) end โŠŠ(a::BitSet, b::BitSet) = a <= b && a != b minimum(s::BitSet) = first(s) maximum(s::BitSet) = last(s) extrema(s::BitSet) = (first(s), last(s)) issorted(s::BitSet) = true Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/multimedia.jl™># This file is a part of Julia. License is MIT: https://julialang.org/license module Multimedia import .Base: show, print, convert, repr export AbstractDisplay, display, pushdisplay, popdisplay, displayable, redisplay, MIME, @MIME_str, istextmime, showable, TextDisplay ########################################################################### # We define a singleton type MIME{mime symbol} for each MIME type, so # that Julia's dispatch and overloading mechanisms can be used to # dispatch show and to add conversions for new types. """ MIME A type representing a standard internet data format. "MIME" stands for "Multipurpose Internet Mail Extensions", since the standard was originally used to describe multimedia attachments to email messages. A `MIME` object can be passed as the second argument to [`show`](@ref) to request output in that format. # Examples ```jldoctest julia> show(stdout, MIME("text/plain"), "hi") "hi" ``` """ struct MIME{mime} end """ @MIME_str A convenience macro for writing [`MIME`](@ref) types, typically used when adding methods to [`show`](@ref). For example the syntax `show(io::IO, ::MIME"text/html", x::MyType) = ...` could be used to define how to write an HTML representation of `MyType`. """ macro MIME_str(s) :(MIME{$(Expr(:quote, Symbol(s)))}) end # fallback text/plain representation of any type: show(io::IO, ::MIME"text/plain", x) = show(io, x) MIME(s) = MIME{Symbol(s)}() show(io::IO, ::MIME{mime}) where {mime} = print(io, "MIME type ", string(mime)) print(io::IO, ::MIME{mime}) where {mime} = print(io, mime) ########################################################################### # For any type T one can define show(io, ::MIME"type", x::T) = ... # in order to provide a way to export T as a given mime type. """ showable(mime, x) Return a boolean value indicating whether or not the object `x` can be written as the given `mime` type. (By default, this is determined automatically by the existence of the corresponding [`show`](@ref) method for `typeof(x)`. Some types provide custom `showable` methods; for example, if the available MIME formats depend on the *value* of `x`.) # Examples ```jldoctest julia> showable(MIME("text/plain"), rand(5)) true julia> showable("image/png", rand(5)) false ``` """ showable(::MIME{mime}, @nospecialize x) where {mime} = hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)}) showable(m::AbstractString, @nospecialize x) = showable(MIME(m), x) """ show(io::IO, mime, x) The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a given `mime` type to a given I/O stream `io` (usually a memory buffer), if possible. In order to provide a rich multimedia representation of a user-defined type `T`, it is only necessary to define a new `show` method for `T`, via: `show(io, ::MIME"mime", x::T) = ...`, where `mime` is a MIME-type string and the function body calls [`write`](@ref) (or similar) to write that representation of `x` to `io`. (Note that the `MIME""` notation only supports literal strings; to construct `MIME` types in a more flexible manner use `MIME{Symbol("")}`.) For example, if you define a `MyImage` type and know how to write it to a PNG file, you could define a function `show(io, ::MIME"image/png", x::MyImage) = ...` to allow your images to be displayed on any PNG-capable `AbstractDisplay` (such as IJulia). As usual, be sure to `import Base.show` in order to add new methods to the built-in Julia function `show`. Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string, which allows us to exploit Julia's dispatch mechanisms in determining how to display objects of any given type. The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` output that calls `show` with 2 arguments, so it is not always necessary to add a method for that case. If a type benefits from custom human-readable output though, `show(::IO, ::MIME"text/plain", ::T)` should be defined. For example, the `Day` type uses `1 day` as the output for the `text/plain` MIME type, and `Day(1)` as the output of 2-argument `show`. # Examples ```jldoctest julia> struct Day n::Int end julia> Base.show(io::IO, ::MIME"text/plain", d::Day) = print(io, d.n, " day") julia> Day(1) 1 day ``` Container types generally implement 3-argument `show` by calling `show(io, MIME"text/plain"(), x)` for elements `x`, with `:compact => true` set in an [`IOContext`](@ref) passed as the first argument. """ show(stream::IO, mime, x) show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) """ repr(mime, x; context=nothing) Return an `AbstractString` or `Vector{UInt8}` containing the representation of `x` in the requested `mime` type, as written by [`show(io, mime, x)`](@ref) (throwing a [`MethodError`](@ref) if no appropriate `show` is available). An `AbstractString` is returned for MIME types with textual representations (such as `"text/html"` or `"application/postscript"`), whereas binary data is returned as `Vector{UInt8}`. (The function `istextmime(mime)` returns whether or not Julia treats a given `mime` type as text.) The optional keyword argument `context` can be set to `:key=>value` pair or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O stream passed to `show`. As a special case, if `x` is an `AbstractString` (for textual MIME types) or a `Vector{UInt8}` (for binary MIME types), the `repr` function assumes that `x` is already in the requested `mime` format and simply returns `x`. This special case does not apply to the `"text/plain"` MIME type. This is useful so that raw data can be passed to `display(m::MIME, x)`. In particular, `repr("text/plain", x)` is typically a "pretty-printed" version of `x` designed for human consumption. See also [`repr(x)`](@ref) to instead return a string corresponding to [`show(x)`](@ref) that may be closer to how the value of `x` would be entered in Julia. # Examples ```jldoctest julia> A = [1 2; 3 4]; julia> repr("text/plain", A) "2ร—2 Matrix{Int64}:\\n 1 2\\n 3 4" ``` """ repr(m::MIME, x; context=nothing) = istextmime(m) ? _textrepr(m, x, context) : _binrepr(m, x, context) repr(m::AbstractString, x; context=nothing) = repr(MIME(m), x; context=context) # strings are shown escaped for text/plain _textrepr(m::MIME, x, context) = String(__binrepr(m, x, context)) _textrepr(::MIME, x::AbstractString, context) = x _textrepr(m::MIME"text/plain", x::AbstractString, context) = String(__binrepr(m, x, context)) function __binrepr(m::MIME, x, context) s = IOBuffer() if context === nothing show(s, m, x) else show(IOContext(s, context), m, x) end take!(s) end _binrepr(m::MIME, x, context) = __binrepr(m, x, context) _binrepr(m::MIME, x::Vector{UInt8}, context) = x """ istextmime(m::MIME) Determine whether a MIME type is text data. MIME types are assumed to be binary data except for a set of types known to be text data (possibly Unicode). # Examples ```jldoctest julia> istextmime(MIME("text/plain")) true julia> istextmime(MIME("image/png")) false ``` """ istextmime(m::MIME) = startswith(string(m), "text/") istextmime(m::AbstractString) = istextmime(MIME(m)) for mime in ["application/atom+xml", "application/ecmascript", "application/javascript", "application/julia", "application/json", "application/postscript", "application/rdf+xml", "application/rss+xml", "application/x-latex", "application/xhtml+xml", "application/xml", "application/xml-dtd", "image/svg+xml", "model/vrml", "model/x3d+vrml", "model/x3d+xml"] global istextmime(::MIME{Symbol(mime)}) = true end ########################################################################### # We have an abstract AbstractDisplay class that can be subclassed in order to # define new rich-display output devices. A typical subclass should # overload display(d::AbstractDisplay, m::MIME, x) for supported MIME types m, # (typically using show, repr, ..., to get the MIME # representation of x) and should also overload display(d::AbstractDisplay, x) # to display x in whatever MIME type is preferred by the AbstractDisplay and # is writable by x. display(..., x) should throw a MethodError if x # cannot be displayed. The return value of display(...) is up to the # AbstractDisplay type. """ AbstractDisplay Abstract supertype for rich display output devices. [`TextDisplay`](@ref) is a subtype of this. """ abstract type AbstractDisplay end # it is convenient to accept strings instead of ::MIME display(d::AbstractDisplay, mime::AbstractString, @nospecialize x) = display(d, MIME(mime), x) display(mime::AbstractString, @nospecialize x) = display(MIME(mime), x) """ displayable(mime) -> Bool displayable(d::AbstractDisplay, mime) -> Bool Return a boolean value indicating whether the given `mime` type (string) is displayable by any of the displays in the current display stack, or specifically by the display `d` in the second variant. """ displayable(d::AbstractDisplay, mime::AbstractString) = displayable(d, MIME(mime)) displayable(mime::AbstractString) = displayable(MIME(mime)) # simplest display, which only knows how to display text/plain """ TextDisplay(io::IO) Return a `TextDisplay <: AbstractDisplay`, which displays any object as the text/plain MIME type (by default), writing the text representation to the given I/O stream. (This is how objects are printed in the Julia REPL.) """ struct TextDisplay <: AbstractDisplay io::IO end display(d::TextDisplay, M::MIME"text/plain", @nospecialize x) = (show(d.io, M, x); println(d.io)) display(d::TextDisplay, @nospecialize x) = display(d, MIME"text/plain"(), x) # if you explicitly call display("text/foo", x), it should work on a TextDisplay: displayable(d::TextDisplay, M::MIME) = istextmime(M) function display(d::TextDisplay, M::MIME, @nospecialize x) displayable(d, M) || throw(MethodError(display, (d, M, x))) show(d.io, M, x); println(d.io) end import Base: close, flush flush(d::TextDisplay) = flush(d.io) close(d::TextDisplay) = close(d.io) ########################################################################### # We keep a stack of Displays, and calling display(x) uses the topmost # AbstractDisplay that is capable of displaying x (doesn't throw an error) const displays = AbstractDisplay[] """ pushdisplay(d::AbstractDisplay) Pushes a new display `d` on top of the global display-backend stack. Calling `display(x)` or `display(mime, x)` will display `x` on the topmost compatible backend in the stack (i.e., the topmost backend that does not throw a [`MethodError`](@ref)). """ function pushdisplay(d::AbstractDisplay) global displays push!(displays, d) end """ popdisplay() popdisplay(d::AbstractDisplay) Pop the topmost backend off of the display-backend stack, or the topmost copy of `d` in the second variant. """ popdisplay() = pop!(displays) function popdisplay(d::AbstractDisplay) for i = length(displays):-1:1 if d == displays[i] return splice!(displays, i) end end throw(KeyError(d)) end function reinit_displays() empty!(displays) pushdisplay(TextDisplay(stdout)) end xdisplayable(D::AbstractDisplay, @nospecialize args...) = applicable(display, D, args...) """ display(x) display(d::AbstractDisplay, x) display(mime, x) display(d::AbstractDisplay, mime, x) Display `x` using the topmost applicable display in the display stack, typically using the richest supported multimedia output for `x`, with plain-text [`stdout`](@ref) output as a fallback. The `display(d, x)` variant attempts to display `x` on the given display `d` only, throwing a [`MethodError`](@ref) if `d` cannot display objects of this type. In general, you cannot assume that `display` output goes to `stdout` (unlike [`print(x)`](@ref) or [`show(x)`](@ref)). For example, `display(x)` may open up a separate window with an image. `display(x)` means "show `x` in the best way you can for the current output device(s)." If you want REPL-like text output that is guaranteed to go to `stdout`, use [`show(stdout, "text/plain", x)`](@ref) instead. There are also two variants with a `mime` argument (a MIME type string, such as `"image/png"`), which attempt to display `x` using the requested MIME type *only*, throwing a `MethodError` if this type is not supported by either the display(s) or by `x`. With these variants, one can also supply the "raw" data in the requested MIME type by passing `x::AbstractString` (for MIME types with text-based storage, such as text/html or application/postscript) or `x::Vector{UInt8}` (for binary MIME types). To customize how instances of a type are displayed, overload [`show`](@ref) rather than `display`, as explained in the manual section on [custom pretty-printing](@ref man-custom-pretty-printing). """ function display(@nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], x) try return display(displays[i], x) catch e isa(e, MethodError) && (e.f === display || e.f === show) || rethrow() end end end throw(MethodError(display, (x,))) end function display(m::MIME, @nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], m, x) try return display(displays[i], m, x) catch e isa(e, MethodError) && e.f == display || rethrow() end end end throw(MethodError(display, (m, x))) end displayable(d::D, ::MIME{mime}) where {D<:AbstractDisplay,mime} = hasmethod(display, Tuple{D,MIME{mime},Any}) function displayable(m::MIME) for d in displays displayable(d, m) && return true end return false end ########################################################################### # The redisplay method can be overridden by a AbstractDisplay in order to # update an existing display (instead of, for example, opening a new # window), and is used by the IJulia interface to defer display # until the next interactive prompt. This is especially useful # for Matlab/Pylab-like stateful plotting interfaces, where # a plot is created and then modified many times (xlabel, title, etc.). """ redisplay(x) redisplay(d::AbstractDisplay, x) redisplay(mime, x) redisplay(d::AbstractDisplay, mime, x) By default, the `redisplay` functions simply call [`display`](@ref). However, some display backends may override `redisplay` to modify an existing display of `x` (if any). Using `redisplay` is also a hint to the backend that `x` may be redisplayed several times, and the backend may choose to defer the display until (for example) the next interactive prompt. """ function redisplay(@nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], x) try return redisplay(displays[i], x) catch e isa(e, MethodError) && e.f in (redisplay, display, show) || rethrow() end end end throw(MethodError(redisplay, (x,))) end function redisplay(m::Union{MIME,AbstractString}, @nospecialize x) for i = length(displays):-1:1 if xdisplayable(displays[i], m, x) try return redisplay(displays[i], m, x) catch e isa(e, MethodError) && e.f in (redisplay, display) || rethrow() end end end throw(MethodError(redisplay, (m, x))) end # default redisplay is simply to call display redisplay(d::AbstractDisplay, @nospecialize x) = display(d, x) redisplay(d::AbstractDisplay, m::Union{MIME,AbstractString}, @nospecialize x) = display(d, m, x) ########################################################################### end # module K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/some.jlˆ # This file is a part of Julia. License is MIT: https://julialang.org/license """ Some{T} A wrapper type used in `Union{Some{T}, Nothing}` to distinguish between the absence of a value ([`nothing`](@ref)) and the presence of a `nothing` value (i.e. `Some(nothing)`). Use [`something`](@ref) to access the value wrapped by a `Some` object. """ struct Some{T} value::T end Some(::Type{T}) where {T} = Some{Type{T}}(T) promote_rule(::Type{Some{T}}, ::Type{Some{S}}) where {T, S<:T} = Some{T} nonnothingtype(::Type{T}) where {T} = typesplit(T, Nothing) promote_rule(T::Type{Nothing}, S::Type) = Union{S, Nothing} function promote_rule(T::Type{>:Nothing}, S::Type) R = nonnothingtype(T) R >: T && return Any T = R R = promote_type(T, S) return Union{R, Nothing} end function nonnothingtype_checked(T::Type) R = nonnothingtype(T) R >: T && error("could not compute non-nothing type") R <: Union{} && error("cannot convert a value to nothing for assignment") return R end convert(::Type{T}, x::T) where {T>:Nothing} = x convert(::Type{T}, x) where {T>:Nothing} = convert(nonnothingtype_checked(T), x) convert(::Type{Some{T}}, x::Some{T}) where {T} = x convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value))::Some{T} function show(io::IO, x::Some) if get(io, :typeinfo, Any) == typeof(x) show(io, x.value) else print(io, "Some(") show(io, x.value) print(io, ')') end end """ notnothing(x) Throw an error if `x === nothing`, and return `x` if not. """ notnothing(x::Any) = x notnothing(::Nothing) = throw(ArgumentError("nothing passed to notnothing")) """ isnothing(x) Return `true` if `x === nothing`, and return `false` if not. !!! compat "Julia 1.1" This function requires at least Julia 1.1. See also [`something`](@ref), [`Base.notnothing`](@ref), [`ismissing`](@ref). """ isnothing(x) = x === nothing """ something(x...) Return the first value in the arguments which is not equal to [`nothing`](@ref), if any. Otherwise throw an error. Arguments of type [`Some`](@ref) are unwrapped. See also [`coalesce`](@ref), [`skipmissing`](@ref), [`@something`](@ref). # Examples ```jldoctest julia> something(nothing, 1) 1 julia> something(Some(1), nothing) 1 julia> something(Some(nothing), 2) === nothing true julia> something(missing, nothing) missing julia> something(nothing, nothing) ERROR: ArgumentError: No value arguments present ``` """ function something end something() = throw(ArgumentError("No value arguments present")) something(x::Nothing, y...) = something(y...) something(x::Some, y...) = x.value something(x::Any, y...) = x """ @something(x...) Short-circuiting version of [`something`](@ref). # Examples ```jldoctest julia> f(x) = (println("f(\$x)"); nothing); julia> a = 1; julia> a = @something a f(2) f(3) error("Unable to find default for `a`") 1 julia> b = nothing; julia> b = @something b f(2) f(3) error("Unable to find default for `b`") f(2) f(3) ERROR: Unable to find default for `b` [...] julia> b = @something b f(2) f(3) Some(nothing) f(2) f(3) julia> b === nothing true ``` !!! compat "Julia 1.7" This macro is available as of Julia 1.7. """ macro something(args...) expr = :(nothing) for arg in reverse(args) expr = :(val = $(esc(arg)); val !== nothing ? val : ($expr)) end something = GlobalRef(Base, :something) return :($something($expr)) end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/dict.jlZZ# This file is a part of Julia. License is MIT: https://julialang.org/license function show(io::IO, t::AbstractDict{K,V}) where V where K recur_io = IOContext(io, :SHOWN_SET => t, :typeinfo => eltype(t)) limit = get(io, :limit, false)::Bool # show in a Julia-syntax-like form: Dict(k=>v, ...) print(io, typeinfo_prefix(io, t)[1]) print(io, '(') if !isempty(t) && !show_circular(io, t) first = true n = 0 for pair in t first || print(io, ", ") first = false show(recur_io, pair) n+=1 limit && n >= 10 && (print(io, "โ€ฆ"); break) end end print(io, ')') end # Dict # These can be changed, to trade off better performance for space const global maxallowedprobe = 16 const global maxprobeshift = 6 """ Dict([itr]) `Dict{K,V}()` constructs a hash table with keys of type `K` and values of type `V`. Keys are compared with [`isequal`](@ref) and hashed with [`hash`](@ref). Given a single iterable argument, constructs a [`Dict`](@ref) whose key-value pairs are taken from 2-tuples `(key,value)` generated by the argument. # Examples ```jldoctest julia> Dict([("A", 1), ("B", 2)]) Dict{String, Int64} with 2 entries: "B" => 2 "A" => 1 ``` Alternatively, a sequence of pair arguments may be passed. ```jldoctest julia> Dict("A"=>1, "B"=>2) Dict{String, Int64} with 2 entries: "B" => 2 "A" => 1 ``` """ mutable struct Dict{K,V} <: AbstractDict{K,V} # Metadata: empty => 0x00, removed => 0x7f, full => 0b1[7 most significant hash bits] slots::Vector{UInt8} keys::Array{K,1} vals::Array{V,1} ndel::Int count::Int age::UInt idxfloor::Int # an index <= the indices of all used slots maxprobe::Int function Dict{K,V}() where V where K n = 16 new(zeros(UInt8,n), Vector{K}(undef, n), Vector{V}(undef, n), 0, 0, 0, n, 0) end function Dict{K,V}(d::Dict{K,V}) where V where K new(copy(d.slots), copy(d.keys), copy(d.vals), d.ndel, d.count, d.age, d.idxfloor, d.maxprobe) end function Dict{K, V}(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) where {K, V} new(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) end end function Dict{K,V}(kv) where V where K h = Dict{K,V}() haslength(kv) && sizehint!(h, Int(length(kv))::Int) for (k,v) in kv h[k] = v end return h end Dict{K,V}(p::Pair) where {K,V} = setindex!(Dict{K,V}(), p.second, p.first) function Dict{K,V}(ps::Pair...) where V where K h = Dict{K,V}() sizehint!(h, length(ps)) for p in ps h[p.first] = p.second end return h end # Note the constructors of WeakKeyDict mirror these here, keep in sync. Dict() = Dict{Any,Any}() Dict(kv::Tuple{}) = Dict() copy(d::Dict) = Dict(d) const AnyDict = Dict{Any,Any} Dict(ps::Pair{K,V}...) where {K,V} = Dict{K,V}(ps) Dict(ps::Pair...) = Dict(ps) function Dict(kv) try dict_with_eltype((K, V) -> Dict{K, V}, kv, eltype(kv)) catch if !isiterable(typeof(kv)) || !all(x->isa(x,Union{Tuple,Pair}),kv) throw(ArgumentError("Dict(kv): kv needs to be an iterator of tuples or pairs")) else rethrow() end end end function grow_to!(dest::AbstractDict{K, V}, itr) where V where K y = iterate(itr) y === nothing && return dest ((k,v), st) = y dest2 = empty(dest, typeof(k), typeof(v)) dest2[k] = v grow_to!(dest2, itr, st) end # this is a special case due to (1) allowing both Pairs and Tuples as elements, # and (2) Pair being invariant. a bit annoying. function grow_to!(dest::AbstractDict{K,V}, itr, st) where V where K y = iterate(itr, st) while y !== nothing (k,v), st = y if isa(k,K) && isa(v,V) dest[k] = v else new = empty(dest, promote_typejoin(K,typeof(k)), promote_typejoin(V,typeof(v))) merge!(new, dest) new[k] = v return grow_to!(new, itr, st) end y = iterate(itr, st) end return dest end empty(a::AbstractDict, ::Type{K}, ::Type{V}) where {K, V} = Dict{K, V}() # Gets 7 most significant bits from the hash (hsh), first bit is 1 _shorthash7(hsh::UInt) = (hsh >> (8sizeof(UInt)-7))%UInt8 | 0x80 # hashindex (key, sz) - computes optimal position and shorthash7 # idx - optimal position in the hash table # sh::UInt8 - short hash (7 highest hash bits) function hashindex(key, sz) hsh = hash(key)::UInt idx = (((hsh % Int) & (sz-1)) + 1)::Int return idx, _shorthash7(hsh) end @propagate_inbounds isslotempty(h::Dict, i::Int) = h.slots[i] == 0x00 @propagate_inbounds isslotfilled(h::Dict, i::Int) = (h.slots[i] & 0x80) != 0 @propagate_inbounds isslotmissing(h::Dict, i::Int) = h.slots[i] == 0x7f @constprop :none function rehash!(h::Dict{K,V}, newsz = length(h.keys)) where V where K olds = h.slots oldk = h.keys oldv = h.vals sz = length(olds) newsz = _tablesz(newsz) h.age += 1 h.idxfloor = 1 if h.count == 0 resize!(h.slots, newsz) fill!(h.slots, 0x0) resize!(h.keys, newsz) resize!(h.vals, newsz) h.ndel = 0 h.maxprobe = 0 return h end slots = zeros(UInt8,newsz) keys = Vector{K}(undef, newsz) vals = Vector{V}(undef, newsz) age0 = h.age count = 0 maxprobe = 0 for i = 1:sz @inbounds if (olds[i] & 0x80) != 0 k = oldk[i] v = oldv[i] index, sh = hashindex(k, newsz) index0 = index while slots[index] != 0 index = (index & (newsz-1)) + 1 end probe = (index - index0) & (newsz-1) probe > maxprobe && (maxprobe = probe) slots[index] = olds[i] keys[index] = k vals[index] = v count += 1 end end @assert h.age == age0 "Multiple concurrent writes to Dict detected!" h.age += 1 h.slots = slots h.keys = keys h.vals = vals h.count = count h.ndel = 0 h.maxprobe = maxprobe return h end function sizehint!(d::Dict{T}, newsz) where T oldsz = length(d.slots) # limit new element count to max_values of the key type newsz = min(max(newsz, length(d)), max_values(T)::Int) # need at least 1.5n space to hold n elements newsz = _tablesz(cld(3 * newsz, 2)) return newsz == oldsz ? d : rehash!(d, newsz) end """ empty!(collection) -> collection Remove all elements from a `collection`. # Examples ```jldoctest julia> A = Dict("a" => 1, "b" => 2) Dict{String, Int64} with 2 entries: "b" => 2 "a" => 1 julia> empty!(A); julia> A Dict{String, Int64}() ``` """ function empty!(h::Dict{K,V}) where V where K fill!(h.slots, 0x0) sz = length(h.slots) empty!(h.keys) empty!(h.vals) resize!(h.keys, sz) resize!(h.vals, sz) h.ndel = 0 h.count = 0 h.maxprobe = 0 h.age += 1 h.idxfloor = sz return h end # get the index where a key is stored, or -1 if not present @assume_effects :terminates_locally function ht_keyindex(h::Dict{K,V}, key) where V where K isempty(h) && return -1 sz = length(h.keys) iter = 0 maxprobe = h.maxprobe maxprobe < sz || throw(AssertionError()) # This error will never trigger, but is needed for terminates_locally to be valid index, sh = hashindex(key, sz) keys = h.keys @inbounds while true isslotempty(h,index) && return -1 if h.slots[index] == sh k = keys[index] if (key === k || isequal(key, k)) return index end end index = (index & (sz-1)) + 1 (iter += 1) > maxprobe && return -1 end # This line is unreachable end # get (index, sh) for the key # index - where a key is stored, or -pos if not present # and the key would be inserted at pos # sh::UInt8 - short hash (7 highest hash bits) # This version is for use by setindex! and get! function ht_keyindex2_shorthash!(h::Dict{K,V}, key) where V where K sz = length(h.keys) iter = 0 maxprobe = h.maxprobe index, sh = hashindex(key, sz) avail = 0 keys = h.keys @inbounds while true if isslotempty(h,index) return (avail < 0 ? avail : -index), sh end if isslotmissing(h,index) if avail == 0 # found an available slot, but need to keep scanning # in case "key" already exists in a later collided slot. avail = -index end elseif h.slots[index] == sh k = keys[index] if key === k || isequal(key, k) return index, sh end end index = (index & (sz-1)) + 1 iter += 1 iter > maxprobe && break end avail < 0 && return avail, sh maxallowed = max(maxallowedprobe, sz>>maxprobeshift) # Check if key is not present, may need to keep searching to find slot @inbounds while iter < maxallowed if !isslotfilled(h,index) h.maxprobe = iter return -index, sh end index = (index & (sz-1)) + 1 iter += 1 end rehash!(h, h.count > 64000 ? sz*2 : sz*4) return ht_keyindex2_shorthash!(h, key) end # Only for better backward compatibility. It can be removed in the future. ht_keyindex2!(h::Dict, key) = ht_keyindex2_shorthash!(h, key)[1] @propagate_inbounds function _setindex!(h::Dict, v, key, index, sh = _shorthash7(hash(key))) h.ndel -= isslotmissing(h, index) h.slots[index] = sh h.keys[index] = key h.vals[index] = v h.count += 1 h.age += 1 if index < h.idxfloor h.idxfloor = index end sz = length(h.keys) # Rehash now if necessary if (h.count + h.ndel)*3 > sz*2 # > 2/3 full (including tombstones) rehash!(h, h.count > 64000 ? h.count*2 : h.count*4) end nothing end function setindex!(h::Dict{K,V}, v0, key0) where V where K if key0 isa K key = key0 else key = convert(K, key0)::K if !(isequal(key, key0)::Bool) throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) end end setindex!(h, v0, key) end function setindex!(h::Dict{K,V}, v0, key::K) where V where K v = v0 isa V ? v0 : convert(V, v0)::V index, sh = ht_keyindex2_shorthash!(h, key) if index > 0 h.age += 1 @inbounds h.keys[index] = key @inbounds h.vals[index] = v else @inbounds _setindex!(h, v, key, -index, sh) end return h end function setindex!(h::Dict{K,Any}, v, key::K) where K @nospecialize v index, sh = ht_keyindex2_shorthash!(h, key) if index > 0 h.age += 1 @inbounds h.keys[index] = key @inbounds h.vals[index] = v else @inbounds _setindex!(h, v, key, -index, sh) end return h end """ get!(collection, key, default) Return the value stored for the given key, or if no mapping for the key is present, store `key => default`, and return `default`. # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2, "c"=>3); julia> get!(d, "a", 5) 1 julia> get!(d, "d", 4) 4 julia> d Dict{String, Int64} with 4 entries: "c" => 3 "b" => 2 "a" => 1 "d" => 4 ``` """ get!(collection, key, default) """ get!(f::Union{Function, Type}, collection, key) Return the value stored for the given key, or if no mapping for the key is present, store `key => f()`, and return `f()`. This is intended to be called using `do` block syntax. # Examples ```jldoctest julia> squares = Dict{Int, Int}(); julia> function get_square!(d, i) get!(d, i) do i^2 end end get_square! (generic function with 1 method) julia> get_square!(squares, 2) 4 julia> squares Dict{Int64, Int64} with 1 entry: 2 => 4 ``` """ get!(f::Callable, collection, key) function get!(default::Callable, h::Dict{K,V}, key0) where V where K if key0 isa K key = key0 else key = convert(K, key0)::K if !isequal(key, key0) throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) end end return get!(default, h, key) end function get!(default::Callable, h::Dict{K,V}, key::K) where V where K index, sh = ht_keyindex2_shorthash!(h, key) index > 0 && return h.vals[index] age0 = h.age v = default() if !isa(v, V) v = convert(V, v)::V end if h.age != age0 index, sh = ht_keyindex2_shorthash!(h, key) end if index > 0 h.age += 1 @inbounds h.keys[index] = key @inbounds h.vals[index] = v else @inbounds _setindex!(h, v, key, -index, sh) end return v end function getindex(h::Dict{K,V}, key) where V where K index = ht_keyindex(h, key) @inbounds return (index < 0) ? throw(KeyError(key)) : h.vals[index]::V end """ get(collection, key, default) Return the value stored for the given key, or the given default value if no mapping for the key is present. !!! compat "Julia 1.7" For tuples and numbers, this function requires at least Julia 1.7. # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2); julia> get(d, "a", 3) 1 julia> get(d, "c", 3) 3 ``` """ get(collection, key, default) function get(h::Dict{K,V}, key, default) where V where K index = ht_keyindex(h, key) @inbounds return (index < 0) ? default : h.vals[index]::V end """ get(f::Union{Function, Type}, collection, key) Return the value stored for the given key, or if no mapping for the key is present, return `f()`. Use [`get!`](@ref) to also store the default value in the dictionary. This is intended to be called using `do` block syntax ```julia get(dict, key) do # default value calculated here time() end ``` """ get(::Callable, collection, key) function get(default::Callable, h::Dict{K,V}, key) where V where K index = ht_keyindex(h, key) @inbounds return (index < 0) ? default() : h.vals[index]::V end """ haskey(collection, key) -> Bool Determine whether a collection has a mapping for a given `key`. # Examples ```jldoctest julia> D = Dict('a'=>2, 'b'=>3) Dict{Char, Int64} with 2 entries: 'a' => 2 'b' => 3 julia> haskey(D, 'a') true julia> haskey(D, 'c') false ``` """ haskey(h::Dict, key) = (ht_keyindex(h, key) >= 0) in(key, v::KeySet{<:Any, <:Dict}) = (ht_keyindex(v.dict, key) >= 0) """ getkey(collection, key, default) Return the key matching argument `key` if one exists in `collection`, otherwise return `default`. # Examples ```jldoctest julia> D = Dict('a'=>2, 'b'=>3) Dict{Char, Int64} with 2 entries: 'a' => 2 'b' => 3 julia> getkey(D, 'a', 1) 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> getkey(D, 'd', 'a') 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) ``` """ function getkey(h::Dict{K,V}, key, default) where V where K index = ht_keyindex(h, key) @inbounds return (index<0) ? default : h.keys[index]::K end function _pop!(h::Dict, index) @inbounds val = h.vals[index] _delete!(h, index) return val end function pop!(h::Dict, key) index = ht_keyindex(h, key) return index > 0 ? _pop!(h, index) : throw(KeyError(key)) end """ pop!(collection, key[, default]) Delete and return the mapping for `key` if it exists in `collection`, otherwise return `default`, or throw an error if `default` is not specified. # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2, "c"=>3); julia> pop!(d, "a") 1 julia> pop!(d, "d") ERROR: KeyError: key "d" not found Stacktrace: [...] julia> pop!(d, "e", 4) 4 ``` """ pop!(collection, key, default) function pop!(h::Dict, key, default) index = ht_keyindex(h, key) return index > 0 ? _pop!(h, index) : default end function pop!(h::Dict) isempty(h) && throw(ArgumentError("dict must be non-empty")) idx = skip_deleted_floor!(h) @inbounds key = h.keys[idx] @inbounds val = h.vals[idx] _delete!(h, idx) key => val end function _delete!(h::Dict{K,V}, index) where {K,V} @inbounds begin slots = h.slots sz = length(slots) _unsetindex!(h.keys, index) _unsetindex!(h.vals, index) # if the next slot is empty we don't need a tombstone # and can remove all tombstones that were required by the element we just deleted ndel = 1 nextind = (index & (sz-1)) + 1 if isslotempty(h, nextind) while true ndel -= 1 slots[index] = 0x00 index = ((index - 2) & (sz-1)) + 1 isslotmissing(h, index) || break end else slots[index] = 0x7f end h.ndel += ndel h.count -= 1 h.age += 1 return h end end """ delete!(collection, key) Delete the mapping for the given key in a collection, if any, and return the collection. # Examples ```jldoctest julia> d = Dict("a"=>1, "b"=>2) Dict{String, Int64} with 2 entries: "b" => 2 "a" => 1 julia> delete!(d, "b") Dict{String, Int64} with 1 entry: "a" => 1 julia> delete!(d, "b") # d is left unchanged Dict{String, Int64} with 1 entry: "a" => 1 ``` """ delete!(collection, key) function delete!(h::Dict, key) index = ht_keyindex(h, key) if index > 0 _delete!(h, index) end return h end function skip_deleted(h::Dict, i) L = length(h.slots) for i = i:L @inbounds if isslotfilled(h,i) return i end end return 0 end function skip_deleted_floor!(h::Dict) idx = skip_deleted(h, h.idxfloor) if idx != 0 h.idxfloor = idx end idx end @propagate_inbounds _iterate(t::Dict{K,V}, i) where {K,V} = i == 0 ? nothing : (Pair{K,V}(t.keys[i],t.vals[i]), i == typemax(Int) ? 0 : i+1) @propagate_inbounds function iterate(t::Dict) _iterate(t, skip_deleted(t, t.idxfloor)) end @propagate_inbounds iterate(t::Dict, i) = _iterate(t, skip_deleted(t, i)) isempty(t::Dict) = (t.count == 0) length(t::Dict) = t.count @propagate_inbounds function Base.iterate(v::T, i::Int = v.dict.idxfloor) where T <: Union{KeySet{<:Any, <:Dict}, ValueIterator{<:Dict}} i == 0 && return nothing i = skip_deleted(v.dict, i) i == 0 && return nothing vals = T <: KeySet ? v.dict.keys : v.dict.vals (@inbounds vals[i], i == typemax(Int) ? 0 : i+1) end function filter!(pred, h::Dict{K,V}) where {K,V} h.count == 0 && return h @inbounds for i=1:length(h.slots) if ((h.slots[i] & 0x80) != 0) && !pred(Pair{K,V}(h.keys[i], h.vals[i])) _delete!(h, i) end end return h end function reduce(::typeof(merge), items::Vector{<:Dict}) K = mapreduce(keytype, promote_type, items) V = mapreduce(valtype, promote_type, items) return reduce(merge!, items; init=Dict{K,V}()) end function map!(f, iter::ValueIterator{<:Dict}) dict = iter.dict vals = dict.vals # @inbounds is here so that it gets propagated to isslotfilled @inbounds for i = dict.idxfloor:lastindex(vals) if isslotfilled(dict, i) vals[i] = f(vals[i]) end end return iter end function mergewith!(combine, d1::Dict{K, V}, d2::AbstractDict) where {K, V} haslength(d2) && sizehint!(d1, length(d1) + length(d2)) for (k, v) in d2 i, sh = ht_keyindex2_shorthash!(d1, k) if i > 0 d1.vals[i] = combine(d1.vals[i], v) else if !(k isa K) k1 = convert(K, k)::K if !isequal(k, k1) throw(ArgumentError("$(limitrepr(k)) is not a valid key for type $K")) end k = k1 end if !isa(v, V) v = convert(V, v)::V end @inbounds _setindex!(d1, v, k, -i, sh) end end return d1 end struct ImmutableDict{K,V} <: AbstractDict{K,V} parent::ImmutableDict{K,V} key::K value::V ImmutableDict{K,V}() where {K,V} = new() # represents an empty dictionary ImmutableDict{K,V}(key, value) where {K,V} = (empty = new(); new(empty, key, value)) ImmutableDict{K,V}(parent::ImmutableDict, key, value) where {K,V} = new(parent, key, value) end """ ImmutableDict `ImmutableDict` is a dictionary implemented as an immutable linked list, which is optimal for small dictionaries that are constructed over many individual insertions. Note that it is not possible to remove a value, although it can be partially overridden and hidden by inserting a new value with the same key. ImmutableDict(KV::Pair) Create a new entry in the `ImmutableDict` for a `key => value` pair - use `(key => value) in dict` to see if this particular combination is in the properties set - use `get(dict, key, default)` to retrieve the most recent value for a particular key """ ImmutableDict ImmutableDict(KV::Pair{K,V}) where {K,V} = ImmutableDict{K,V}(KV[1], KV[2]) ImmutableDict(t::ImmutableDict{K,V}, KV::Pair) where {K,V} = ImmutableDict{K,V}(t, KV[1], KV[2]) ImmutableDict(t::ImmutableDict{K,V}, KV::Pair, rest::Pair...) where {K,V} = ImmutableDict(ImmutableDict(t, KV), rest...) ImmutableDict(KV::Pair, rest::Pair...) = ImmutableDict(ImmutableDict(KV), rest...) function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) key, value = key_value while isdefined(dict, :parent) if isequal(dict.key, key) valcmp(value, dict.value) && return true end dict = dict.parent end return false end function haskey(dict::ImmutableDict, key) while isdefined(dict, :parent) isequal(dict.key, key) && return true dict = dict.parent end return false end function getindex(dict::ImmutableDict, key) while isdefined(dict, :parent) isequal(dict.key, key) && return dict.value dict = dict.parent end throw(KeyError(key)) end function get(dict::ImmutableDict, key, default) while isdefined(dict, :parent) isequal(dict.key, key) && return dict.value dict = dict.parent end return default end function get(default::Callable, dict::ImmutableDict, key) while isdefined(dict, :parent) isequal(dict.key, key) && return dict.value dict = dict.parent end return default() end # this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) function iterate(d::ImmutableDict{K,V}, t=d) where {K, V} !isdefined(t, :parent) && return nothing (Pair{K,V}(t.key, t.value), t.parent) end length(t::ImmutableDict) = count(Returns(true), t) isempty(t::ImmutableDict) = !isdefined(t, :parent) empty(::ImmutableDict, ::Type{K}, ::Type{V}) where {K, V} = ImmutableDict{K,V}() _similar_for(c::AbstractDict, ::Type{Pair{K,V}}, itr, isz, len) where {K, V} = empty(c, K, V) _similar_for(c::AbstractDict, ::Type{T}, itr, isz, len) where {T} = throw(ArgumentError("for AbstractDicts, similar requires an element type of Pair;\n if calling map, consider a comprehension instead")) R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/abstractset.jl’1# This file is a part of Julia. License is MIT: https://julialang.org/license eltype(::Type{<:AbstractSet{T}}) where {T} = @isdefined(T) ? T : Any sizehint!(s::AbstractSet, n) = s function copy!(dst::AbstractSet, src::AbstractSet) dst === src && return dst union!(empty!(dst), src) end ## set operations (union, intersection, symmetric difference) """ union(s, itrs...) โˆช(s, itrs...) Construct an object containing all distinct elements from all of the arguments. The first argument controls what kind of container is returned. If this is an array, it maintains the order in which elements first appear. Unicode `โˆช` can be typed by writing `\\cup` then pressing tab in the Julia REPL, and in many editors. This is an infix operator, allowing `s โˆช itr`. See also [`unique`](@ref), [`intersect`](@ref), [`isdisjoint`](@ref), [`vcat`](@ref), [`Iterators.flatten`](@ref). # Examples ```jldoctest julia> union([1, 2], [3]) 3-element Vector{Int64}: 1 2 3 julia> union([4 2 3 4 4], 1:3, 3.0) 4-element Vector{Float64}: 4.0 2.0 3.0 1.0 julia> (0, 0.0) โˆช (-0.0, NaN) 3-element Vector{Real}: 0 -0.0 NaN julia> union(Set([1, 2]), 2:3) Set{Int64} with 3 elements: 2 3 1 ``` """ function union end union(s, sets...) = union!(emptymutable(s, promote_eltype(s, sets...)), s, sets...) union(s::AbstractSet) = copy(s) const โˆช = union """ union!(s::Union{AbstractSet,AbstractVector}, itrs...) Construct the [`union`](@ref) of passed in sets and overwrite `s` with the result. Maintain order with arrays. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> a = Set([3, 4, 5]); julia> union!(a, 1:2:7); julia> a Set{Int64} with 5 elements: 5 4 7 3 1 ``` """ function union!(s::AbstractSet, sets...) for x in sets union!(s, x) end return s end max_values(::Type) = typemax(Int) max_values(T::Union{map(X -> Type{X}, BitIntegerSmall_types)...}) = 1 << (8*sizeof(T)) # saturated addition to prevent overflow with typemax(Int) function max_values(T::Union) a = max_values(T.a)::Int b = max_values(T.b)::Int return max(a, b, a + b) end max_values(::Type{Bool}) = 2 max_values(::Type{Nothing}) = 1 function union!(s::AbstractSet{T}, itr) where T haslength(itr) && sizehint!(s, length(s) + Int(length(itr))::Int) for x in itr push!(s, x) length(s) == max_values(T) && break end return s end """ intersect(s, itrs...) โˆฉ(s, itrs...) Construct the set containing those elements which appear in all of the arguments. The first argument controls what kind of container is returned. If this is an array, it maintains the order in which elements first appear. Unicode `โˆฉ` can be typed by writing `\\cap` then pressing tab in the Julia REPL, and in many editors. This is an infix operator, allowing `s โˆฉ itr`. See also [`setdiff`](@ref), [`isdisjoint`](@ref), [`issubset`](@ref Base.issubset), [`issetequal`](@ref). !!! compat "Julia 1.8" As of Julia 1.8 intersect returns a result with the eltype of the type-promoted eltypes of the two inputs # Examples ```jldoctest julia> intersect([1, 2, 3], [3, 4, 5]) 1-element Vector{Int64}: 3 julia> intersect([1, 4, 4, 5, 6], [6, 4, 6, 7, 8]) 2-element Vector{Int64}: 4 6 julia> intersect(1:16, 7:99) 7:16 julia> (0, 0.0) โˆฉ (-0.0, 0) 1-element Vector{Real}: 0 julia> intersect(Set([1, 2]), BitSet([2, 3]), 1.0:10.0) Set{Float64} with 1 element: 2.0 ``` """ function intersect(s::AbstractSet, itr, itrs...) # heuristics to try to `intersect` with the shortest Set on the left if length(s)>50 && haslength(itr) && all(haslength, itrs) min_length, min_idx = findmin(length, itrs) if length(itr) > min_length new_itrs = setindex(itrs, itr, min_idx) return intersect(s, itrs[min_idx], new_itrs...) end end T = promote_eltype(s, itr, itrs...) if T == promote_eltype(s, itr) out = intersect(s, itr) else out = union!(emptymutable(s, T), s) intersect!(out, itr) end return intersect!(out, itrs...) end intersect(s) = union(s) function intersect(s::AbstractSet, itr) if haslength(itr) && hasfastin(itr) && length(s) < length(itr) return mapfilter(in(itr), push!, s, emptymutable(s, promote_eltype(s, itr))) else return mapfilter(in(s), push!, itr, emptymutable(s, promote_eltype(s, itr))) end end const โˆฉ = intersect """ intersect!(s::Union{AbstractSet,AbstractVector}, itrs...) Intersect all passed in sets and overwrite `s` with the result. Maintain order with arrays. $(_DOCS_ALIASING_WARNING) """ function intersect!(s::AbstractSet, itrs...) for x in itrs intersect!(s, x) end return s end intersect!(s::AbstractSet, s2::AbstractSet) = filter!(in(s2), s) intersect!(s::AbstractSet, itr) = intersect!(s, union!(emptymutable(s, eltype(itr)), itr)) """ setdiff(s, itrs...) Construct the set of elements in `s` but not in any of the iterables in `itrs`. Maintain order with arrays. See also [`setdiff!`](@ref), [`union`](@ref) and [`intersect`](@ref). # Examples ```jldoctest julia> setdiff([1,2,3], [3,4,5]) 2-element Vector{Int64}: 1 2 ``` """ setdiff(s::AbstractSet, itrs...) = setdiff!(copymutable(s), itrs...) setdiff(s) = union(s) """ setdiff!(s, itrs...) Remove from set `s` (in-place) each element of each iterable from `itrs`. Maintain order with arrays. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> a = Set([1, 3, 4, 5]); julia> setdiff!(a, 1:2:6); julia> a Set{Int64} with 1 element: 4 ``` """ function setdiff!(s::AbstractSet, itrs...) for x in itrs setdiff!(s, x) end return s end function setdiff!(s::AbstractSet, itr) for x in itr delete!(s, x) end return s end """ symdiff(s, itrs...) Construct the symmetric difference of elements in the passed in sets. When `s` is not an `AbstractSet`, the order is maintained. See also [`symdiff!`](@ref), [`setdiff`](@ref), [`union`](@ref) and [`intersect`](@ref). # Examples ```jldoctest julia> symdiff([1,2,3], [3,4,5], [4,5,6]) 3-element Vector{Int64}: 1 2 6 julia> symdiff([1,2,1], [2, 1, 2]) Int64[] ``` """ symdiff(s, sets...) = symdiff!(emptymutable(s, promote_eltype(s, sets...)), s, sets...) symdiff(s) = symdiff!(copy(s)) """ symdiff!(s::Union{AbstractSet,AbstractVector}, itrs...) Construct the symmetric difference of the passed in sets, and overwrite `s` with the result. When `s` is an array, the order is maintained. Note that in this case the multiplicity of elements matters. $(_DOCS_ALIASING_WARNING) """ function symdiff!(s::AbstractSet, itrs...) for x in itrs symdiff!(s, x) end return s end symdiff!(s::AbstractSet, itr) = symdiff!(s::AbstractSet, Set(itr)) function symdiff!(s::AbstractSet, itr::AbstractSet) for x in itr x in s ? delete!(s, x) : push!(s, x) end return s end ## non-strict subset comparison const โІ = issubset function โЇ end """ issubset(a, b) -> Bool โІ(a, b) -> Bool โЇ(b, a) -> Bool Determine whether every element of `a` is also in `b`, using [`in`](@ref). See also [`โŠŠ`](@ref), [`โŠˆ`](@ref), [`โˆฉ`](@ref intersect), [`โˆช`](@ref union), [`contains`](@ref). # Examples ```jldoctest julia> issubset([1, 2], [1, 2, 3]) true julia> [1, 2, 3] โІ [1, 2] false julia> [1, 2, 3] โЇ [1, 2] true ``` """ issubset, โІ, โЇ const FASTIN_SET_THRESHOLD = 70 function issubset(a, b) if haslength(b) && (isa(a, AbstractSet) || !hasfastin(b)) blen = length(b) # conditions above make this length computed only when needed # check a for too many unique elements if isa(a, AbstractSet) && length(a) > blen return false end # when `in` would be too slow and b is big enough, convert it to a Set # this threshold was empirically determined (cf. #26198) if !hasfastin(b) && blen > FASTIN_SET_THRESHOLD return issubset(a, Set(b)) end end for elt in a elt in b || return false end return true end """ hasfastin(T) Determine whether the computation `x โˆˆ collection` where `collection::T` can be considered as a "fast" operation (typically constant or logarithmic complexity). The definition `hasfastin(x) = hasfastin(typeof(x))` is provided for convenience so that instances can be passed instead of types. However the form that accepts a type argument should be defined for new types. """ hasfastin(::Type) = false hasfastin(::Union{Type{<:AbstractSet},Type{<:AbstractDict},Type{<:AbstractRange}}) = true hasfastin(x) = hasfastin(typeof(x)) โЇ(a, b) = b โІ a ## strict subset comparison function โŠŠ end function โŠ‹ end """ โŠŠ(a, b) -> Bool โŠ‹(b, a) -> Bool Determines if `a` is a subset of, but not equal to, `b`. See also [`issubset`](@ref) (`โІ`), [`โŠˆ`](@ref). # Examples ```jldoctest julia> (1, 2) โŠŠ (1, 2, 3) true julia> (1, 2) โŠŠ (1, 2) false ``` """ โŠŠ, โŠ‹ โŠŠ(a::AbstractSet, b::AbstractSet) = length(a) < length(b) && a โІ b โŠŠ(a::AbstractSet, b) = a โŠŠ Set(b) โŠŠ(a, b::AbstractSet) = Set(a) โŠŠ b โŠŠ(a, b) = Set(a) โŠŠ Set(b) โŠ‹(a, b) = b โŠŠ a function โŠˆ end function โЉ end """ โŠˆ(a, b) -> Bool โЉ(b, a) -> Bool Negation of `โІ` and `โЇ`, i.e. checks that `a` is not a subset of `b`. See also [`issubset`](@ref) (`โІ`), [`โŠŠ`](@ref). # Examples ```jldoctest julia> (1, 2) โŠˆ (2, 3) true julia> (1, 2) โŠˆ (1, 2, 3) false ``` """ โŠˆ, โЉ โŠˆ(a, b) = !โІ(a, b) โЉ(a, b) = b โŠˆ a ## set equality comparison """ issetequal(a, b) -> Bool Determine whether `a` and `b` have the same elements. Equivalent to `a โІ b && b โІ a` but more efficient when possible. See also: [`isdisjoint`](@ref), [`union`](@ref). # Examples ```jldoctest julia> issetequal([1, 2], [1, 2, 3]) false julia> issetequal([1, 2], [2, 1]) true ``` """ issetequal(a::AbstractSet, b::AbstractSet) = a == b issetequal(a::AbstractSet, b) = issetequal(a, Set(b)) function issetequal(a, b::AbstractSet) if haslength(a) # check b for too many unique elements length(a) < length(b) && return false end return issetequal(Set(a), b) end function issetequal(a, b) haslength(a) && return issetequal(a, Set(b)) haslength(b) && return issetequal(b, Set(a)) return issetequal(Set(a), Set(b)) end ## set disjoint comparison """ isdisjoint(a, b) -> Bool Determine whether the collections `a` and `b` are disjoint. Equivalent to `isempty(a โˆฉ b)` but more efficient when possible. See also: [`intersect`](@ref), [`isempty`](@ref), [`issetequal`](@ref). !!! compat "Julia 1.5" This function requires at least Julia 1.5. # Examples ```jldoctest julia> isdisjoint([1, 2], [2, 3, 4]) false julia> isdisjoint([3, 1], [2, 4]) true ``` """ function isdisjoint(a, b) function _isdisjoint(a, b) hasfastin(b) && return !any(in(b), a) hasfastin(a) && return !any(in(a), b) haslength(b) && length(b) < FASTIN_SET_THRESHOLD && return !any(in(b), a) return !any(in(Set(b)), a) end if haslength(a) && haslength(b) && length(b) < length(a) return _isdisjoint(b, a) end _isdisjoint(a, b) end function isdisjoint(a::AbstractRange{T}, b::AbstractRange{T}) where T (isempty(a) || isempty(b)) && return true fa, la = extrema(a) fb, lb = extrema(b) if (la < fb) | (lb < fa) return true else return _overlapping_range_isdisjoint(a, b) end end _overlapping_range_isdisjoint(a::AbstractRange{T}, b::AbstractRange{T}) where T = invoke(isdisjoint, Tuple{Any,Any}, a, b) function _overlapping_range_isdisjoint(a::AbstractRange{T}, b::AbstractRange{T}) where T<:Integer if abs(step(a)) == abs(step(b)) return mod(minimum(a), step(a)) != mod(minimum(b), step(a)) else return invoke(isdisjoint, Tuple{Any,Any}, a, b) end end ## partial ordering of sets by containment ==(a::AbstractSet, b::AbstractSet) = length(a) == length(b) && a โІ b # convenience functions for AbstractSet # (if needed, only their synonyms โŠŠ and โІ must be specialized) <( a::AbstractSet, b::AbstractSet) = a โŠŠ b <=(a::AbstractSet, b::AbstractSet) = a โІ b ## filtering sets filter(pred, s::AbstractSet) = mapfilter(pred, push!, s, emptymutable(s)) # it must be safe to delete the current element while iterating over s: unsafe_filter!(pred, s::AbstractSet) = mapfilter(!pred, delete!, s, s) # TODO: delete mapfilter in favor of comprehensions/foldl/filter when competitive function mapfilter(pred, f, itr, res) for x in itr pred(x) && f(res, x) end res end J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/set.jl[# This file is a part of Julia. License is MIT: https://julialang.org/license """ Set{T} <: AbstractSet{T} `Set`s are mutable containers that provide fast membership testing. `Set`s have efficient implementations of set operations such as `in`, `union` and `intersect`. Elements in a `Set` are unique, as determined by the elements' definition of `isequal`. The order of elements in a `Set` is an implementation detail and cannot be relied on. See also: [`AbstractSet`](@ref), [`BitSet`](@ref), [`Dict`](@ref), [`push!`](@ref), [`empty!`](@ref), [`union!`](@ref), [`in`](@ref), [`isequal`](@ref) # Examples ```jldoctest; filter = r"^ '.'"ma julia> s = Set("aaBca") Set{Char} with 3 elements: 'a' 'c' 'B' julia> push!(s, 'b') Set{Char} with 4 elements: 'a' 'b' 'B' 'c' julia> s = Set([NaN, 0.0, 1.0, 2.0]); julia> -0.0 in s # isequal(0.0, -0.0) is false false julia> NaN in s # isequal(NaN, NaN) is true true ``` """ struct Set{T} <: AbstractSet{T} dict::Dict{T,Nothing} global _Set(dict::Dict{T,Nothing}) where {T} = new{T}(dict) end Set{T}() where {T} = _Set(Dict{T,Nothing}()) Set{T}(s::Set{T}) where {T} = _Set(Dict{T,Nothing}(s.dict)) Set{T}(itr) where {T} = union!(Set{T}(), itr) Set() = Set{Any}() function Set{T}(s::KeySet{T, <:Dict{T}}) where {T} d = s.dict slots = copy(d.slots) keys = copy(d.keys) vals = similar(d.vals, Nothing) _Set(Dict{T,Nothing}(slots, keys, vals, d.ndel, d.count, d.age, d.idxfloor, d.maxprobe)) end Set(itr) = _Set(itr, IteratorEltype(itr)) _Set(itr, ::HasEltype) = Set{eltype(itr)}(itr) function _Set(itr, ::EltypeUnknown) T = @default_eltype(itr) (isconcretetype(T) || T === Union{}) || return grow_to!(Set{T}(), itr) return Set{T}(itr) end empty(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() # return an empty set with eltype T, which is mutable (can be grown) # by default, a Set is returned emptymutable(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() _similar_for(c::AbstractSet, ::Type{T}, itr, isz, len) where {T} = empty(c, T) function show(io::IO, s::Set) if isempty(s) if get(io, :typeinfo, Any) == typeof(s) print(io, "Set()") else show(io, typeof(s)) print(io, "()") end else print(io, "Set(") show_vector(io, s) print(io, ')') end end isempty(s::Set) = isempty(s.dict) length(s::Set) = length(s.dict) in(x, s::Set) = haskey(s.dict, x) # This avoids hashing and probing twice and it works the same as # in!(x, s::Set) = in(x, s) ? true : (push!(s, x); false) function in!(x, s::Set) idx, sh = ht_keyindex2_shorthash!(s.dict, x) idx > 0 && return true _setindex!(s.dict, nothing, x, -idx, sh) return false end push!(s::Set, x) = (s.dict[x] = nothing; s) pop!(s::Set, x) = (pop!(s.dict, x); x) pop!(s::Set, x, default) = (x in s ? pop!(s, x) : default) function pop!(s::Set) isempty(s) && throw(ArgumentError("set must be non-empty")) return pop!(s.dict)[1] end delete!(s::Set, x) = (delete!(s.dict, x); s) copy(s::Set) = copymutable(s) copymutable(s::Set{T}) where {T} = Set{T}(s) # Set is the default mutable fall-back copymutable(s::AbstractSet{T}) where {T} = Set{T}(s) sizehint!(s::Set, newsz) = (sizehint!(s.dict, newsz); s) empty!(s::Set) = (empty!(s.dict); s) rehash!(s::Set) = (rehash!(s.dict); s) iterate(s::Set, i...) = iterate(KeySet(s.dict), i...) # In case the size(s) is smaller than size(t) its more efficient to iterate through # elements of s instead and only delete the ones also contained in t. # The threshold for this decision boils down to a tradeoff between # size(s) * cost(in() + delete!()) โ‰ถ size(t) * cost(delete!()) # Empirical observations on Ints point towards a threshold of 0.8. # To be on the safe side (e.g. cost(in) >>> cost(delete!) ) a # conservative threshold of 0.5 was chosen. function setdiff!(s::Set, t::Set) if 2 * length(s) < length(t) for x in s x in t && delete!(s, x) end else for x in t delete!(s, x) end end return s end """ unique(itr) Return an array containing only the unique elements of collection `itr`, as determined by [`isequal`](@ref), in the order that the first of each set of equivalent elements originally appears. The element type of the input is preserved. See also: [`unique!`](@ref), [`allunique`](@ref), [`allequal`](@ref). # Examples ```jldoctest julia> unique([1, 2, 6, 2]) 3-element Vector{Int64}: 1 2 6 julia> unique(Real[1, 1.0, 2]) 2-element Vector{Real}: 1 2 ``` """ function unique(itr) if isa(IteratorEltype(itr), HasEltype) T = eltype(itr) out = Vector{T}() seen = Set{T}() for x in itr !in!(x, seen) && push!(out, x) end return out end T = @default_eltype(itr) y = iterate(itr) y === nothing && return T[] x, i = y S = typeof(x) R = isconcretetype(T) ? T : S return _unique_from(itr, R[x], Set{R}((x,)), i) end _unique_from(itr, out, seen, i) = unique_from(itr, out, seen, i) @inline function unique_from(itr, out::Vector{T}, seen, i) where T while true y = iterate(itr, i) y === nothing && break x, i = y S = typeof(x) if !(S === T || S <: T) R = promote_typejoin(S, T) seenR = convert(Set{R}, seen) outR = convert(Vector{R}, out) !in!(x, seenR) && push!(outR, x) return _unique_from(itr, outR, seenR, i) end !in!(x, seen) && push!(out, x) end return out end unique(r::AbstractRange) = allunique(r) ? r : oftype(r, r[begin:begin]) """ unique(f, itr) Return an array containing one value from `itr` for each unique value produced by `f` applied to elements of `itr`. # Examples ```jldoctest julia> unique(x -> x^2, [1, -1, 3, -3, 4]) 3-element Vector{Int64}: 1 3 4 ``` This functionality can also be used to extract the *indices* of the first occurrences of unique elements in an array: ```jldoctest julia> a = [3.1, 4.2, 5.3, 3.1, 3.1, 3.1, 4.2, 1.7]; julia> i = unique(i -> a[i], eachindex(a)) 4-element Vector{Int64}: 1 2 3 8 julia> a[i] 4-element Vector{Float64}: 3.1 4.2 5.3 1.7 julia> a[i] == unique(a) true ``` """ function unique(f, C; seen::Union{Nothing,Set}=nothing) out = Vector{eltype(C)}() if seen !== nothing for x in C !in!(f(x), seen) && push!(out, x) end return out end s = iterate(C) if s === nothing return out end (x, i) = s y = f(x) seen = Set{typeof(y)}() push!(seen, y) push!(out, x) return _unique!(f, out, C, seen, i) end function _unique!(f, out::AbstractVector, C, seen::Set, i) s = iterate(C, i) while s !== nothing (x, i) = s y = f(x) if y โˆ‰ seen push!(out, x) if y isa eltype(seen) push!(seen, y) else seen2 = convert(Set{promote_typejoin(eltype(seen), typeof(y))}, seen) push!(seen2, y) return _unique!(f, out, C, seen2, i) end end s = iterate(C, i) end return out end """ unique!(f, A::AbstractVector) Selects one value from `A` for each unique value produced by `f` applied to elements of `A`, then return the modified A. !!! compat "Julia 1.1" This method is available as of Julia 1.1. # Examples ```jldoctest julia> unique!(x -> x^2, [1, -1, 3, -3, 4]) 3-element Vector{Int64}: 1 3 4 julia> unique!(n -> n%3, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6]) 3-element Vector{Int64}: 5 1 9 julia> unique!(iseven, [2, 3, 5, 7, 9]) 2-element Vector{Int64}: 2 3 ``` """ function unique!(f, A::AbstractVector; seen::Union{Nothing,Set}=nothing) if length(A) <= 1 return A end i = firstindex(A)::Int x = @inbounds A[i] y = f(x) if seen === nothing seen = Set{typeof(y)}() end push!(seen, y) return _unique!(f, A, seen, i, i+1) end function _unique!(f, A::AbstractVector, seen::Set, current::Integer, i::Integer) while i <= lastindex(A) x = @inbounds A[i] y = f(x) if y โˆ‰ seen current += 1 @inbounds A[current] = x if y isa eltype(seen) push!(seen, y) else seen2 = convert(Set{promote_typejoin(eltype(seen), typeof(y))}, seen) push!(seen2, y) return _unique!(f, A, seen2, current, i+1) end end i += 1 end return resize!(A, current - firstindex(A)::Int + 1)::typeof(A) end # If A is not grouped, then we will need to keep track of all of the elements that we have # seen so far. _unique!(A::AbstractVector) = unique!(identity, A::AbstractVector) # If A is grouped, so that each unique element is in a contiguous group, then we only # need to keep track of one element at a time. We replace the elements of A with the # unique elements that we see in the order that we see them. Once we have iterated # through A, we resize A based on the number of unique elements that we see. function _groupedunique!(A::AbstractVector) isempty(A) && return A idxs = eachindex(A) y = first(A) # We always keep the first element T = NTuple{2,Any} # just to eliminate `iterate(idxs)::Nothing` candidate it = iterate(idxs, (iterate(idxs)::T)[2]) count = 1 for x in Iterators.drop(A, 1) if !isequal(x, y) it = it::T y = A[it[1]] = x count += 1 it = iterate(idxs, it[2]) end end resize!(A, count)::typeof(A) end """ unique!(A::AbstractVector) Remove duplicate items as determined by [`isequal`](@ref), then return the modified `A`. `unique!` will return the elements of `A` in the order that they occur. If you do not care about the order of the returned data, then calling `(sort!(A); unique!(A))` will be much more efficient as long as the elements of `A` can be sorted. # Examples ```jldoctest julia> unique!([1, 1, 1]) 1-element Vector{Int64}: 1 julia> A = [7, 3, 2, 3, 7, 5]; julia> unique!(A) 4-element Vector{Int64}: 7 3 2 5 julia> B = [7, 6, 42, 6, 7, 42]; julia> sort!(B); # unique! is able to process sorted data much more efficiently. julia> unique!(B) 3-element Vector{Int64}: 6 7 42 ``` """ function unique!(itr) if isa(itr, AbstractVector) if OrderStyle(eltype(itr)) === Ordered() (issorted(itr) || issorted(itr, rev=true)) && return _groupedunique!(itr) end end isempty(itr) && return itr return _unique!(itr) end """ allunique(itr) -> Bool Return `true` if all values from `itr` are distinct when compared with [`isequal`](@ref). See also: [`unique`](@ref), [`issorted`](@ref), [`allequal`](@ref). # Examples ```jldoctest julia> allunique([1, 2, 3]) true julia> allunique([1, 2, 1, 2]) false julia> allunique(Real[1, 1.0, 2]) false julia> allunique([NaN, 2.0, NaN, 4.0]) false ``` """ function allunique(C) if haslength(C) length(C) < 2 && return true length(C) < 32 && return _indexed_allunique(collect(C)) end return _hashed_allunique(C) end function _hashed_allunique(C) seen = Set{eltype(C)}() x = iterate(C) if haslength(C) && length(C) > 1000 for i in OneTo(1000) v, s = something(x) in!(v, seen) && return false x = iterate(C, s) end sizehint!(seen, length(C)) end while x !== nothing v, s = x in!(v, seen) && return false x = iterate(C, s) end return true end allunique(::Union{AbstractSet,AbstractDict}) = true allunique(r::AbstractRange) = !iszero(step(r)) || length(r) <= 1 allunique(A::StridedArray) = length(A) < 32 ? _indexed_allunique(A) : _hashed_allunique(A) function _indexed_allunique(A) length(A) < 2 && return true iter = eachindex(A) I = iterate(iter) while I !== nothing i, s = I a = A[i] for j in Iterators.rest(iter, s) isequal(a, @inbounds A[j]) && return false end I = iterate(iter, s) end return true end function allunique(t::Tuple) length(t) < 32 || return _hashed_allunique(t) a = afoldl(true, tail(t)...) do b, x b & !isequal(first(t), x) end return a && allunique(tail(t)) end allunique(t::Tuple{}) = true """ allequal(itr) -> Bool Return `true` if all values from `itr` are equal when compared with [`isequal`](@ref). See also: [`unique`](@ref), [`allunique`](@ref). !!! compat "Julia 1.8" The `allequal` function requires at least Julia 1.8. # Examples ```jldoctest julia> allequal([]) true julia> allequal([1]) true julia> allequal([1, 1]) true julia> allequal([1, 2]) false julia> allequal(Dict(:a => 1, :b => 1)) false ``` """ allequal(itr) = isempty(itr) ? true : all(isequal(first(itr)), itr) allequal(c::Union{AbstractSet,AbstractDict}) = length(c) <= 1 allequal(r::AbstractRange) = iszero(step(r)) || length(r) <= 1 filter!(f, s::Set) = unsafe_filter!(f, s) const hashs_seed = UInt === UInt64 ? 0x852ada37cfe8e0ce : 0xcfe8e0ce function hash(s::AbstractSet, h::UInt) hv = hashs_seed for x in s hv โŠป= hash(x) end hash(hv, h) end convert(::Type{T}, s::T) where {T<:AbstractSet} = s convert(::Type{T}, s::AbstractSet) where {T<:AbstractSet} = T(s)::T ## replace/replace! ## function check_count(count::Integer) count < 0 && throw(DomainError(count, "`count` must not be negative (got $count)")) return min(count, typemax(Int)) % Int end # TODO: use copy!, which is currently unavailable from here since it is defined in Future _copy_oftype(x, ::Type{T}) where {T} = copyto!(similar(x, T), x) # TODO: use similar() once deprecation is removed and it preserves keys _copy_oftype(x::AbstractDict, ::Type{Pair{K,V}}) where {K,V} = merge!(empty(x, K, V), x) _copy_oftype(x::AbstractSet, ::Type{T}) where {T} = union!(empty(x, T), x) _copy_oftype(x::AbstractArray{T}, ::Type{T}) where {T} = copy(x) _copy_oftype(x::AbstractDict{K,V}, ::Type{Pair{K,V}}) where {K,V} = copy(x) _copy_oftype(x::AbstractSet{T}, ::Type{T}) where {T} = copy(x) _similar_or_copy(x::Any) = similar(x) _similar_or_copy(x::Any, ::Type{T}) where {T} = similar(x, T) # Make a copy on construction since it is faster than inserting elements separately _similar_or_copy(x::Union{AbstractDict,AbstractSet}) = copy(x) _similar_or_copy(x::Union{AbstractDict,AbstractSet}, ::Type{T}) where {T} = _copy_oftype(x, T) # to make replace/replace! work for a new container type Cont, only # _replace!(new::Callable, res::Cont, A::Cont, count::Int) # has to be implemented """ replace!(A, old_new::Pair...; [count::Integer]) For each pair `old=>new` in `old_new`, replace all occurrences of `old` in collection `A` by `new`. Equality is determined using [`isequal`](@ref). If `count` is specified, then replace at most `count` occurrences in total. See also [`replace`](@ref replace(A, old_new::Pair...)). # Examples ```jldoctest julia> replace!([1, 2, 1, 3], 1=>0, 2=>4, count=2) 4-element Vector{Int64}: 0 4 1 3 julia> replace!(Set([1, 2, 3]), 1=>0) Set{Int64} with 3 elements: 0 2 3 ``` """ replace!(A, old_new::Pair...; count::Integer=typemax(Int)) = replace_pairs!(A, A, check_count(count), old_new) function replace_pairs!(res, A, count::Int, old_new::Tuple{Vararg{Pair}}) @inline function new(x) for o_n in old_new isequal(first(o_n), x) && return last(o_n) end return x # no replace end _replace!(new, res, A, count) end """ replace!(new::Union{Function, Type}, A; [count::Integer]) Replace each element `x` in collection `A` by `new(x)`. If `count` is specified, then replace at most `count` values in total (replacements being defined as `new(x) !== x`). # Examples ```jldoctest julia> replace!(x -> isodd(x) ? 2x : x, [1, 2, 3, 4]) 4-element Vector{Int64}: 2 2 6 4 julia> replace!(Dict(1=>2, 3=>4)) do kv first(kv) < 3 ? first(kv)=>3 : kv end Dict{Int64, Int64} with 2 entries: 3 => 4 1 => 3 julia> replace!(x->2x, Set([3, 6])) Set{Int64} with 2 elements: 6 12 ``` """ replace!(new::Callable, A; count::Integer=typemax(Int)) = _replace!(new, A, A, check_count(count)) """ replace(A, old_new::Pair...; [count::Integer]) Return a copy of collection `A` where, for each pair `old=>new` in `old_new`, all occurrences of `old` are replaced by `new`. Equality is determined using [`isequal`](@ref). If `count` is specified, then replace at most `count` occurrences in total. The element type of the result is chosen using promotion (see [`promote_type`](@ref)) based on the element type of `A` and on the types of the `new` values in pairs. If `count` is omitted and the element type of `A` is a `Union`, the element type of the result will not include singleton types which are replaced with values of a different type: for example, `Union{T,Missing}` will become `T` if `missing` is replaced. See also [`replace!`](@ref), [`splice!`](@ref), [`delete!`](@ref), [`insert!`](@ref). !!! compat "Julia 1.7" Version 1.7 is required to replace elements of a `Tuple`. # Examples ```jldoctest julia> replace([1, 2, 1, 3], 1=>0, 2=>4, count=2) 4-element Vector{Int64}: 0 4 1 3 julia> replace([1, missing], missing=>0) 2-element Vector{Int64}: 1 0 ``` """ function replace(A, old_new::Pair...; count::Union{Integer,Nothing}=nothing) V = promote_valuetype(old_new...) if count isa Nothing T = promote_type(subtract_singletontype(eltype(A), old_new...), V) replace_pairs!(_similar_or_copy(A, T), A, typemax(Int), old_new) else U = promote_type(eltype(A), V) replace_pairs!(_similar_or_copy(A, U), A, check_count(count), old_new) end end promote_valuetype(x::Pair{K, V}) where {K, V} = V promote_valuetype(x::Pair{K, V}, y::Pair...) where {K, V} = promote_type(V, promote_valuetype(y...)) # Subtract singleton types which are going to be replaced function subtract_singletontype(::Type{T}, x::Pair{K}) where {T, K} if issingletontype(K) typesplit(T, K) else T end end subtract_singletontype(::Type{T}, x::Pair{K}, y::Pair...) where {T, K} = subtract_singletontype(subtract_singletontype(T, y...), x) """ replace(new::Union{Function, Type}, A; [count::Integer]) Return a copy of `A` where each value `x` in `A` is replaced by `new(x)`. If `count` is specified, then replace at most `count` values in total (replacements being defined as `new(x) !== x`). !!! compat "Julia 1.7" Version 1.7 is required to replace elements of a `Tuple`. # Examples ```jldoctest julia> replace(x -> isodd(x) ? 2x : x, [1, 2, 3, 4]) 4-element Vector{Int64}: 2 2 6 4 julia> replace(Dict(1=>2, 3=>4)) do kv first(kv) < 3 ? first(kv)=>3 : kv end Dict{Int64, Int64} with 2 entries: 3 => 4 1 => 3 ``` """ replace(new::Callable, A; count::Integer=typemax(Int)) = _replace!(new, _similar_or_copy(A), A, check_count(count)) # Handle ambiguities replace!(a::Callable, b::Pair; count::Integer=-1) = throw(MethodError(replace!, (a, b))) replace!(a::Callable, b::Pair, c::Pair; count::Integer=-1) = throw(MethodError(replace!, (a, b, c))) replace(a::Callable, b::Pair; count::Integer=-1) = throw(MethodError(replace, (a, b))) replace(a::Callable, b::Pair, c::Pair; count::Integer=-1) = throw(MethodError(replace, (a, b, c))) ### replace! for AbstractDict/AbstractSet askey(k, ::AbstractDict) = k.first askey(k, ::AbstractSet) = k function _replace!(new::Callable, res::Union{AbstractDict,AbstractSet}, A::Union{AbstractDict,AbstractSet}, count::Int) @assert res isa AbstractDict && A isa AbstractDict || res isa AbstractSet && A isa AbstractSet count == 0 && return res c = 0 if res === A # cannot replace elements while iterating over A repl = Pair{eltype(A),eltype(A)}[] for x in A y = new(x) if x !== y push!(repl, x => y) c += 1 c == count && break end end for oldnew in repl pop!(res, askey(first(oldnew), res)) end for oldnew in repl push!(res, last(oldnew)) end else for x in A y = new(x) if x !== y pop!(res, askey(x, res)) push!(res, y) c += 1 c == count && break end end end res end ### replace! for AbstractArray function _replace!(new::Callable, res::AbstractArray, A::AbstractArray, count::Int) c = 0 if count >= length(A) # simpler loop allows for SIMD if res === A # for optimization only for i in eachindex(A) @inbounds Ai = A[i] y = new(Ai) @inbounds A[i] = y end else for i in eachindex(A) @inbounds Ai = A[i] y = new(Ai) @inbounds res[i] = y end end else for i in eachindex(A) @inbounds Ai = A[i] if c < count y = new(Ai) @inbounds res[i] = y c += (Ai !== y) else @inbounds res[i] = Ai end end end res end ### specialization for Dict / Set function _replace!(new::Callable, t::Dict{K,V}, A::AbstractDict, count::Int) where {K,V} # we ignore A, which is supposed to be equal to the destination t, # as it can generally be faster to just replace inline count == 0 && return t c = 0 news = Pair{K,V}[] i = skip_deleted_floor!(t) @inbounds while i != 0 k1, v1 = t.keys[i], t.vals[i] x1 = Pair{K,V}(k1, v1) x2 = new(x1) if x1 !== x2 k2, v2 = first(x2), last(x2) if isequal(k1, k2) t.keys[i] = k2 t.vals[i] = v2 t.age += 1 else _delete!(t, i) push!(news, x2) end c += 1 c == count && break end i = i == typemax(Int) ? 0 : skip_deleted(t, i+1) end for n in news push!(t, n) end t end function _replace!(new::Callable, t::Set{T}, ::AbstractSet, count::Int) where {T} _replace!(t.dict, t.dict, count) do kv k = first(kv) k2 = new(k) k2 === k ? kv : k2 => nothing end t end ### replace for tuples function _replace(f::Callable, t::Tuple, count::Int) if count == 0 || isempty(t) t else x = f(t[1]) (x, _replace(f, tail(t), count - !==(x, t[1]))...) end end replace(f::Callable, t::Tuple; count::Integer=typemax(Int)) = _replace(f, t, check_count(count)) function _replace(t::Tuple, count::Int, old_new::Tuple{Vararg{Pair}}) _replace(t, count) do x @inline for o_n in old_new isequal(first(o_n), x) && return last(o_n) end return x end end replace(t::Tuple, old_new::Pair...; count::Integer=typemax(Int)) = _replace(t, check_count(count), old_new) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/char.jlท0# This file is a part of Julia. License is MIT: https://julialang.org/license """ The `AbstractChar` type is the supertype of all character implementations in Julia. A character represents a Unicode code point, and can be converted to an integer via the [`codepoint`](@ref) function in order to obtain the numerical value of the code point, or constructed from the same integer. These numerical values determine how characters are compared with `<` and `==`, for example. New `T <: AbstractChar` types should define a `codepoint(::T)` method and a `T(::UInt32)` constructor, at minimum. A given `AbstractChar` subtype may be capable of representing only a subset of Unicode, in which case conversion from an unsupported `UInt32` value may throw an error. Conversely, the built-in [`Char`](@ref) type represents a *superset* of Unicode (in order to losslessly encode invalid byte streams), in which case conversion of a non-Unicode value *to* `UInt32` throws an error. The [`isvalid`](@ref) function can be used to check which codepoints are representable in a given `AbstractChar` type. Internally, an `AbstractChar` type may use a variety of encodings. Conversion via `codepoint(char)` will not reveal this encoding because it always returns the Unicode value of the character. `print(io, c)` of any `c::AbstractChar` produces an encoding determined by `io` (UTF-8 for all built-in `IO` types), via conversion to `Char` if necessary. `write(io, c)`, in contrast, may emit an encoding depending on `typeof(c)`, and `read(io, typeof(c))` should read the same encoding as `write`. New `AbstractChar` types must provide their own implementations of `write` and `read`. """ AbstractChar """ Char(c::Union{Number,AbstractChar}) `Char` is a 32-bit [`AbstractChar`](@ref) type that is the default representation of characters in Julia. `Char` is the type used for character literals like `'x'` and it is also the element type of [`String`](@ref). In order to losslessly represent arbitrary byte streams stored in a `String`, a `Char` value may store information that cannot be converted to a Unicode codepoint โ€” converting such a `Char` to `UInt32` will throw an error. The [`isvalid(c::Char)`](@ref) function can be used to query whether `c` represents a valid Unicode character. """ Char @constprop :aggressive (::Type{T})(x::Number) where {T<:AbstractChar} = T(UInt32(x)) @constprop :aggressive AbstractChar(x::Number) = Char(x) @constprop :aggressive (::Type{T})(x::AbstractChar) where {T<:Union{Number,AbstractChar}} = T(codepoint(x)) @constprop :aggressive (::Type{T})(x::AbstractChar) where {T<:Union{Int32,Int64}} = codepoint(x) % T (::Type{T})(x::T) where {T<:AbstractChar} = x """ ncodeunits(c::Char) -> Int Return the number of code units required to encode a character as UTF-8. This is the number of bytes which will be printed if the character is written to an output stream, or `ncodeunits(string(c))` but computed efficiently. !!! compat "Julia 1.1" This method requires at least Julia 1.1. In Julia 1.0 consider using `ncodeunits(string(c))`. """ ncodeunits(c::Char) = write(devnull, c) # this is surprisingly efficient """ codepoint(c::AbstractChar) -> Integer Return the Unicode codepoint (an unsigned integer) corresponding to the character `c` (or throw an exception if `c` does not represent a valid character). For `Char`, this is a `UInt32` value, but `AbstractChar` types that represent only a subset of Unicode may return a different-sized integer (e.g. `UInt8`). """ function codepoint end @constprop :aggressive codepoint(c::Char) = UInt32(c) struct InvalidCharError{T<:AbstractChar} <: Exception char::T end struct CodePointError{T<:Integer} <: Exception code::T end @noinline throw_invalid_char(c::AbstractChar) = throw(InvalidCharError(c)) @noinline throw_code_point_err(u::Integer) = throw(CodePointError(u)) function ismalformed(c::Char) u = bitcast(UInt32, c) l1 = leading_ones(u) << 3 t0 = trailing_zeros(u) & 56 (l1 == 8) | (l1 + t0 > 32) | (((u & 0x00c0c0c0) โŠป 0x00808080) >> t0 != 0) end @inline is_overlong_enc(u::UInt32) = (u >> 24 == 0xc0) | (u >> 24 == 0xc1) | (u >> 21 == 0x0704) | (u >> 20 == 0x0f08) function isoverlong(c::Char) u = bitcast(UInt32, c) is_overlong_enc(u) end # fallback: other AbstractChar types, by default, are assumed # not to support malformed or overlong encodings. """ ismalformed(c::AbstractChar) -> Bool Return `true` if `c` represents malformed (non-Unicode) data according to the encoding used by `c`. Defaults to `false` for non-`Char` types. See also [`show_invalid`](@ref). """ ismalformed(c::AbstractChar) = false """ isoverlong(c::AbstractChar) -> Bool Return `true` if `c` represents an overlong UTF-8 sequence. Defaults to `false` for non-`Char` types. See also [`decode_overlong`](@ref) and [`show_invalid`](@ref). """ isoverlong(c::AbstractChar) = false @constprop :aggressive function UInt32(c::Char) # TODO: use optimized inline LLVM u = bitcast(UInt32, c) u < 0x80000000 && return u >> 24 l1 = leading_ones(u) t0 = trailing_zeros(u) & 56 (l1 == 1) | (8l1 + t0 > 32) | ((((u & 0x00c0c0c0) โŠป 0x00808080) >> t0 != 0) | is_overlong_enc(u)) && throw_invalid_char(c) u &= 0xffffffff >> l1 u >>= t0 ((u & 0x0000007f) >> 0) | ((u & 0x00007f00) >> 2) | ((u & 0x007f0000) >> 4) | ((u & 0x7f000000) >> 6) end """ decode_overlong(c::AbstractChar) -> Integer When [`isoverlong(c)`](@ref) is `true`, `decode_overlong(c)` returns the Unicode codepoint value of `c`. `AbstractChar` implementations that support overlong encodings should implement `Base.decode_overlong`. """ function decode_overlong end @constprop :aggressive function decode_overlong(c::Char) u = bitcast(UInt32, c) l1 = leading_ones(u) t0 = trailing_zeros(u) & 56 u &= 0xffffffff >> l1 u >>= t0 ((u & 0x0000007f) >> 0) | ((u & 0x00007f00) >> 2) | ((u & 0x007f0000) >> 4) | ((u & 0x7f000000) >> 6) end @constprop :aggressive function Char(u::UInt32) u < 0x80 && return bitcast(Char, u << 24) u < 0x00200000 || throw_code_point_err(u) c = ((u << 0) & 0x0000003f) | ((u << 2) & 0x00003f00) | ((u << 4) & 0x003f0000) | ((u << 6) & 0x3f000000) c = u < 0x00000800 ? (c << 16) | 0xc0800000 : u < 0x00010000 ? (c << 08) | 0xe0808000 : (c << 00) | 0xf0808080 bitcast(Char, c) end @constprop :aggressive @noinline UInt32_cold(c::Char) = UInt32(c) @constprop :aggressive function (T::Union{Type{Int8},Type{UInt8}})(c::Char) i = bitcast(Int32, c) i โ‰ฅ 0 ? ((i >>> 24) % T) : T(UInt32_cold(c)) end @constprop :aggressive @noinline Char_cold(b::UInt32) = Char(b) @constprop :aggressive function Char(b::Union{Int8,UInt8}) 0 โ‰ค b โ‰ค 0x7f ? bitcast(Char, (b % UInt32) << 24) : Char_cold(UInt32(b)) end convert(::Type{AbstractChar}, x::Number) = Char(x) # default to Char convert(::Type{T}, x::Number) where {T<:AbstractChar} = T(x)::T convert(::Type{T}, x::AbstractChar) where {T<:Number} = T(x)::T convert(::Type{T}, c::AbstractChar) where {T<:AbstractChar} = T(c)::T convert(::Type{T}, c::T) where {T<:AbstractChar} = c rem(x::AbstractChar, ::Type{T}) where {T<:Number} = rem(codepoint(x), T) typemax(::Type{Char}) = bitcast(Char, typemax(UInt32)) typemin(::Type{Char}) = bitcast(Char, typemin(UInt32)) size(c::AbstractChar) = () size(c::AbstractChar, d::Integer) = d < 1 ? throw(BoundsError()) : 1 ndims(c::AbstractChar) = 0 ndims(::Type{<:AbstractChar}) = 0 length(c::AbstractChar) = 1 IteratorSize(::Type{Char}) = HasShape{0}() firstindex(c::AbstractChar) = 1 lastindex(c::AbstractChar) = 1 getindex(c::AbstractChar) = c getindex(c::AbstractChar, i::Integer) = i == 1 ? c : throw(BoundsError()) getindex(c::AbstractChar, I::Integer...) = all(x -> x == 1, I) ? c : throw(BoundsError()) first(c::AbstractChar) = c last(c::AbstractChar) = c eltype(::Type{T}) where {T<:AbstractChar} = T iterate(c::AbstractChar, done=false) = done ? nothing : (c, true) isempty(c::AbstractChar) = false in(x::AbstractChar, y::AbstractChar) = x == y ==(x::Char, y::Char) = bitcast(UInt32, x) == bitcast(UInt32, y) isless(x::Char, y::Char) = bitcast(UInt32, x) < bitcast(UInt32, y) hash(x::Char, h::UInt) = hash_uint64(((bitcast(UInt32, x) + UInt64(0xd4d64234)) << 32) โŠป UInt64(h)) first_utf8_byte(c::Char) = (bitcast(UInt32, c) >> 24) % UInt8 # fallbacks: isless(x::AbstractChar, y::AbstractChar) = isless(Char(x), Char(y)) ==(x::AbstractChar, y::AbstractChar) = Char(x) == Char(y) hash(x::AbstractChar, h::UInt) = hash(Char(x), h) widen(::Type{T}) where {T<:AbstractChar} = T @inline -(x::AbstractChar, y::AbstractChar) = Int(x) - Int(y) @inline function -(x::T, y::Integer) where {T<:AbstractChar} if x isa Char u = Int32((bitcast(UInt32, x) >> 24) % Int8) if u >= 0 # inline the runtime fast path z = u - y return 0 <= z < 0x80 ? bitcast(Char, (z % UInt32) << 24) : Char(UInt32(z)) end end return T(Int32(x) - Int32(y)) end @inline function +(x::T, y::Integer) where {T<:AbstractChar} if x isa Char u = Int32((bitcast(UInt32, x) >> 24) % Int8) if u >= 0 # inline the runtime fast path z = u + y return 0 <= z < 0x80 ? bitcast(Char, (z % UInt32) << 24) : Char(UInt32(z)) end end return T(Int32(x) + Int32(y)) end @inline +(x::Integer, y::AbstractChar) = y + x # `print` should output UTF-8 by default for all AbstractChar types. # (Packages may implement other IO subtypes to specify different encodings.) # In contrast, `write(io, c)` outputs a `c` in an encoding determined by typeof(c). print(io::IO, c::Char) = (write(io, c); nothing) print(io::IO, c::AbstractChar) = print(io, Char(c)) # fallback: convert to output UTF-8 const hex_chars = UInt8['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] function show_invalid(io::IO, c::Char) write(io, 0x27) u = bitcast(UInt32, c) while true a = hex_chars[((u >> 28) & 0xf) + 1] b = hex_chars[((u >> 24) & 0xf) + 1] write(io, 0x5c, UInt8('x'), a, b) (u <<= 8) == 0 && break end write(io, 0x27) end """ show_invalid(io::IO, c::AbstractChar) Called by `show(io, c)` when [`isoverlong(c)`](@ref) or [`ismalformed(c)`](@ref) return `true`. Subclasses of `AbstractChar` should define `Base.show_invalid` methods if they support storing invalid character data. """ show_invalid # show c to io, assuming UTF-8 encoded output function show(io::IO, c::AbstractChar) if c <= '\\' b = c == '\0' ? 0x30 : c == '\a' ? 0x61 : c == '\b' ? 0x62 : c == '\t' ? 0x74 : c == '\n' ? 0x6e : c == '\v' ? 0x76 : c == '\f' ? 0x66 : c == '\r' ? 0x72 : c == '\e' ? 0x65 : c == '\'' ? 0x27 : c == '\\' ? 0x5c : 0xff if b != 0xff write(io, 0x27, 0x5c, b, 0x27) return end end if isoverlong(c) || ismalformed(c) show_invalid(io, c) elseif isprint(c) write(io, 0x27) print(io, c) # use print, not write, to use UTF-8 for any AbstractChar write(io, 0x27) else # unprintable, well-formed, non-overlong Unicode u = codepoint(c) write(io, 0x27, 0x5c, u <= 0x7f ? 0x78 : u <= 0xffff ? 0x75 : 0x55) d = max(2, 8 - (leading_zeros(u) >> 2)) while 0 < d write(io, hex_chars[((u >> ((d -= 1) << 2)) & 0xf) + 1]) end write(io, 0x27) end return end function show(io::IO, ::MIME"text/plain", c::T) where {T<:AbstractChar} show(io, c) get(io, :compact, false)::Bool && return if !ismalformed(c) print(io, ": ") if isoverlong(c) print(io, "[overlong] ") u = decode_overlong(c) c = T(u) else u = codepoint(c) end h = uppercase(string(u, base = 16, pad = 4)) print(io, (isascii(c) ? "ASCII/" : ""), "Unicode U+", h) else print(io, ": Malformed UTF-8") end abr = Unicode.category_abbrev(c) str = Unicode.category_string(c) print(io, " (category ", abr, ": ", str, ")") end T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/basic.jl'b# This file is a part of Julia. License is MIT: https://julialang.org/license """ The `AbstractString` type is the supertype of all string implementations in Julia. Strings are encodings of sequences of [Unicode](https://unicode.org/) code points as represented by the `AbstractChar` type. Julia makes a few assumptions about strings: * Strings are encoded in terms of fixed-size "code units" * Code units can be extracted with `codeunit(s, i)` * The first code unit has index `1` * The last code unit has index `ncodeunits(s)` * Any index `i` such that `1 โ‰ค i โ‰ค ncodeunits(s)` is in bounds * String indexing is done in terms of these code units: * Characters are extracted by `s[i]` with a valid string index `i` * Each `AbstractChar` in a string is encoded by one or more code units * Only the index of the first code unit of an `AbstractChar` is a valid index * The encoding of an `AbstractChar` is independent of what precedes or follows it * String encodings are [self-synchronizing] โ€“ i.e. `isvalid(s, i)` is O(1) [self-synchronizing]: https://en.wikipedia.org/wiki/Self-synchronizing_code Some string functions that extract code units, characters or substrings from strings error if you pass them out-of-bounds or invalid string indices. This includes `codeunit(s, i)` and `s[i]`. Functions that do string index arithmetic take a more relaxed approach to indexing and give you the closest valid string index when in-bounds, or when out-of-bounds, behave as if there were an infinite number of characters padding each side of the string. Usually these imaginary padding characters have code unit length `1` but string types may choose different "imaginary" character sizes as makes sense for their implementations (e.g. substrings may pass index arithmetic through to the underlying string they provide a view into). Relaxed indexing functions include those intended for index arithmetic: `thisind`, `nextind` and `prevind`. This model allows index arithmetic to work with out-of- bounds indices as intermediate values so long as one never uses them to retrieve a character, which often helps avoid needing to code around edge cases. See also [`codeunit`](@ref), [`ncodeunits`](@ref), [`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref). """ AbstractString ## required string functions ## """ ncodeunits(s::AbstractString) -> Int Return the number of code units in a string. Indices that are in bounds to access this string must satisfy `1 โ‰ค i โ‰ค ncodeunits(s)`. Not all such indices are valid โ€“ they may not be the start of a character, but they will return a code unit value when calling `codeunit(s,i)`. # Examples ```jldoctest julia> ncodeunits("The Julia Language") 18 julia> ncodeunits("โˆซeหฃ") 6 julia> ncodeunits('โˆซ'), ncodeunits('e'), ncodeunits('หฃ') (3, 1, 2) ``` See also [`codeunit`](@ref), [`checkbounds`](@ref), [`sizeof`](@ref), [`length`](@ref), [`lastindex`](@ref). """ ncodeunits(s::AbstractString) """ codeunit(s::AbstractString) -> Type{<:Union{UInt8, UInt16, UInt32}} Return the code unit type of the given string object. For ASCII, Latin-1, or UTF-8 encoded strings, this would be `UInt8`; for UCS-2 and UTF-16 it would be `UInt16`; for UTF-32 it would be `UInt32`. The code unit type need not be limited to these three types, but it's hard to think of widely used string encodings that don't use one of these units. `codeunit(s)` is the same as `typeof(codeunit(s,1))` when `s` is a non-empty string. See also [`ncodeunits`](@ref). """ codeunit(s::AbstractString) const CodeunitType = Union{Type{UInt8},Type{UInt16},Type{UInt32}} """ codeunit(s::AbstractString, i::Integer) -> Union{UInt8, UInt16, UInt32} Return the code unit value in the string `s` at index `i`. Note that codeunit(s, i) :: codeunit(s) I.e. the value returned by `codeunit(s, i)` is of the type returned by `codeunit(s)`. # Examples ```jldoctest julia> a = codeunit("Hello", 2) 0x65 julia> typeof(a) UInt8 ``` See also [`ncodeunits`](@ref), [`checkbounds`](@ref). """ @propagate_inbounds codeunit(s::AbstractString, i::Integer) = i isa Int ? throw(MethodError(codeunit, (s, i))) : codeunit(s, Int(i)) """ isvalid(s::AbstractString, i::Integer) -> Bool Predicate indicating whether the given index is the start of the encoding of a character in `s` or not. If `isvalid(s, i)` is true then `s[i]` will return the character whose encoding starts at that index, if it's false, then `s[i]` will raise an invalid index error or a bounds error depending on if `i` is in bounds. In order for `isvalid(s, i)` to be an O(1) function, the encoding of `s` must be [self-synchronizing](https://en.wikipedia.org/wiki/Self-synchronizing_code). This is a basic assumption of Julia's generic string support. See also [`getindex`](@ref), [`iterate`](@ref), [`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref), [`length`](@ref). # Examples ```jldoctest julia> str = "ฮฑฮฒฮณdef"; julia> isvalid(str, 1) true julia> str[1] 'ฮฑ': Unicode U+03B1 (category Ll: Letter, lowercase) julia> isvalid(str, 2) false julia> str[2] ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'ฮฑ', [3]=>'ฮฒ' Stacktrace: [...] ``` """ @propagate_inbounds isvalid(s::AbstractString, i::Integer) = i isa Int ? throw(MethodError(isvalid, (s, i))) : isvalid(s, Int(i)) """ iterate(s::AbstractString, i::Integer) -> Union{Tuple{<:AbstractChar, Int}, Nothing} Return a tuple of the character in `s` at index `i` with the index of the start of the following character in `s`. This is the key method that allows strings to be iterated, yielding a sequences of characters. If `i` is out of bounds in `s` then a bounds error is raised. The `iterate` function, as part of the iteration protocol may assume that `i` is the start of a character in `s`. See also [`getindex`](@ref), [`checkbounds`](@ref). """ @propagate_inbounds iterate(s::AbstractString, i::Integer) = i isa Int ? throw(MethodError(iterate, (s, i))) : iterate(s, Int(i)) ## basic generic definitions ## eltype(::Type{<:AbstractString}) = Char # some string types may use another AbstractChar """ sizeof(str::AbstractString) Size, in bytes, of the string `str`. Equal to the number of code units in `str` multiplied by the size, in bytes, of one code unit in `str`. # Examples ```jldoctest julia> sizeof("") 0 julia> sizeof("โˆ€") 3 ``` """ sizeof(s::AbstractString) = ncodeunits(s)::Int * sizeof(codeunit(s)::CodeunitType) firstindex(s::AbstractString) = 1 lastindex(s::AbstractString) = thisind(s, ncodeunits(s)::Int) isempty(s::AbstractString) = iszero(ncodeunits(s)::Int) function getindex(s::AbstractString, i::Integer) @boundscheck checkbounds(s, i) @inbounds return isvalid(s, i) ? (iterate(s, i)::NTuple{2,Any})[1] : string_index_err(s, i) end getindex(s::AbstractString, i::Colon) = s # TODO: handle other ranges with stride ยฑ1 specially? # TODO: add more @propagate_inbounds annotations? getindex(s::AbstractString, v::AbstractVector{<:Integer}) = sprint(io->(for i in v; write(io, s[i]) end), sizehint=length(v)) getindex(s::AbstractString, v::AbstractVector{Bool}) = throw(ArgumentError("logical indexing not supported for strings")) function get(s::AbstractString, i::Integer, default) # TODO: use ternary once @inbounds is expression-like if checkbounds(Bool, s, i) @inbounds return s[i] else return default end end ## bounds checking ## checkbounds(::Type{Bool}, s::AbstractString, i::Integer) = 1 โ‰ค i โ‰ค ncodeunits(s)::Int checkbounds(::Type{Bool}, s::AbstractString, r::AbstractRange{<:Integer}) = isempty(r) || (1 โ‰ค minimum(r) && maximum(r) โ‰ค ncodeunits(s)::Int) checkbounds(::Type{Bool}, s::AbstractString, I::AbstractArray{<:Real}) = all(i -> checkbounds(Bool, s, i), I) checkbounds(::Type{Bool}, s::AbstractString, I::AbstractArray{<:Integer}) = all(i -> checkbounds(Bool, s, i), I) checkbounds(s::AbstractString, I::Union{Integer,AbstractArray}) = checkbounds(Bool, s, I) ? nothing : throw(BoundsError(s, I)) ## construction, conversion, promotion ## string() = "" string(s::AbstractString) = s Vector{UInt8}(s::AbstractString) = unsafe_wrap(Vector{UInt8}, String(s)) Array{UInt8}(s::AbstractString) = unsafe_wrap(Vector{UInt8}, String(s)) Vector{T}(s::AbstractString) where {T<:AbstractChar} = collect(T, s) Symbol(s::AbstractString) = Symbol(String(s)) Symbol(x...) = Symbol(string(x...)) convert(::Type{T}, s::T) where {T<:AbstractString} = s convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s)::T ## summary ## function summary(io::IO, s::AbstractString) prefix = isempty(s) ? "empty" : string(ncodeunits(s), "-codeunit") print(io, prefix, " ", typeof(s)) end ## string & character concatenation ## """ *(s::Union{AbstractString, AbstractChar}, t::Union{AbstractString, AbstractChar}...) -> AbstractString Concatenate strings and/or characters, producing a [`String`](@ref). This is equivalent to calling the [`string`](@ref) function on the arguments. Concatenation of built-in string types always produces a value of type `String` but other string types may choose to return a string of a different type as appropriate. # Examples ```jldoctest julia> "Hello " * "world" "Hello world" julia> 'j' * "ulia" "julia" ``` """ (*)(s1::Union{AbstractChar, AbstractString}, ss::Union{AbstractChar, AbstractString}...) = string(s1, ss...) one(::Union{T,Type{T}}) where {T<:AbstractString} = convert(T, "") ## generic string comparison ## """ cmp(a::AbstractString, b::AbstractString) -> Int Compare two strings. Return `0` if both strings have the same length and the character at each index is the same in both strings. Return `-1` if `a` is a prefix of `b`, or if `a` comes before `b` in alphabetical order. Return `1` if `b` is a prefix of `a`, or if `b` comes before `a` in alphabetical order (technically, lexicographical order by Unicode code points). # Examples ```jldoctest julia> cmp("abc", "abc") 0 julia> cmp("ab", "abc") -1 julia> cmp("abc", "ab") 1 julia> cmp("ab", "ac") -1 julia> cmp("ac", "ab") 1 julia> cmp("ฮฑ", "a") 1 julia> cmp("b", "ฮฒ") -1 ``` """ function cmp(a::AbstractString, b::AbstractString) a === b && return 0 (iv1, iv2) = (iterate(a), iterate(b)) while iv1 !== nothing && iv2 !== nothing (c, d) = (first(iv1)::AbstractChar, first(iv2)::AbstractChar) c โ‰  d && return ifelse(c < d, -1, 1) (iv1, iv2) = (iterate(a, last(iv1)), iterate(b, last(iv2))) end return iv1 === nothing ? (iv2 === nothing ? 0 : -1) : 1 end """ ==(a::AbstractString, b::AbstractString) -> Bool Test whether two strings are equal character by character (technically, Unicode code point by code point). # Examples ```jldoctest julia> "abc" == "abc" true julia> "abc" == "ฮฑฮฒฮณ" false ``` """ ==(a::AbstractString, b::AbstractString) = cmp(a, b) == 0 """ isless(a::AbstractString, b::AbstractString) -> Bool Test whether string `a` comes before string `b` in alphabetical order (technically, in lexicographical order by Unicode code points). # Examples ```jldoctest julia> isless("a", "b") true julia> isless("ฮฒ", "ฮฑ") false julia> isless("a", "a") false ``` """ isless(a::AbstractString, b::AbstractString) = cmp(a, b) < 0 # faster comparisons for symbols @assume_effects :total function cmp(a::Symbol, b::Symbol) Int(sign(ccall(:strcmp, Int32, (Cstring, Cstring), a, b))) end isless(a::Symbol, b::Symbol) = cmp(a, b) < 0 # hashing hash(s::AbstractString, h::UInt) = hash(String(s), h) ## character index arithmetic ## """ length(s::AbstractString) -> Int length(s::AbstractString, i::Integer, j::Integer) -> Int Return the number of characters in string `s` from indices `i` through `j`. This is computed as the number of code unit indices from `i` to `j` which are valid character indices. With only a single string argument, this computes the number of characters in the entire string. With `i` and `j` arguments it computes the number of indices between `i` and `j` inclusive that are valid indices in the string `s`. In addition to in-bounds values, `i` may take the out-of-bounds value `ncodeunits(s) + 1` and `j` may take the out-of-bounds value `0`. !!! note The time complexity of this operation is linear in general. That is, it will take the time proportional to the number of bytes or characters in the string because it counts the value on the fly. This is in contrast to the method for arrays, which is a constant-time operation. See also [`isvalid`](@ref), [`ncodeunits`](@ref), [`lastindex`](@ref), [`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref). # Examples ```jldoctest julia> length("jฮผฮ›Iฮฑ") 5 ``` """ length(s::AbstractString) = @inbounds return length(s, 1, ncodeunits(s)::Int) function length(s::AbstractString, i::Int, j::Int) @boundscheck begin 0 < i โ‰ค ncodeunits(s)::Int+1 || throw(BoundsError(s, i)) 0 โ‰ค j < ncodeunits(s)::Int+1 || throw(BoundsError(s, j)) end n = 0 for k = i:j @inbounds n += isvalid(s, k) end return n end @propagate_inbounds length(s::AbstractString, i::Integer, j::Integer) = length(s, Int(i), Int(j)) """ thisind(s::AbstractString, i::Integer) -> Int If `i` is in bounds in `s` return the index of the start of the character whose encoding code unit `i` is part of. In other words, if `i` is the start of a character, return `i`; if `i` is not the start of a character, rewind until the start of a character and return that index. If `i` is equal to 0 or `ncodeunits(s)+1` return `i`. In all other cases throw `BoundsError`. # Examples ```jldoctest julia> thisind("ฮฑ", 0) 0 julia> thisind("ฮฑ", 1) 1 julia> thisind("ฮฑ", 2) 1 julia> thisind("ฮฑ", 3) 3 julia> thisind("ฮฑ", 4) ERROR: BoundsError: attempt to access 2-codeunit String at index [4] [...] julia> thisind("ฮฑ", -1) ERROR: BoundsError: attempt to access 2-codeunit String at index [-1] [...] ``` """ thisind(s::AbstractString, i::Integer) = thisind(s, Int(i)) function thisind(s::AbstractString, i::Int) z = ncodeunits(s)::Int + 1 i == z && return i @boundscheck 0 โ‰ค i โ‰ค z || throw(BoundsError(s, i)) @inbounds while 1 < i && !(isvalid(s, i)::Bool) i -= 1 end return i end """ prevind(str::AbstractString, i::Integer, n::Integer=1) -> Int * Case `n == 1` If `i` is in bounds in `s` return the index of the start of the character whose encoding starts before index `i`. In other words, if `i` is the start of a character, return the start of the previous character; if `i` is not the start of a character, rewind until the start of a character and return that index. If `i` is equal to `1` return `0`. If `i` is equal to `ncodeunits(str)+1` return `lastindex(str)`. Otherwise throw `BoundsError`. * Case `n > 1` Behaves like applying `n` times `prevind` for `n==1`. The only difference is that if `n` is so large that applying `prevind` would reach `0` then each remaining iteration decreases the returned value by `1`. This means that in this case `prevind` can return a negative value. * Case `n == 0` Return `i` only if `i` is a valid index in `str` or is equal to `ncodeunits(str)+1`. Otherwise `StringIndexError` or `BoundsError` is thrown. # Examples ```jldoctest julia> prevind("ฮฑ", 3) 1 julia> prevind("ฮฑ", 1) 0 julia> prevind("ฮฑ", 0) ERROR: BoundsError: attempt to access 2-codeunit String at index [0] [...] julia> prevind("ฮฑ", 2, 2) 0 julia> prevind("ฮฑ", 2, 3) -1 ``` """ prevind(s::AbstractString, i::Integer, n::Integer) = prevind(s, Int(i), Int(n)) prevind(s::AbstractString, i::Integer) = prevind(s, Int(i)) prevind(s::AbstractString, i::Int) = prevind(s, i, 1) function prevind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) z = ncodeunits(s) + 1 @boundscheck 0 < i โ‰ค z || throw(BoundsError(s, i)) n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) while n > 0 && 1 < i @inbounds n -= isvalid(s, i -= 1) end return i - n end """ nextind(str::AbstractString, i::Integer, n::Integer=1) -> Int * Case `n == 1` If `i` is in bounds in `s` return the index of the start of the character whose encoding starts after index `i`. In other words, if `i` is the start of a character, return the start of the next character; if `i` is not the start of a character, move forward until the start of a character and return that index. If `i` is equal to `0` return `1`. If `i` is in bounds but greater or equal to `lastindex(str)` return `ncodeunits(str)+1`. Otherwise throw `BoundsError`. * Case `n > 1` Behaves like applying `n` times `nextind` for `n==1`. The only difference is that if `n` is so large that applying `nextind` would reach `ncodeunits(str)+1` then each remaining iteration increases the returned value by `1`. This means that in this case `nextind` can return a value greater than `ncodeunits(str)+1`. * Case `n == 0` Return `i` only if `i` is a valid index in `s` or is equal to `0`. Otherwise `StringIndexError` or `BoundsError` is thrown. # Examples ```jldoctest julia> nextind("ฮฑ", 0) 1 julia> nextind("ฮฑ", 1) 3 julia> nextind("ฮฑ", 3) ERROR: BoundsError: attempt to access 2-codeunit String at index [3] [...] julia> nextind("ฮฑ", 0, 2) 3 julia> nextind("ฮฑ", 1, 2) 4 ``` """ nextind(s::AbstractString, i::Integer, n::Integer) = nextind(s, Int(i), Int(n)) nextind(s::AbstractString, i::Integer) = nextind(s, Int(i)) nextind(s::AbstractString, i::Int) = nextind(s, i, 1) function nextind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) z = ncodeunits(s) @boundscheck 0 โ‰ค i โ‰ค z || throw(BoundsError(s, i)) n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) while n > 0 && i < z @inbounds n -= isvalid(s, i += 1) end return i + n end ## string index iteration type ## struct EachStringIndex{T<:AbstractString} s::T end keys(s::AbstractString) = EachStringIndex(s) length(e::EachStringIndex) = length(e.s) first(::EachStringIndex) = 1 last(e::EachStringIndex) = lastindex(e.s) iterate(e::EachStringIndex, state=firstindex(e.s)) = state > ncodeunits(e.s) ? nothing : (state, nextind(e.s, state)) eltype(::Type{<:EachStringIndex}) = Int """ isascii(c::Union{AbstractChar,AbstractString}) -> Bool Test whether a character belongs to the ASCII character set, or whether this is true for all elements of a string. # Examples ```jldoctest julia> isascii('a') true julia> isascii('ฮฑ') false julia> isascii("abc") true julia> isascii("ฮฑฮฒฮณ") false ``` For example, `isascii` can be used as a predicate function for [`filter`](@ref) or [`replace`](@ref) to remove or replace non-ASCII characters, respectively: ```jldoctest julia> filter(isascii, "abcdeฮณfgh") # discard non-ASCII chars "abcdefgh" julia> replace("abcdeฮณfgh", !isascii=>' ') # replace non-ASCII chars with spaces "abcde fgh" ``` """ isascii(c::Char) = bswap(reinterpret(UInt32, c)) < 0x80 isascii(s::AbstractString) = all(isascii, s) isascii(c::AbstractChar) = UInt32(c) < 0x80 @inline function _isascii(code_units::AbstractVector{CU}, first, last) where {CU} r = zero(CU) for n = first:last @inbounds r |= code_units[n] end return 0 โ‰ค r < 0x80 end #The chunking algorithm makes the last two chunks overlap inorder to keep the size fixed @inline function _isascii_chunks(chunk_size,cu::AbstractVector{CU}, first,last) where {CU} n=first while n <= last - chunk_size _isascii(cu,n,n+chunk_size-1) || return false n += chunk_size end return _isascii(cu,last-chunk_size+1,last) end """ isascii(cu::AbstractVector{CU}) where {CU <: Integer} -> Bool Test whether all values in the vector belong to the ASCII character set (0x00 to 0x7f). This function is intended to be used by other string implementations that need a fast ASCII check. """ function isascii(cu::AbstractVector{CU}) where {CU <: Integer} chunk_size = 1024 chunk_threshold = chunk_size + (chunk_size รท 2) first = firstindex(cu); last = lastindex(cu) l = last - first + 1 l < chunk_threshold && return _isascii(cu,first,last) return _isascii_chunks(chunk_size,cu,first,last) end ## string map, filter ## function map(f, s::AbstractString) out = StringVector(max(4, sizeof(s)::Intรทsizeof(codeunit(s)::CodeunitType))) index = UInt(1) for c::AbstractChar in s cโ€ฒ = f(c) isa(cโ€ฒ, AbstractChar) || throw(ArgumentError( "map(f, s::AbstractString) requires f to return AbstractChar; " * "try map(f, collect(s)) or a comprehension instead")) index + 3 > length(out) && resize!(out, unsigned(2 * length(out))) index += __unsafe_string!(out, convert(Char, cโ€ฒ), index) end resize!(out, index-1) sizehint!(out, index-1) return String(out) end function filter(f, s::AbstractString) out = IOBuffer(sizehint=sizeof(s)) for c in s f(c) && write(out, c) end String(_unsafe_take!(out)) end ## string first and last ## """ first(s::AbstractString, n::Integer) Get a string consisting of the first `n` characters of `s`. # Examples ```jldoctest julia> first("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 0) "" julia> first("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 1) "โˆ€" julia> first("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 3) "โˆ€ฯตโ‰ " ``` """ first(s::AbstractString, n::Integer) = @inbounds s[1:min(end, nextind(s, 0, n))] """ last(s::AbstractString, n::Integer) Get a string consisting of the last `n` characters of `s`. # Examples ```jldoctest julia> last("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 0) "" julia> last("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 1) "0" julia> last("โˆ€ฯตโ‰ 0: ฯตยฒ>0", 3) "ยฒ>0" ``` """ last(s::AbstractString, n::Integer) = @inbounds s[max(1, prevind(s, ncodeunits(s)+1, n)):end] """ reverseind(v, i) Given an index `i` in [`reverse(v)`](@ref), return the corresponding index in `v` so that `v[reverseind(v,i)] == reverse(v)[i]`. (This can be nontrivial in cases where `v` contains non-ASCII characters.) # Examples ```jldoctest julia> s = "Julia๐Ÿš€" "Julia๐Ÿš€" julia> r = reverse(s) "๐Ÿš€ailuJ" julia> for i in eachindex(s) print(r[reverseind(r, i)]) end Julia๐Ÿš€ ``` """ reverseind(s::AbstractString, i::Integer) = thisind(s, ncodeunits(s)-i+1) """ repeat(s::AbstractString, r::Integer) Repeat a string `r` times. This can be written as `s^r`. See also [`^`](@ref :^(::Union{AbstractString, AbstractChar}, ::Integer)). # Examples ```jldoctest julia> repeat("ha", 3) "hahaha" ``` """ repeat(s::AbstractString, r::Integer) = repeat(String(s), r) """ ^(s::Union{AbstractString,AbstractChar}, n::Integer) -> AbstractString Repeat a string or character `n` times. This can also be written as `repeat(s, n)`. See also [`repeat`](@ref). # Examples ```jldoctest julia> "Test "^3 "Test Test Test " ``` """ (^)(s::Union{AbstractString,AbstractChar}, r::Integer) = repeat(s, r) # reverse-order iteration for strings and indices thereof iterate(r::Iterators.Reverse{<:AbstractString}, i=lastindex(r.itr)) = i < firstindex(r.itr) ? nothing : (r.itr[i], prevind(r.itr, i)) iterate(r::Iterators.Reverse{<:EachStringIndex}, i=lastindex(r.itr.s)) = i < firstindex(r.itr.s) ? nothing : (i, prevind(r.itr.s, i)) ## code unit access ## """ CodeUnits(s::AbstractString) Wrap a string (without copying) in an immutable vector-like object that accesses the code units of the string's representation. """ struct CodeUnits{T,S<:AbstractString} <: DenseVector{T} s::S CodeUnits(s::S) where {S<:AbstractString} = new{codeunit(s),S}(s) end length(s::CodeUnits) = ncodeunits(s.s) sizeof(s::CodeUnits{T}) where {T} = ncodeunits(s.s) * sizeof(T) size(s::CodeUnits) = (length(s),) elsize(s::Type{<:CodeUnits{T}}) where {T} = sizeof(T) @propagate_inbounds getindex(s::CodeUnits, i::Int) = codeunit(s.s, i) IndexStyle(::Type{<:CodeUnits}) = IndexLinear() @inline iterate(s::CodeUnits, i=1) = (i % UInt) - 1 < length(s) ? (@inbounds s[i], i + 1) : nothing write(io::IO, s::CodeUnits) = write(io, s.s) unsafe_convert(::Type{Ptr{T}}, s::CodeUnits{T}) where {T} = unsafe_convert(Ptr{T}, s.s) unsafe_convert(::Type{Ptr{Int8}}, s::CodeUnits{UInt8}) = unsafe_convert(Ptr{Int8}, s.s) """ codeunits(s::AbstractString) Obtain a vector-like object containing the code units of a string. Returns a `CodeUnits` wrapper by default, but `codeunits` may optionally be defined for new string types if necessary. # Examples ```jldoctest julia> codeunits("Juฮปia") 6-element Base.CodeUnits{UInt8, String}: 0x4a 0x75 0xce 0xbb 0x69 0x61 ``` """ codeunits(s::AbstractString) = CodeUnits(s) function _split_rest(s::AbstractString, n::Int) lastind = lastindex(s) i = try prevind(s, lastind, n) catch e e isa BoundsError || rethrow() _check_length_split_rest(length(s), n) end last_n = SubString(s, nextind(s, i), lastind) front = s[begin:i] return front, last_n end U/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/string.jlœZ# This file is a part of Julia. License is MIT: https://julialang.org/license """ StringIndexError(str, i) An error occurred when trying to access `str` at index `i` that is not valid. """ struct StringIndexError <: Exception string::AbstractString index::Integer end @noinline string_index_err(s::AbstractString, i::Integer) = throw(StringIndexError(s, Int(i))) function Base.showerror(io::IO, exc::StringIndexError) s = exc.string print(io, "StringIndexError: ", "invalid index [$(exc.index)]") if firstindex(s) <= exc.index <= ncodeunits(s) iprev = thisind(s, exc.index) inext = nextind(s, iprev) escprev = escape_string(s[iprev:iprev]) if inext <= ncodeunits(s) escnext = escape_string(s[inext:inext]) print(io, ", valid nearby indices [$iprev]=>'$escprev', [$inext]=>'$escnext'") else print(io, ", valid nearby index [$iprev]=>'$escprev'") end end end const ByteArray = Union{CodeUnits{UInt8,String}, Vector{UInt8},Vector{Int8}, FastContiguousSubArray{UInt8,1,CodeUnits{UInt8,String}}, FastContiguousSubArray{UInt8,1,Vector{UInt8}}, FastContiguousSubArray{Int8,1,Vector{Int8}}} @inline between(b::T, lo::T, hi::T) where {T<:Integer} = (lo โ‰ค b) & (b โ‰ค hi) """ String <: AbstractString The default string type in Julia, used by e.g. string literals. `String`s are immutable sequences of `Char`s. A `String` is stored internally as a contiguous byte array, and while they are interpreted as being UTF-8 encoded, they can be composed of any byte sequence. Use [`isvalid`](@ref) to validate that the underlying byte sequence is valid as UTF-8. """ String ## constructors and conversions ## # String constructor docstring from boot.jl, workaround for #16730 # and the unavailability of @doc in boot.jl context. """ String(v::AbstractVector{UInt8}) Create a new `String` object using the data buffer from byte vector `v`. If `v` is a `Vector{UInt8}` it will be truncated to zero length and future modification of `v` cannot affect the contents of the resulting string. To avoid truncation of `Vector{UInt8}` data, use `String(copy(v))`; for other `AbstractVector` types, `String(v)` already makes a copy. When possible, the memory of `v` will be used without copying when the `String` object is created. This is guaranteed to be the case for byte vectors returned by [`take!`](@ref) on a writable [`IOBuffer`](@ref) and by calls to [`read(io, nb)`](@ref). This allows zero-copy conversion of I/O data to strings. In other cases, `Vector{UInt8}` data may be copied, but `v` is truncated anyway to guarantee consistent behavior. """ String(v::AbstractVector{UInt8}) = String(copyto!(StringVector(length(v)), v)) String(v::Vector{UInt8}) = ccall(:jl_array_to_string, Ref{String}, (Any,), v) """ unsafe_string(p::Ptr{UInt8}, [length::Integer]) Copy a string from the address of a C-style (NUL-terminated) string encoded as UTF-8. (The pointer can be safely freed afterwards.) If `length` is specified (the length of the data in bytes), the string does not have to be NUL-terminated. This function is labeled "unsafe" because it will crash if `p` is not a valid memory address to data of the requested length. """ function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}, len::Integer) p == C_NULL && throw(ArgumentError("cannot convert NULL to string")) ccall(:jl_pchar_to_string, Ref{String}, (Ptr{UInt8}, Int), p, len) end function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}) p == C_NULL && throw(ArgumentError("cannot convert NULL to string")) ccall(:jl_cstr_to_string, Ref{String}, (Ptr{UInt8},), p) end # This is @assume_effects :effect_free :nothrow :terminates_globally @ccall jl_alloc_string(n::Csize_t)::Ref{String}, # but the macro is not available at this time in bootstrap, so we write it manually. @eval _string_n(n::Integer) = $(Expr(:foreigncall, QuoteNode(:jl_alloc_string), Ref{String}, Expr(:call, Expr(:core, :svec), :Csize_t), 1, QuoteNode((:ccall,0xe)), :(convert(Csize_t, n)))) """ String(s::AbstractString) Create a new `String` from an existing `AbstractString`. """ String(s::AbstractString) = print_to_string(s) @assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) unsafe_wrap(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) unsafe_wrap(::Type{Vector{UInt8}}, s::FastContiguousSubArray{UInt8,1,Vector{UInt8}}) = unsafe_wrap(Vector{UInt8}, pointer(s), size(s)) Vector{UInt8}(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s) Vector{UInt8}(s::String) = Vector{UInt8}(codeunits(s)) Array{UInt8}(s::String) = Vector{UInt8}(codeunits(s)) String(s::CodeUnits{UInt8,String}) = s.s ## low-level functions ## pointer(s::String) = unsafe_convert(Ptr{UInt8}, s) pointer(s::String, i::Integer) = pointer(s) + Int(i)::Int - 1 ncodeunits(s::String) = Core.sizeof(s) codeunit(s::String) = UInt8 codeunit(s::String, i::Integer) = codeunit(s, Int(i)) @assume_effects :foldable @inline function codeunit(s::String, i::Int) @boundscheck checkbounds(s, i) b = GC.@preserve s unsafe_load(pointer(s, i)) return b end ## comparison ## @assume_effects :total _memcmp(a::String, b::String) = @invoke _memcmp(a::Union{Ptr{UInt8},AbstractString},b::Union{Ptr{UInt8},AbstractString}) _memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}) = _memcmp(a, b, min(sizeof(a), sizeof(b))) function _memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}, len::Int) GC.@preserve a b begin pa = unsafe_convert(Ptr{UInt8}, a) pb = unsafe_convert(Ptr{UInt8}, b) memcmp(pa, pb, len % Csize_t) % Int end end function cmp(a::String, b::String) al, bl = sizeof(a), sizeof(b) c = _memcmp(a, b) return c < 0 ? -1 : c > 0 ? +1 : cmp(al,bl) end ==(a::String, b::String) = a===b typemin(::Type{String}) = "" typemin(::String) = typemin(String) ## thisind, nextind ## @propagate_inbounds thisind(s::String, i::Int) = _thisind_str(s, i) # s should be String or SubString{String} @inline function _thisind_str(s, i::Int) i == 0 && return 0 n = ncodeunits(s) i == n + 1 && return i @boundscheck between(i, 1, n) || throw(BoundsError(s, i)) @inbounds b = codeunit(s, i) (b & 0xc0 == 0x80) & (i-1 > 0) || return i @inbounds b = codeunit(s, i-1) between(b, 0b11000000, 0b11110111) && return i-1 (b & 0xc0 == 0x80) & (i-2 > 0) || return i @inbounds b = codeunit(s, i-2) between(b, 0b11100000, 0b11110111) && return i-2 (b & 0xc0 == 0x80) & (i-3 > 0) || return i @inbounds b = codeunit(s, i-3) between(b, 0b11110000, 0b11110111) && return i-3 return i end @propagate_inbounds nextind(s::String, i::Int) = _nextind_str(s, i) # s should be String or SubString{String} @inline function _nextind_str(s, i::Int) i == 0 && return 1 n = ncodeunits(s) @boundscheck between(i, 1, n) || throw(BoundsError(s, i)) @inbounds l = codeunit(s, i) (l < 0x80) | (0xf8 โ‰ค l) && return i+1 if l < 0xc0 iโ€ฒ = @inbounds thisind(s, i) return iโ€ฒ < i ? @inbounds(nextind(s, iโ€ฒ)) : i+1 end # first continuation byte (i += 1) > n && return i @inbounds b = codeunit(s, i) b & 0xc0 โ‰  0x80 && return i ((i += 1) > n) | (l < 0xe0) && return i # second continuation byte @inbounds b = codeunit(s, i) b & 0xc0 โ‰  0x80 && return i ((i += 1) > n) | (l < 0xf0) && return i # third continuation byte @inbounds b = codeunit(s, i) ifelse(b & 0xc0 โ‰  0x80, i, i+1) end ## checking UTF-8 & ACSII validity ## #= The UTF-8 Validation is performed by a shift based DFA. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ UTF-8 DFA State Diagram โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€2โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€3โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”Œโ”€โ” โ”Œโ–ผโ” โ”‚ โ”‚ โ”‚ ASCII โ”‚ UTF-8 โ”‚ โ”œโ”€5โ”€โ”€โ–บโ”‚9โ”œโ”€โ”€โ”€1โ”€โ”€โ”€โ”€โ–บ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”ค โ”‚ โ”‚ โ”Œโ–ผโ” โ”‚ โ”‚ โ”‚ โ”Œโ”€0โ”€โ” โ”‚ โ”œโ”€6โ”€โ”€โ–บโ”‚8โ”œโ”€1,7,9โ”€โ”€โ–บ4โ”œโ”€โ”€1,7,9โ”€โ”€โ–บ โ”‚ โ”‚ โ”‚ โ”Œโ”€0โ”€โ” โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”ค โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ–ผโ”€โ”€โ”€โ”ดโ” โ”‚ โ”œโ”€11โ”€โ–บโ”‚7โ”œโ”€โ”€7,9โ”€โ”€โ”€โ–บ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ3โ”œโ”€โ” โ”‚ โ”‚ โ”Œโ–ผโ”€โ”€โ”€โ”ดโ” โ”‚ โ”‚ โ”‚ โ–ผ โ”‚ โ””โ”€โ”˜ โ””โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 0 โ”œโ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ 1 โ”œโ”€โ–บ โ”€โ”€โ”ค โ”‚ โ”Œโ”€โ”€โ”€โ”€โ–บ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ” โ”‚ โ”‚ โ””โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ–ฒโ”€โ”€โ”˜ โ”œโ”€10โ”€โ–บโ”‚5โ”œโ”€โ”€โ”€โ”€โ”€7โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”ค โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€4โ”€โ”€โ–บโ”‚6โ”œโ”€โ”€โ”€โ”€โ”€1,9โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ INVALID โ”‚ โ””โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”Œโ”€*โ”€โ” โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€1,7,9โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”Œโ–ผโ”€โ”€โ”€โ”ดโ” โ”‚ โ”‚ โ”‚ 2 โ—„โ”€โ”€โ”€ All undefined transitions result in state 2 โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Validation States 0 -> _UTF8_DFA_ASCII is the start state and will only stay in this state if the string is only ASCII characters If the DFA ends in this state the string is ASCII only 1 -> _UTF8_DFA_ACCEPT is the valid complete character state of the DFA once it has encountered a UTF-8 Unicode character 2 -> _UTF8_DFA_INVALID is only reached by invalid bytes and once in this state it will not change as seen by all 1s in that column of table below 3 -> One valid continuation byte needed to return to state 0 4,5,6 -> Two valid continuation bytes needed to return to state 0 7,8,9 -> Three valids continuation bytes needed to return to state 0 Current State 0ฬฒ 1ฬฒ 2ฬฒ 3ฬฒ 4ฬฒ 5ฬฒ 6ฬฒ 7ฬฒ 8ฬฒ 9ฬฒ 0 | 0 1 2 2 2 2 2 2 2 2 1 | 2 2 2 1 3 2 3 2 4 4 2 | 3 3 2 2 2 2 2 2 2 2 3 | 4 4 2 2 2 2 2 2 2 2 4 | 6 6 2 2 2 2 2 2 2 2 Character 5 | 9 9 2 2 2 2 2 2 2 2 <- Next State Class 6 | 8 8 2 2 2 2 2 2 2 2 7 | 2 2 2 1 3 3 2 4 4 2 8 | 2 2 2 2 2 2 2 2 2 2 9 | 2 2 2 1 3 2 3 4 4 2 10 | 5 5 2 2 2 2 2 2 2 2 11 | 7 7 2 2 2 2 2 2 2 2 Shifts | 0 4 10 14 18 24 8 20 12 26 The shifts that represent each state were derived using teh SMT solver Z3, to ensure when encoded into the rows the correct shift was a result. Each character class row is encoding 10 states with shifts as defined above. By shifting the bitsof a row by the current state then masking the result with 0x11110 give the shift for the new state =# #State type used by UTF-8 DFA const _UTF8DFAState = UInt32 # Fill the table with 256 UInt64 representing the DFA transitions for all bytes const _UTF8_DFA_TABLE = let # let block rather than function doesn't pollute base num_classes=12 num_states=10 bit_per_state = 6 # These shifts were derived using a SMT solver state_shifts = [0, 4, 10, 14, 18, 24, 8, 20, 12, 26] character_classes = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ] # These are the rows discussed in comments above state_arrays = [ 0 1 2 2 2 2 2 2 2 2; 2 2 2 1 3 2 3 2 4 4; 3 3 2 2 2 2 2 2 2 2; 4 4 2 2 2 2 2 2 2 2; 6 6 2 2 2 2 2 2 2 2; 9 9 2 2 2 2 2 2 2 2; 8 8 2 2 2 2 2 2 2 2; 2 2 2 1 3 3 2 4 4 2; 2 2 2 2 2 2 2 2 2 2; 2 2 2 1 3 2 3 4 4 2; 5 5 2 2 2 2 2 2 2 2; 7 7 2 2 2 2 2 2 2 2] #This converts the state_arrays into the shift encoded _UTF8DFAState class_row = zeros(_UTF8DFAState, num_classes) for i = 1:num_classes row = _UTF8DFAState(0) for j in 1:num_states #Calculate the shift required for the next state to_shift = UInt8((state_shifts[state_arrays[i,j]+1]) ) #Shift the next state into the position of the current state row = row | (_UTF8DFAState(to_shift) << state_shifts[j]) end class_row[i]=row end map(c->class_row[c+1],character_classes) end const _UTF8_DFA_ASCII = _UTF8DFAState(0) #This state represents the start and end of any valid string const _UTF8_DFA_ACCEPT = _UTF8DFAState(4) #This state represents the start and end of any valid string const _UTF8_DFA_INVALID = _UTF8DFAState(10) # If the state machine is ever in this state just stop # The dfa step is broken out so that it may be used in other functions. The mask was calculated to work with state shifts above @inline _utf_dfa_step(state::_UTF8DFAState, byte::UInt8) = @inbounds (_UTF8_DFA_TABLE[byte+1] >> state) & _UTF8DFAState(0x0000001E) @inline function _isvalid_utf8_dfa(state::_UTF8DFAState, bytes::AbstractVector{UInt8}, first::Int = firstindex(bytes), last::Int = lastindex(bytes)) for i = first:last @inbounds state = _utf_dfa_step(state, bytes[i]) end return (state) end @inline function _find_nonascii_chunk(chunk_size,cu::AbstractVector{CU}, first,last) where {CU} n=first while n <= last - chunk_size _isascii(cu,n,n+chunk_size-1) || return n n += chunk_size end n= last-chunk_size+1 _isascii(cu,n,last) || return n return nothing end ## # Classifcations of string # 0: neither valid ASCII nor UTF-8 # 1: valid ASCII # 2: valid UTF-8 byte_string_classify(s::AbstractString) = byte_string_classify(codeunits(s)) function byte_string_classify(bytes::AbstractVector{UInt8}) chunk_size = 1024 chunk_threshold = chunk_size + (chunk_size รท 2) n = length(bytes) if n > chunk_threshold start = _find_nonascii_chunk(chunk_size,bytes,1,n) isnothing(start) && return 1 else _isascii(bytes,1,n) && return 1 start = 1 end return _byte_string_classify_nonascii(bytes,start,n) end function _byte_string_classify_nonascii(bytes::AbstractVector{UInt8}, first::Int, last::Int) chunk_size = 256 start = first stop = min(last,first + chunk_size - 1) state = _UTF8_DFA_ACCEPT while start <= last # try to process ascii chunks while state == _UTF8_DFA_ACCEPT _isascii(bytes,start,stop) || break (start = start + chunk_size) <= last || break stop = min(last,stop + chunk_size) end # Process non ascii chunk state = _isvalid_utf8_dfa(state,bytes,start,stop) state == _UTF8_DFA_INVALID && return 0 start = start + chunk_size stop = min(last,stop + chunk_size) end return ifelse(state == _UTF8_DFA_ACCEPT,2,0) end isvalid(::Type{String}, bytes::AbstractVector{UInt8}) = (@inline byte_string_classify(bytes)) โ‰  0 isvalid(::Type{String}, s::AbstractString) = (@inline byte_string_classify(s)) โ‰  0 @inline isvalid(s::AbstractString) = @inline isvalid(String, codeunits(s)) is_valid_continuation(c) = c & 0xc0 == 0x80 ## required core functionality ## @inline function iterate(s::String, i::Int=firstindex(s)) (i % UInt) - 1 < ncodeunits(s) || return nothing b = @inbounds codeunit(s, i) u = UInt32(b) << 24 between(b, 0x80, 0xf7) || return reinterpret(Char, u), i+1 return iterate_continued(s, i, u) end function iterate_continued(s::String, i::Int, u::UInt32) u < 0xc0000000 && (i += 1; @goto ret) n = ncodeunits(s) # first continuation byte (i += 1) > n && @goto ret @inbounds b = codeunit(s, i) b & 0xc0 == 0x80 || @goto ret u |= UInt32(b) << 16 # second continuation byte ((i += 1) > n) | (u < 0xe0000000) && @goto ret @inbounds b = codeunit(s, i) b & 0xc0 == 0x80 || @goto ret u |= UInt32(b) << 8 # third continuation byte ((i += 1) > n) | (u < 0xf0000000) && @goto ret @inbounds b = codeunit(s, i) b & 0xc0 == 0x80 || @goto ret u |= UInt32(b); i += 1 @label ret return reinterpret(Char, u), i end @propagate_inbounds function getindex(s::String, i::Int) b = codeunit(s, i) u = UInt32(b) << 24 between(b, 0x80, 0xf7) || return reinterpret(Char, u) return getindex_continued(s, i, u) end function getindex_continued(s::String, i::Int, u::UInt32) if u < 0xc0000000 # called from `getindex` which checks bounds @inbounds isvalid(s, i) && @goto ret string_index_err(s, i) end n = ncodeunits(s) (i += 1) > n && @goto ret @inbounds b = codeunit(s, i) # cont byte 1 b & 0xc0 == 0x80 || @goto ret u |= UInt32(b) << 16 ((i += 1) > n) | (u < 0xe0000000) && @goto ret @inbounds b = codeunit(s, i) # cont byte 2 b & 0xc0 == 0x80 || @goto ret u |= UInt32(b) << 8 ((i += 1) > n) | (u < 0xf0000000) && @goto ret @inbounds b = codeunit(s, i) # cont byte 3 b & 0xc0 == 0x80 || @goto ret u |= UInt32(b) @label ret return reinterpret(Char, u) end getindex(s::String, r::AbstractUnitRange{<:Integer}) = s[Int(first(r)):Int(last(r))] @inline function getindex(s::String, r::UnitRange{Int}) isempty(r) && return "" i, j = first(r), last(r) @boundscheck begin checkbounds(s, r) @inbounds isvalid(s, i) || string_index_err(s, i) @inbounds isvalid(s, j) || string_index_err(s, j) end j = nextind(s, j) - 1 n = j - i + 1 ss = _string_n(n) GC.@preserve s ss unsafe_copyto!(pointer(ss), pointer(s, i), n) return ss end # nothrow because we know the start and end indices are valid @assume_effects :nothrow length(s::String) = length_continued(s, 1, ncodeunits(s), ncodeunits(s)) # effects needed because @inbounds @assume_effects :consistent :effect_free @inline function length(s::String, i::Int, j::Int) @boundscheck begin 0 < i โ‰ค ncodeunits(s)+1 || throw(BoundsError(s, i)) 0 โ‰ค j < ncodeunits(s)+1 || throw(BoundsError(s, j)) end j < i && return 0 @inbounds i, k = thisind(s, i), i c = j - i + (i == k) @inbounds length_continued(s, i, j, c) end @assume_effects :terminates_locally @inline @propagate_inbounds function length_continued(s::String, i::Int, n::Int, c::Int) i < n || return c b = codeunit(s, i) while true while true (i += 1) โ‰ค n || return c 0xc0 โ‰ค b โ‰ค 0xf7 && break b = codeunit(s, i) end l = b b = codeunit(s, i) # cont byte 1 c -= (x = b & 0xc0 == 0x80) x & (l โ‰ฅ 0xe0) || continue (i += 1) โ‰ค n || return c b = codeunit(s, i) # cont byte 2 c -= (x = b & 0xc0 == 0x80) x & (l โ‰ฅ 0xf0) || continue (i += 1) โ‰ค n || return c b = codeunit(s, i) # cont byte 3 c -= (b & 0xc0 == 0x80) end end ## overload methods for efficiency ## isvalid(s::String, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i isascii(s::String) = isascii(codeunits(s)) # don't assume effects for general integers since we cannot know their implementation @assume_effects :foldable repeat(c::Char, r::BitInteger) = @invoke repeat(c::Char, r::Integer) """ repeat(c::AbstractChar, r::Integer) -> String Repeat a character `r` times. This can equivalently be accomplished by calling [`c^r`](@ref :^(::Union{AbstractString, AbstractChar}, ::Integer)). # Examples ```jldoctest julia> repeat('A', 3) "AAA" ``` """ function repeat(c::AbstractChar, r::Integer) c = Char(c)::Char r == 0 && return "" r < 0 && throw(ArgumentError("can't repeat a character $r times")) u = bswap(reinterpret(UInt32, c)) n = 4 - (leading_zeros(u | 0xff) >> 3) s = _string_n(n*r) p = pointer(s) GC.@preserve s if n == 1 memset(p, u % UInt8, r) elseif n == 2 p16 = reinterpret(Ptr{UInt16}, p) for i = 1:r unsafe_store!(p16, u % UInt16, i) end elseif n == 3 b1 = (u >> 0) % UInt8 b2 = (u >> 8) % UInt8 b3 = (u >> 16) % UInt8 for i = 0:r-1 unsafe_store!(p, b1, 3i + 1) unsafe_store!(p, b2, 3i + 2) unsafe_store!(p, b3, 3i + 3) end elseif n == 4 p32 = reinterpret(Ptr{UInt32}, p) for i = 1:r unsafe_store!(p32, u, i) end end return s end X/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/substring.jl1'# This file is a part of Julia. License is MIT: https://julialang.org/license """ SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) SubString(s::AbstractString, r::UnitRange{<:Integer}) Like [`getindex`](@ref), but returns a view into the parent string `s` within range `i:j` or `r` respectively instead of making a copy. The [`@views`](@ref) macro converts any string slices `s[i:j]` into substrings `SubString(s, i, j)` in a block of code. # Examples ```jldoctest julia> SubString("abc", 1, 2) "ab" julia> SubString("abc", 1:2) "ab" julia> SubString("abc", 2) "bc" ``` """ struct SubString{T<:AbstractString} <: AbstractString string::T offset::Int ncodeunits::Int function SubString{T}(s::T, i::Int, j::Int) where T<:AbstractString i โ‰ค j || return new(s, 0, 0) @boundscheck begin checkbounds(s, i:j) @inbounds isvalid(s, i) || string_index_err(s, i) @inbounds isvalid(s, j) || string_index_err(s, j) end return new(s, i-1, nextind(s,j)-i) end end @propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j) @propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j)) @propagate_inbounds SubString(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, first(r), last(r)) @propagate_inbounds function SubString(s::SubString, i::Int, j::Int) @boundscheck i โ‰ค j && checkbounds(s, i:j) SubString(s.string, s.offset+i, s.offset+j) end SubString(s::AbstractString) = SubString(s, 1, lastindex(s)::Int) SubString{T}(s::T) where {T<:AbstractString} = SubString{T}(s, 1, lastindex(s)::Int) @propagate_inbounds view(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r) @propagate_inbounds maybeview(s::AbstractString, r::AbstractUnitRange{<:Integer}) = view(s, r) @propagate_inbounds maybeview(s::AbstractString, args...) = getindex(s, args...) convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} = SubString(convert(S, s))::SubString{S} convert(::Type{T}, s::T) where {T<:SubString} = s # Regex match allows only Union{String, SubString{String}} so define conversion to this type convert(::Type{Union{String, SubString{String}}}, s::String) = s convert(::Type{Union{String, SubString{String}}}, s::SubString{String}) = s convert(::Type{Union{String, SubString{String}}}, s::AbstractString) = convert(String, s)::String function String(s::SubString{String}) parent = s.string copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits) return copy end ncodeunits(s::SubString) = s.ncodeunits codeunit(s::SubString) = codeunit(s.string)::CodeunitType length(s::SubString) = length(s.string, s.offset+1, s.offset+s.ncodeunits) function codeunit(s::SubString, i::Integer) @boundscheck checkbounds(s, i) @inbounds return codeunit(s.string, s.offset + i) end function iterate(s::SubString, i::Integer=firstindex(s)) i == ncodeunits(s)+1 && return nothing @boundscheck checkbounds(s, i) y = iterate(s.string, s.offset + i) y === nothing && return nothing c, i = y::Tuple{AbstractChar,Int} return c, i - s.offset end function getindex(s::SubString, i::Integer) @boundscheck checkbounds(s, i) @inbounds return getindex(s.string, s.offset + i) end isascii(ss::SubString{String}) = isascii(codeunits(ss)) function isvalid(s::SubString, i::Integer) ib = true @boundscheck ib = checkbounds(Bool, s, i) @inbounds return ib && isvalid(s.string, s.offset + i)::Bool end thisind(s::SubString{String}, i::Int) = _thisind_str(s, i) nextind(s::SubString{String}, i::Int) = _nextind_str(s, i) parent(s::SubString) = s.string parentindices(s::SubString) = (s.offset + 1 : thisind(s.string, s.offset + s.ncodeunits),) function ==(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) sizeof(a) == sizeof(b) && _memcmp(a, b) == 0 end function cmp(a::SubString{String}, b::SubString{String}) c = _memcmp(a, b) return c < 0 ? -1 : c > 0 ? +1 : cmp(sizeof(a), sizeof(b)) end # don't make unnecessary copies when passing substrings to C functions cconvert(::Type{Ptr{UInt8}}, s::SubString{String}) = s cconvert(::Type{Ptr{Int8}}, s::SubString{String}) = s function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int8, UInt8} convert(Ptr{R}, pointer(s.string)) + s.offset end pointer(x::SubString{String}) = pointer(x.string) + x.offset pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1) function hash(s::SubString{String}, h::UInt) h += memhash_seed ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h end """ reverse(s::AbstractString) -> AbstractString Reverses a string. Technically, this function reverses the codepoints in a string and its main utility is for reversed-order string processing, especially for reversed regular-expression searches. See also [`reverseind`](@ref) to convert indices in `s` to indices in `reverse(s)` and vice-versa, and `graphemes` from module `Unicode` to operate on user-visible "characters" (graphemes) rather than codepoints. See also [`Iterators.reverse`](@ref) for reverse-order iteration without making a copy. Custom string types must implement the `reverse` function themselves and should typically return a string with the same type and encoding. If they return a string with a different encoding, they must also override `reverseind` for that string type to satisfy `s[reverseind(s,i)] == reverse(s)[i]`. # Examples ```jldoctest julia> reverse("JuliaLang") "gnaLailuJ" ``` !!! note The examples below may be rendered differently on different systems. The comments indicate how they're supposed to be rendered Combining characters can lead to surprising results: ```jldoctest julia> reverse("axฬ‚e") # hat is above x in the input, above e in the output "eฬ‚xa" julia> using Unicode julia> join(reverse(collect(graphemes("axฬ‚e")))) # reverses graphemes; hat is above x in both in- and output "exฬ‚a" ``` """ function reverse(s::Union{String,SubString{String}})::String # Read characters forwards from `s` and write backwards to `out` out = _string_n(sizeof(s)) offs = sizeof(s) + 1 for c in s offs -= ncodeunits(c) __unsafe_string!(out, c, offs) end return out end string(a::String) = String(a) string(a::SubString{String}) = String(a) function Symbol(s::SubString{String}) return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), s, sizeof(s)) end @inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector) x = bswap(reinterpret(UInt32, c)) n = ncodeunits(c) GC.@preserve out begin unsafe_store!(pointer(out, offs), x % UInt8) n == 1 && return n x >>= 8 unsafe_store!(pointer(out, offs+1), x % UInt8) n == 2 && return n x >>= 8 unsafe_store!(pointer(out, offs+2), x % UInt8) n == 3 && return n x >>= 8 unsafe_store!(pointer(out, offs+3), x % UInt8) end return n end @assume_effects :nothrow @inline function __unsafe_string!(out, s::String, offs::Integer) n = sizeof(s) GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) return n end @inline function __unsafe_string!(out, s::SubString{String}, offs::Integer) n = sizeof(s) GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) return n end @assume_effects :nothrow @inline function __unsafe_string!(out, s::Symbol, offs::Integer) n = sizeof(s) GC.@preserve s out unsafe_copyto!(pointer(out, offs), unsafe_convert(Ptr{UInt8},s), n) return n end # nothrow needed here because for v in a can't prove the indexing is inbounds. @assume_effects :foldable :nothrow string(a::Union{Char, String, Symbol}...) = _string(a...) string(a::Union{Char, String, SubString{String}, Symbol}...) = _string(a...) function _string(a::Union{Char, String, SubString{String}, Symbol}...) n = 0 for v in a # 4 types is too many for automatic Union-splitting, so we split manually # and allow one specializable call site per concrete type if v isa Char n += ncodeunits(v) elseif v isa String n += sizeof(v) elseif v isa SubString{String} n += sizeof(v) else n += sizeof(v::Symbol) end end out = _string_n(n) offs = 1 for v in a if v isa Char offs += __unsafe_string!(out, v, offs) elseif v isa String || v isa SubString{String} offs += __unsafe_string!(out, v, offs) else offs += __unsafe_string!(out, v::Symbol, offs) end end return out end # don't assume effects for general integers since we cannot know their implementation # not nothrow because r<0 throws @assume_effects :foldable repeat(s::String, r::BitInteger) = @invoke repeat(s::String, r::Integer) function repeat(s::Union{String, SubString{String}}, r::Integer) r < 0 && throw(ArgumentError("can't repeat a string $r times")) r == 0 && return "" r == 1 && return String(s) n = sizeof(s) out = _string_n(n*r) if n == 1 # common case: repeating a single-byte string @inbounds b = codeunit(s, 1) memset(unsafe_convert(Ptr{UInt8}, out), b, r) else for i = 0:r-1 GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n) end end return out end function filter(f, s::Union{String, SubString{String}}) out = StringVector(sizeof(s)) offset = 1 for c in s if f(c) offset += __unsafe_string!(out, c, offset) end end resize!(out, offset-1) sizehint!(out, offset-1) return String(out) end getindex(s::AbstractString, r::AbstractUnitRange{<:Integer}) = SubString(s, r) N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/osutils.jlะ# This file is a part of Julia. License is MIT: https://julialang.org/license """ @static Partially evaluate an expression at parse time. For example, `@static Sys.iswindows() ? foo : bar` will evaluate `Sys.iswindows()` and insert either `foo` or `bar` into the expression. This is useful in cases where a construct would be invalid on other platforms, such as a `ccall` to a non-existent function. `@static if Sys.isapple() foo end` and `@static foo <&&,||> bar` are also valid syntax. """ macro static(ex) if isa(ex, Expr) @label loop hd = ex.head if hd โˆˆ (:if, :elseif, :&&, :||) cond = Core.eval(__module__, ex.args[1])::Bool if xor(cond, hd === :||) return esc(ex.args[2]) elseif length(ex.args) == 3 br = ex.args[3] if br isa Expr && br.head === :elseif ex = br @goto loop else return esc(ex.args[3]) end elseif hd โˆˆ (:if, :elseif) return nothing else return cond end end end throw(ArgumentError("invalid @static macro")) end H/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/c.jl{Z# This file is a part of Julia. License is MIT: https://julialang.org/license # definitions related to C interface import Core.Intrinsics: cglobal, bitcast """ cglobal((symbol, library) [, type=Cvoid]) Obtain a pointer to a global variable in a C-exported shared library, specified exactly as in [`ccall`](@ref). Returns a `Ptr{Type}`, defaulting to `Ptr{Cvoid}` if no `Type` argument is supplied. The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), respectively. """ cglobal """ CFunction struct Garbage-collection handle for the return value from `@cfunction` when the first argument is annotated with '\\\$'. Like all `cfunction` handles, it should be passed to `ccall` as a `Ptr{Cvoid}`, and will be converted automatically at the call site to the appropriate type. See [`@cfunction`](@ref). """ mutable struct CFunction <: Ref{Cvoid} ptr::Ptr{Cvoid} f::Any _1::Ptr{Cvoid} _2::Ptr{Cvoid} let constructor = false end end unsafe_convert(::Type{Ptr{Cvoid}}, cf::CFunction) = cf.ptr """ @cfunction(callable, ReturnType, (ArgumentTypes...,)) -> Ptr{Cvoid} @cfunction(\$callable, ReturnType, (ArgumentTypes...,)) -> CFunction Generate a C-callable function pointer from the Julia function `callable` for the given type signature. To pass the return value to a `ccall`, use the argument type `Ptr{Cvoid}` in the signature. Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression (although it can include a splat expression). And that these arguments will be evaluated in global scope during compile-time (not deferred until runtime). Adding a '\\\$' in front of the function argument changes this to instead create a runtime closure over the local variable `callable` (this is not supported on all architectures). See [manual section on ccall and cfunction usage](@ref Calling-C-and-Fortran-Code). # Examples ```julia-repl julia> function foo(x::Int, y::Int) return x + y end julia> @cfunction(foo, Int, (Int, Int)) Ptr{Cvoid} @0x000000001b82fcd0 ``` """ macro cfunction(f, rt, at) if !(isa(at, Expr) && at.head === :tuple) throw(ArgumentError("@cfunction argument types must be a literal tuple")) end at.head = :call pushfirst!(at.args, GlobalRef(Core, :svec)) if isa(f, Expr) && f.head === :$ fptr = f.args[1] typ = CFunction else fptr = QuoteNode(f) typ = Ptr{Cvoid} end cfun = Expr(:cfunction, typ, fptr, rt, at, QuoteNode(:ccall)) return esc(cfun) end if ccall(:jl_is_char_signed, Ref{Bool}, ()) const Cchar = Int8 else const Cchar = UInt8 end """ Cchar Equivalent to the native `char` c-type. """ Cchar # The ccall here is equivalent to Sys.iswindows(), but that's not defined yet @static if ccall(:jl_get_UNAME, Any, ()) === :NT const Clong = Int32 const Culong = UInt32 const Cwchar_t = UInt16 else const Clong = Int const Culong = UInt const Cwchar_t = Int32 end """ Clong Equivalent to the native `signed long` c-type. """ Clong """ Culong Equivalent to the native `unsigned long` c-type. """ Culong """ Cwchar_t Equivalent to the native `wchar_t` c-type ([`Int32`](@ref)). """ Cwchar_t """ Cwstring A C-style string composed of the native wide character type [`Cwchar_t`](@ref)s. `Cwstring`s are NUL-terminated. For C-style strings composed of the native character type, see [`Cstring`](@ref). For more information about string interoperability with C, see the [manual](@ref man-bits-types). """ Cwstring """ Cstring A C-style string composed of the native character type [`Cchar`](@ref)s. `Cstring`s are NUL-terminated. For C-style strings composed of the native wide character type, see [`Cwstring`](@ref). For more information about string interoperability with C, see the [manual](@ref man-bits-types). """ Cstring @static if ccall(:jl_get_UNAME, Any, ()) !== :NT const sizeof_mode_t = ccall(:jl_sizeof_mode_t, Cint, ()) if sizeof_mode_t == 2 const Cmode_t = Int16 elseif sizeof_mode_t == 4 const Cmode_t = Int32 elseif sizeof_mode_t == 8 const Cmode_t = Int64 end end # construction from pointers Cstring(p::Union{Ptr{Int8},Ptr{UInt8},Ptr{Cvoid}}) = bitcast(Cstring, p) Cwstring(p::Union{Ptr{Cwchar_t},Ptr{Cvoid}}) = bitcast(Cwstring, p) Ptr{T}(p::Cstring) where {T<:Union{Int8,UInt8,Cvoid}} = bitcast(Ptr{T}, p) Ptr{T}(p::Cwstring) where {T<:Union{Cwchar_t,Cvoid}} = bitcast(Ptr{Cwchar_t}, p) convert(::Type{Cstring}, p::Union{Ptr{Int8},Ptr{UInt8},Ptr{Cvoid}}) = Cstring(p) convert(::Type{Cwstring}, p::Union{Ptr{Cwchar_t},Ptr{Cvoid}}) = Cwstring(p) convert(::Type{Ptr{T}}, p::Cstring) where {T<:Union{Int8,UInt8,Cvoid}} = Ptr{T}(p) convert(::Type{Ptr{T}}, p::Cwstring) where {T<:Union{Cwchar_t,Cvoid}} = Ptr{T}(p) """ pointer(array [, index]) Get the native address of an array or string, optionally at a given location `index`. This function is "unsafe". Be careful to ensure that a Julia reference to `array` exists as long as this pointer will be used. The [`GC.@preserve`](@ref) macro should be used to protect the `array` argument from garbage collection within a given block of code. Calling [`Ref(array[, index])`](@ref Ref) is generally preferable to this function as it guarantees validity. """ function pointer end pointer(p::Cstring) = convert(Ptr{Cchar}, p) pointer(p::Cwstring) = convert(Ptr{Cwchar_t}, p) # comparisons against pointers (mainly to support `cstr==C_NULL`) ==(x::Union{Cstring,Cwstring}, y::Ptr) = pointer(x) == y ==(x::Ptr, y::Union{Cstring,Cwstring}) = x == pointer(y) unsafe_string(s::Cstring) = unsafe_string(convert(Ptr{UInt8}, s)) # convert strings to String etc. to pass as pointers cconvert(::Type{Cstring}, s::String) = s cconvert(::Type{Cstring}, s::AbstractString) = cconvert(Cstring, String(s)::String) function cconvert(::Type{Cwstring}, s::AbstractString) v = transcode(Cwchar_t, String(s)) !isempty(v) && v[end] == 0 || push!(v, 0) return v end eltype(::Type{Cstring}) = Cchar eltype(::Type{Cwstring}) = Cwchar_t containsnul(p::Ptr, len) = C_NULL != ccall(:memchr, Ptr{Cchar}, (Ptr{Cchar}, Cint, Csize_t), p, 0, len) containsnul(s::String) = containsnul(unsafe_convert(Ptr{Cchar}, s), sizeof(s)) containsnul(s::AbstractString) = '\0' in s function unsafe_convert(::Type{Cstring}, s::Union{String,AbstractVector{UInt8}}) p = unsafe_convert(Ptr{Cchar}, s) containsnul(p, sizeof(s)) && throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) return Cstring(p) end function unsafe_convert(::Type{Cwstring}, v::Vector{Cwchar_t}) for i = 1:length(v)-1 v[i] == 0 && throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(v))")) end v[end] == 0 || throw(ArgumentError("C string data must be NUL terminated: $(repr(v))")) p = unsafe_convert(Ptr{Cwchar_t}, v) return Cwstring(p) end # symbols are guaranteed not to contain embedded NUL cconvert(::Type{Cstring}, s::Symbol) = s unsafe_convert(::Type{Cstring}, s::Symbol) = Cstring(unsafe_convert(Ptr{Cchar}, s)) @static if ccall(:jl_get_UNAME, Any, ()) === :NT """ Base.cwstring(s) Converts a string `s` to a NUL-terminated `Vector{Cwchar_t}`, suitable for passing to C functions expecting a `Ptr{Cwchar_t}`. The main advantage of using this over the implicit conversion provided by [`Cwstring`](@ref) is if the function is called multiple times with the same argument. This is only available on Windows. """ function cwstring(s::AbstractString) bytes = codeunits(String(s)) 0 in bytes && throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) return push!(transcode(UInt16, bytes), 0) end end # transcoding between data in UTF-8 and UTF-16 for Windows APIs, # and also UTF-32 for APIs using Cwchar_t on other platforms. """ transcode(T, src) Convert string data between Unicode encodings. `src` is either a `String` or a `Vector{UIntXX}` of UTF-XX code units, where `XX` is 8, 16, or 32. `T` indicates the encoding of the return value: `String` to return a (UTF-8 encoded) `String` or `UIntXX` to return a `Vector{UIntXX}` of UTF-`XX` data. (The alias [`Cwchar_t`](@ref) can also be used as the integer type, for converting `wchar_t*` strings used by external C libraries.) The `transcode` function succeeds as long as the input data can be reasonably represented in the target encoding; it always succeeds for conversions between UTF-XX encodings, even for invalid Unicode data. Only conversion to/from UTF-8 is currently supported. # Examples ```jldoctest julia> str = "ฮฑฮฒฮณ" "ฮฑฮฒฮณ" julia> transcode(UInt16, str) 3-element Vector{UInt16}: 0x03b1 0x03b2 0x03b3 julia> transcode(String, transcode(UInt16, str)) "ฮฑฮฒฮณ" ``` """ function transcode end transcode(::Type{T}, src::AbstractVector{T}) where {T<:Union{UInt8,UInt16,UInt32,Int32}} = src transcode(::Type{T}, src::String) where {T<:Union{Int32,UInt32}} = T[T(c) for c in src] transcode(::Type{T}, src::AbstractVector{UInt8}) where {T<:Union{Int32,UInt32}} = transcode(T, String(Vector(src))) transcode(::Type{T}, src::CodeUnits{UInt8,String}) where {T<:Union{Int32,UInt32}} = transcode(T, String(src)) function transcode(::Type{UInt8}, src::Vector{<:Union{Int32,UInt32}}) buf = IOBuffer() for c in src print(buf, Char(c)) end take!(buf) end transcode(::Type{String}, src::String) = src transcode(T, src::String) = transcode(T, codeunits(src)) transcode(::Type{String}, src) = String(transcode(UInt8, src)) function transcode(::Type{UInt16}, src::AbstractVector{UInt8}) require_one_based_indexing(src) dst = UInt16[] i, n = 1, length(src) n > 0 || return dst sizehint!(dst, 2n) a = src[1] while true if i < n && -64 <= a % Int8 <= -12 # multi-byte character b = src[i += 1] if -64 <= (b % Int8) || a == 0xf4 && 0x8f < b # invalid UTF-8 (non-continuation or too-high code point) push!(dst, a) a = b; continue elseif a < 0xe0 # 2-byte UTF-8 push!(dst, xor(0x3080, UInt16(a) << 6, b)) elseif i < n # 3/4-byte character c = src[i += 1] if -64 <= (c % Int8) # invalid UTF-8 (non-continuation) push!(dst, a, b) a = c; continue elseif a < 0xf0 # 3-byte UTF-8 push!(dst, xor(0x2080, UInt16(a) << 12, UInt16(b) << 6, c)) elseif i < n d = src[i += 1] if -64 <= (d % Int8) # invalid UTF-8 (non-continuation) push!(dst, a, b, c) a = d; continue elseif a == 0xf0 && b < 0x90 # overlong encoding push!(dst, xor(0x2080, UInt16(b) << 12, UInt16(c) << 6, d)) else # 4-byte UTF-8 push!(dst, 0xe5b8 + (UInt16(a) << 8) + (UInt16(b) << 2) + (c >> 4), xor(0xdc80, UInt16(c & 0xf) << 6, d)) end else # too short push!(dst, a, b, c) break end else # too short push!(dst, a, b) break end else # ASCII or invalid UTF-8 (continuation byte or too-high code point) push!(dst, a) end i < n || break a = src[i += 1] end return dst end function transcode(::Type{UInt8}, src::AbstractVector{UInt16}) require_one_based_indexing(src) n = length(src) n == 0 && return UInt8[] # Precompute m = sizeof(dst). This involves annoying duplication # of the loop over the src array. However, this is not just an # optimization: it is problematic for security reasons to grow # dst dynamically, because Base.winprompt uses this function to # convert passwords to UTF-8 and we don't want to make unintentional # copies of the password data. a = src[1] i, m = 1, 0 while true if a < 0x80 m += 1 elseif a < 0x800 # 2-byte UTF-8 m += 2 elseif a & 0xfc00 == 0xd800 && i < length(src) b = src[i += 1] if (b & 0xfc00) == 0xdc00 # 2-unit UTF-16 sequence => 4-byte UTF-8 m += 4 else m += 3 a = b; continue end else # 1-unit high UTF-16 or unpaired high surrogate # either way, encode as 3-byte UTF-8 code point m += 3 end i < n || break a = src[i += 1] end dst = StringVector(m) a = src[1] i, j = 1, 0 while true if a < 0x80 # ASCII dst[j += 1] = a % UInt8 elseif a < 0x800 # 2-byte UTF-8 dst[j += 1] = 0xc0 | ((a >> 6) % UInt8) dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) elseif a & 0xfc00 == 0xd800 && i < n b = src[i += 1] if (b & 0xfc00) == 0xdc00 # 2-unit UTF-16 sequence => 4-byte UTF-8 a += 0x2840 dst[j += 1] = 0xf0 | ((a >> 8) % UInt8) dst[j += 1] = 0x80 | ((a % UInt8) >> 2) dst[j += 1] = xor(0xf0, ((a % UInt8) << 4) & 0x3f, (b >> 6) % UInt8) dst[j += 1] = 0x80 | ((b % UInt8) & 0x3f) else dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) a = b; continue end else # 1-unit high UTF-16 or unpaired high surrogate # either way, encode as 3-byte UTF-8 code point dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) end i < n || break a = src[i += 1] end return dst end function unsafe_string(p::Ptr{T}, length::Integer) where {T<:Union{UInt16,UInt32,Cwchar_t}} transcode(String, unsafe_wrap(Array, p, length; own=false)) end function unsafe_string(cw::Cwstring) p = convert(Ptr{Cwchar_t}, cw) n = 1 while unsafe_load(p, n) != 0 n += 1 end return unsafe_string(p, n - 1) end # deferring (or un-deferring) ctrl-c handler for external C code that # is not interrupt safe (see also issue #2622). The sigatomic_begin/end # functions should always be called in matched pairs, ideally via: # disable_sigint() do .. end # reennable_sigint is provided so that immediate ctrl-c handling is # re-enabled within a sigatomic region, e.g. inside a Julia callback function # within a long-running C routine. sigatomic_begin() = ccall(:jl_sigatomic_begin, Cvoid, ()) sigatomic_end() = ccall(:jl_sigatomic_end, Cvoid, ()) """ disable_sigint(f::Function) Disable Ctrl-C handler during execution of a function on the current task, for calling external code that may call julia code that is not interrupt safe. Intended to be called using `do` block syntax as follows: disable_sigint() do # interrupt-unsafe code ... end This is not needed on worker threads (`Threads.threadid() != 1`) since the `InterruptException` will only be delivered to the master thread. External functions that do not call julia code or julia runtime automatically disable sigint during their execution. """ function disable_sigint(f::Function) sigatomic_begin() res = f() # Exception unwind sigatomic automatically sigatomic_end() res end """ reenable_sigint(f::Function) Re-enable Ctrl-C handler during execution of a function. Temporarily reverses the effect of [`disable_sigint`](@ref). """ function reenable_sigint(f::Function) sigatomic_end() res = f() # Exception unwind sigatomic automatically sigatomic_begin() res end """ exit_on_sigint(on::Bool) Set `exit_on_sigint` flag of the julia runtime. If `false`, Ctrl-C (SIGINT) is capturable as [`InterruptException`](@ref) in `try` block. This is the default behavior in REPL, any code run via `-e` and `-E` and in Julia script run with `-i` option. If `true`, `InterruptException` is not thrown by Ctrl-C. Running code upon such event requires [`atexit`](@ref). This is the default behavior in Julia script run without `-i` option. !!! compat "Julia 1.5" Function `exit_on_sigint` requires at least Julia 1.5. """ function exit_on_sigint(on::Bool) ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on) end function _ccallable(rt::Type, sigt::Type) ccall(:jl_extern_c, Cvoid, (Any, Any), rt, sigt) end function expand_ccallable(rt, def) if isa(def,Expr) && (def.head === :(=) || def.head === :function) sig = def.args[1] if sig.head === :(::) if rt === nothing rt = sig.args[2] end sig = sig.args[1] end if rt === nothing error("@ccallable requires a return type") end if sig.head === :call f = sig.args[1] if isa(f,Expr) && f.head === :(::) f = f.args[end] else f = :(typeof($f)) end at = map(sig.args[2:end]) do a if isa(a,Expr) && a.head === :(::) a.args[end] else :Any end end return quote $(esc(def)) _ccallable($(esc(rt)), $(Expr(:curly, :Tuple, esc(f), map(esc, at)...))) end end end error("expected method definition in @ccallable") end """ @ccallable(def) Make the annotated function be callable from C using its name. This can, for example, be used to expose functionality as a C-API when creating a custom Julia sysimage. """ macro ccallable(def) expand_ccallable(nothing, def) end macro ccallable(rt, def) expand_ccallable(rt, def) end # @ccall implementation """ ccall_macro_parse(expression) `ccall_macro_parse` is an implementation detail of `@ccall`. It takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)` returns: a tuple of `(function_name, return_type, arg_types, args)` The above input outputs this: (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value]) """ function ccall_macro_parse(expr::Expr) # setup and check for errors if !Meta.isexpr(expr, :(::)) throw(ArgumentError("@ccall needs a function signature with a return type")) end rettype = expr.args[2] call = expr.args[1] if !Meta.isexpr(call, :call) throw(ArgumentError("@ccall has to take a function call")) end # get the function symbols func = let f = call.args[1] if Meta.isexpr(f, :.) :(($(f.args[2]), $(f.args[1]))) elseif Meta.isexpr(f, :$) f elseif f isa Symbol QuoteNode(f) else throw(ArgumentError("@ccall function name must be a symbol, a `.` node (e.g. `libc.printf`) or an interpolated function pointer (with `\$`)")) end end # detect varargs varargs = nothing argstart = 2 callargs = call.args if length(callargs) >= 2 && Meta.isexpr(callargs[2], :parameters) argstart = 3 varargs = callargs[2].args end # collect args and types args = [] types = [] function pusharg!(arg) if !Meta.isexpr(arg, :(::)) throw(ArgumentError("args in @ccall need type annotations. '$arg' doesn't have one.")) end push!(args, arg.args[1]) push!(types, arg.args[2]) end for i in argstart:length(callargs) pusharg!(callargs[i]) end # add any varargs if necessary nreq = 0 if !isnothing(varargs) if length(args) == 0 throw(ArgumentError("C ABI prohibits vararg without one required argument")) end nreq = length(args) for a in varargs pusharg!(a) end end return func, rettype, types, args, nreq end function ccall_macro_lower(convention, func, rettype, types, args, nreq) statements = [] # if interpolation was used, ensure the value is a function pointer at runtime. if Meta.isexpr(func, :$) push!(statements, Expr(:(=), :func, esc(func.args[1]))) name = QuoteNode(func.args[1]) func = :func check = quote if !isa(func, Ptr{Cvoid}) name = $name throw(ArgumentError("interpolated function `$name` was not a Ptr{Cvoid}, but $(typeof(func))")) end end push!(statements, check) else func = esc(func) end return Expr(:block, statements..., Expr(:call, :ccall, func, Expr(:cconv, convention, nreq), esc(rettype), Expr(:tuple, map(esc, types)...), map(esc, args)...)) end """ @ccall library.function_name(argvalue1::argtype1, ...)::returntype @ccall function_name(argvalue1::argtype1, ...)::returntype @ccall \$function_pointer(argvalue1::argtype1, ...)::returntype Call a function in a C-exported shared library, specified by `library.function_name`, where `library` is a string constant or literal. The library may be omitted, in which case the `function_name` is resolved in the current process. Alternatively, `@ccall` may also be used to call a function pointer `\$function_pointer`, such as one returned by `dlsym`. Each `argvalue` to `@ccall` is converted to the corresponding `argtype`, by automatic insertion of calls to `unsafe_convert(argtype, cconvert(argtype, argvalue))`. (See also the documentation for [`unsafe_convert`](@ref Base.unsafe_convert) and [`cconvert`](@ref Base.cconvert) for further details.) In most cases, this simply results in a call to `convert(argtype, argvalue)`. # Examples @ccall strlen(s::Cstring)::Csize_t This calls the C standard library function: size_t strlen(char *) with a Julia variable named `s`. See also `ccall`. Varargs are supported with the following convention: @ccall printf("%s = %d"::Cstring ; "foo"::Cstring, foo::Cint)::Cint The semicolon is used to separate required arguments (of which there must be at least one) from variadic arguments. Example using an external library: # C signature of g_uri_escape_string: # char *g_uri_escape_string(const char *unescaped, const char *reserved_chars_allowed, gboolean allow_utf8); const glib = "libglib-2.0" @ccall glib.g_uri_escape_string(my_uri::Cstring, ":/"::Cstring, true::Cint)::Cstring The string literal could also be used directly before the function name, if desired `"libglib-2.0".g_uri_escape_string(...` """ macro ccall(expr) return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...) end macro ccall_effects(effects::UInt8, expr) return ccall_macro_lower((:ccall, effects), ccall_macro_parse(expr)...) end I/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/io.jlะข# This file is a part of Julia. License is MIT: https://julialang.org/license # Generic IO stubs -- all subtypes should implement these (if meaningful) """ EOFError() No more data was available to read from a file or stream. """ struct EOFError <: Exception end """ SystemError(prefix::AbstractString, [errno::Int32]) A system call failed with an error code (in the `errno` global variable). """ struct SystemError <: Exception prefix::String errnum::Int32 extrainfo SystemError(p::AbstractString, e::Integer, extrainfo) = new(p, e, extrainfo) SystemError(p::AbstractString, e::Integer) = new(p, e, nothing) SystemError(p::AbstractString) = new(p, Libc.errno(), nothing) end lock(::IO) = nothing unlock(::IO) = nothing reseteof(x::IO) = nothing const SZ_UNBUFFERED_IO = 65536 buffer_writes(x::IO, bufsize=SZ_UNBUFFERED_IO) = x """ isopen(object) -> Bool Determine whether an object - such as a stream or timer -- is not yet closed. Once an object is closed, it will never produce a new event. However, since a closed stream may still have data to read in its buffer, use [`eof`](@ref) to check for the ability to read data. Use the `FileWatching` package to be notified when a stream might be writable or readable. # Examples ```jldoctest julia> io = open("my_file.txt", "w+"); julia> isopen(io) true julia> close(io) julia> isopen(io) false ``` """ function isopen end """ close(stream) Close an I/O stream. Performs a [`flush`](@ref) first. """ function close end """ closewrite(stream) Shutdown the write half of a full-duplex I/O stream. Performs a [`flush`](@ref) first. Notify the other end that no more data will be written to the underlying file. This is not supported by all IO types. # Examples ```jldoctest julia> io = Base.BufferStream(); # this never blocks, so we can read and write on the same Task julia> write(io, "request"); julia> # calling `read(io)` here would block forever julia> closewrite(io); julia> read(io, String) "request" ``` """ function closewrite end """ flush(stream) Commit all currently buffered writes to the given stream. """ function flush end """ bytesavailable(io) Return the number of bytes available for reading before a read from this stream or buffer will block. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization"); julia> bytesavailable(io) 34 ``` """ function bytesavailable end """ readavailable(stream) Read available buffered data from a stream. Actual I/O is performed only if no data has already been buffered. The result is a `Vector{UInt8}`. !!! warning The amount of data returned is implementation-dependent; for example it can depend on the internal choice of buffer size. Other functions such as [`read`](@ref) should generally be used instead. """ function readavailable end """ isreadable(io) -> Bool Return `false` if the specified IO object is not readable. # Examples ```jldoctest julia> open("myfile.txt", "w") do io print(io, "Hello world!"); isreadable(io) end false julia> open("myfile.txt", "r") do io isreadable(io) end true julia> rm("myfile.txt") ``` """ isreadable(io::IO) = isopen(io) """ iswritable(io) -> Bool Return `false` if the specified IO object is not writable. # Examples ```jldoctest julia> open("myfile.txt", "w") do io print(io, "Hello world!"); iswritable(io) end true julia> open("myfile.txt", "r") do io iswritable(io) end false julia> rm("myfile.txt") ``` """ iswritable(io::IO) = isopen(io) """ eof(stream) -> Bool Test whether an I/O stream is at end-of-file. If the stream is not yet exhausted, this function will block to wait for more data if necessary, and then return `false`. Therefore it is always safe to read one byte after seeing `eof` return `false`. `eof` will return `false` as long as buffered data is still available, even if the remote end of a connection is closed. # Examples ```jldoctest julia> b = IOBuffer("my buffer"); julia> eof(b) false julia> seekend(b); julia> eof(b) true ``` """ function eof end function copy end function wait_readnb end function wait_close end """ read(io::IO, T) Read a single value of type `T` from `io`, in canonical binary representation. Note that Julia does not convert the endianness for you. Use [`ntoh`](@ref) or [`ltoh`](@ref) for this purpose. read(io::IO, String) Read the entirety of `io`, as a `String` (see also [`readchomp`](@ref)). # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization"); julia> read(io, Char) 'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase) julia> io = IOBuffer("JuliaLang is a GitHub organization"); julia> read(io, String) "JuliaLang is a GitHub organization" ``` """ read(stream, t) read(stream, ::Type{Union{}}, slurp...; kwargs...) = error("cannot read a value of type Union{}") """ write(io::IO, x) Write the canonical binary representation of a value to the given I/O stream or file. Return the number of bytes written into the stream. See also [`print`](@ref) to write a text representation (with an encoding that may depend upon `io`). The endianness of the written value depends on the endianness of the host system. Convert to/from a fixed endianness when writing/reading (e.g. using [`htol`](@ref) and [`ltoh`](@ref)) to get results that are consistent across platforms. You can write multiple values with the same `write` call. i.e. the following are equivalent: write(io, x, y...) write(io, x) + write(io, y...) # Examples Consistent serialization: ```jldoctest julia> fname = tempname(); # random temporary filename julia> open(fname,"w") do f # Make sure we write 64bit integer in little-endian byte order write(f,htol(Int64(42))) end 8 julia> open(fname,"r") do f # Convert back to host byte order and host integer type Int(ltoh(read(f,Int64))) end 42 ``` Merging write calls: ```jldoctest julia> io = IOBuffer(); julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") 56 julia> String(take!(io)) "JuliaLang is a GitHub organization. It has many members." julia> write(io, "Sometimes those members") + write(io, " write documentation.") 44 julia> String(take!(io)) "Sometimes those members write documentation." ``` User-defined plain-data types without `write` methods can be written when wrapped in a `Ref`: ```jldoctest julia> struct MyStruct; x::Float64; end julia> io = IOBuffer() IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1) julia> write(io, Ref(MyStruct(42.0))) 8 julia> seekstart(io); read!(io, Ref(MyStruct(NaN))) Base.RefValue{MyStruct}(MyStruct(42.0)) ``` """ function write end read(s::IO, ::Type{UInt8}) = error(typeof(s)," does not support byte I/O") write(s::IO, x::UInt8) = error(typeof(s)," does not support byte I/O") """ unsafe_write(io::IO, ref, nbytes::UInt) Copy `nbytes` from `ref` (converted to a pointer) into the `IO` object. It is recommended that subtypes `T<:IO` override the following method signature to provide more efficient implementations: `unsafe_write(s::T, p::Ptr{UInt8}, n::UInt)` """ function unsafe_write(s::IO, p::Ptr{UInt8}, n::UInt) written::Int = 0 for i = 1:n written += write(s, unsafe_load(p, i)) end return written end """ unsafe_read(io::IO, ref, nbytes::UInt) Copy `nbytes` from the `IO` stream object into `ref` (converted to a pointer). It is recommended that subtypes `T<:IO` override the following method signature to provide more efficient implementations: `unsafe_read(s::T, p::Ptr{UInt8}, n::UInt)` """ function unsafe_read(s::IO, p::Ptr{UInt8}, n::UInt) for i = 1:n unsafe_store!(p, read(s, UInt8)::UInt8, i) end nothing end function peek(s::IO, ::Type{T}) where T mark(s) try read(s, T)::T finally reset(s) end end peek(s) = peek(s, UInt8)::UInt8 # Generic `open` methods """ open_flags(; keywords...) -> NamedTuple Compute the `read`, `write`, `create`, `truncate`, `append` flag value for a given set of keyword arguments to [`open`](@ref) a [`NamedTuple`](@ref). """ function open_flags(; read :: Union{Bool,Nothing} = nothing, write :: Union{Bool,Nothing} = nothing, create :: Union{Bool,Nothing} = nothing, truncate :: Union{Bool,Nothing} = nothing, append :: Union{Bool,Nothing} = nothing, ) if write === true && read !== true && append !== true create === nothing && (create = true) truncate === nothing && (truncate = true) end if truncate === true || append === true write === nothing && (write = true) create === nothing && (create = true) end write === nothing && (write = false) read === nothing && (read = !write) create === nothing && (create = false) truncate === nothing && (truncate = false) append === nothing && (append = false) return ( read = read, write = write, create = create, truncate = truncate, append = append, ) end """ open(f::Function, args...; kwargs...) Apply the function `f` to the result of `open(args...; kwargs...)` and close the resulting file descriptor upon completion. # Examples ```jldoctest julia> write("myfile.txt", "Hello world!"); julia> open(io->read(io, String), "myfile.txt") "Hello world!" julia> rm("myfile.txt") ``` """ function open(f::Function, args...; kwargs...) io = open(args...; kwargs...) try f(io) finally close(io) end end """ AbstractPipe `AbstractPipe` is the abstract supertype for IO pipes that provide for communication between processes. If `pipe isa AbstractPipe`, it must obey the following interface: - `pipe.in` or `pipe.in_stream`, if present, must be of type `IO` and be used to provide input to the pipe - `pipe.out` or `pipe.out_stream`, if present, must be of type `IO` and be used for output from the pipe - `pipe.err` or `pipe.err_stream`, if present, must be of type `IO` and be used for writing errors from the pipe """ abstract type AbstractPipe <: IO end function getproperty(pipe::AbstractPipe, name::Symbol) if name === :in || name === :in_stream || name === :out || name === :out_stream || name === :err || name === :err_stream return getfield(pipe, name)::IO end return getfield(pipe, name) end function pipe_reader end function pipe_writer end for f in (:flush, :closewrite, :iswritable) @eval $(f)(io::AbstractPipe) = $(f)(pipe_writer(io)::IO) end write(io::AbstractPipe, byte::UInt8) = write(pipe_writer(io)::IO, byte) write(to::IO, from::AbstractPipe) = write(to, pipe_reader(from)) unsafe_write(io::AbstractPipe, p::Ptr{UInt8}, nb::UInt) = unsafe_write(pipe_writer(io)::IO, p, nb)::Union{Int,UInt} buffer_writes(io::AbstractPipe, args...) = buffer_writes(pipe_writer(io)::IO, args...) for f in ( # peek/mark interface :mark, :unmark, :reset, :ismarked, # Simple reader functions :read, :readavailable, :bytesavailable, :reseteof, :isreadable) @eval $(f)(io::AbstractPipe) = $(f)(pipe_reader(io)::IO) end read(io::AbstractPipe, byte::Type{UInt8}) = read(pipe_reader(io)::IO, byte)::UInt8 unsafe_read(io::AbstractPipe, p::Ptr{UInt8}, nb::UInt) = unsafe_read(pipe_reader(io)::IO, p, nb) readuntil(io::AbstractPipe, arg::UInt8; kw...) = readuntil(pipe_reader(io)::IO, arg; kw...) readuntil(io::AbstractPipe, arg::AbstractChar; kw...) = readuntil(pipe_reader(io)::IO, arg; kw...) readuntil(io::AbstractPipe, arg::AbstractString; kw...) = readuntil(pipe_reader(io)::IO, arg; kw...) readuntil(io::AbstractPipe, arg::AbstractVector; kw...) = readuntil(pipe_reader(io)::IO, arg; kw...) readuntil_vector!(io::AbstractPipe, target::AbstractVector, keep::Bool, out) = readuntil_vector!(pipe_reader(io)::IO, target, keep, out) readbytes!(io::AbstractPipe, target::AbstractVector{UInt8}, n=length(target)) = readbytes!(pipe_reader(io)::IO, target, n) peek(io::AbstractPipe, ::Type{T}) where {T} = peek(pipe_reader(io)::IO, T)::T wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(pipe_reader(io)::IO, nb) eof(io::AbstractPipe) = eof(pipe_reader(io)::IO)::Bool isopen(io::AbstractPipe) = isopen(pipe_writer(io)::IO) || isopen(pipe_reader(io)::IO) close(io::AbstractPipe) = (close(pipe_writer(io)::IO); close(pipe_reader(io)::IO)) wait_close(io::AbstractPipe) = (wait_close(pipe_writer(io)::IO); wait_close(pipe_reader(io)::IO)) # Exception-safe wrappers (io = open(); try f(io) finally close(io)) """ write(filename::AbstractString, content) Write the canonical binary representation of `content` to a file, which will be created if it does not exist yet or overwritten if it does exist. Return the number of bytes written into the file. """ write(filename::AbstractString, a1, args...) = open(io->write(io, a1, args...), convert(String, filename)::String, "w") """ read(filename::AbstractString) Read the entire contents of a file as a `Vector{UInt8}`. read(filename::AbstractString, String) Read the entire contents of a file as a string. read(filename::AbstractString, args...) Open a file and read its contents. `args` is passed to `read`: this is equivalent to `open(io->read(io, args...), filename)`. """ read(filename::AbstractString, args...) = open(io->read(io, args...), convert(String, filename)::String) read(filename::AbstractString, ::Type{T}) where {T} = open(io->read(io, T), convert(String, filename)::String) """ read!(stream::IO, array::AbstractArray) read!(filename::AbstractString, array::AbstractArray) Read binary data from an I/O stream or file, filling in `array`. """ function read! end read!(filename::AbstractString, a) = open(io->read!(io, a), convert(String, filename)::String) """ readuntil(stream::IO, delim; keep::Bool = false) readuntil(filename::AbstractString, delim; keep::Bool = false) Read a string from an I/O stream or a file, up to the given delimiter. The delimiter can be a `UInt8`, `AbstractChar`, string, or vector. Keyword argument `keep` controls whether the delimiter is included in the result. The text is assumed to be encoded in UTF-8. # Examples ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); julia> readuntil("my_file.txt", 'L') "Julia" julia> readuntil("my_file.txt", '.', keep = true) "JuliaLang is a GitHub organization." julia> rm("my_file.txt") ``` """ readuntil(filename::AbstractString, args...; kw...) = open(io->readuntil(io, args...; kw...), convert(String, filename)::String) """ readline(io::IO=stdin; keep::Bool=false) readline(filename::AbstractString; keep::Bool=false) Read a single line of text from the given I/O stream or file (defaults to `stdin`). When reading from a file, the text is assumed to be encoded in UTF-8. Lines in the input end with `'\\n'` or `"\\r\\n"` or the end of an input stream. When `keep` is false (as it is by default), these trailing newline characters are removed from the line before it is returned. When `keep` is true, they are returned as part of the line. # Examples ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); julia> readline("my_file.txt") "JuliaLang is a GitHub organization." julia> readline("my_file.txt", keep=true) "JuliaLang is a GitHub organization.\\n" julia> rm("my_file.txt") ``` ```julia-repl julia> print("Enter your name: ") Enter your name: julia> your_name = readline() Logan "Logan" ``` """ function readline(filename::AbstractString; keep::Bool=false) open(filename) do f readline(f, keep=keep) end end function readline(s::IO=stdin; keep::Bool=false) line = readuntil(s, 0x0a, keep=true)::Vector{UInt8} i = length(line) if keep || i == 0 || line[i] != 0x0a return String(line) elseif i < 2 || line[i-1] != 0x0d return String(resize!(line,i-1)) else return String(resize!(line,i-2)) end end """ readlines(io::IO=stdin; keep::Bool=false) readlines(filename::AbstractString; keep::Bool=false) Read all lines of an I/O stream or a file as a vector of strings. Behavior is equivalent to saving the result of reading [`readline`](@ref) repeatedly with the same arguments and saving the resulting lines as a vector of strings. See also [`eachline`](@ref) to iterate over the lines without reading them all at once. # Examples ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); julia> readlines("my_file.txt") 2-element Vector{String}: "JuliaLang is a GitHub organization." "It has many members." julia> readlines("my_file.txt", keep=true) 2-element Vector{String}: "JuliaLang is a GitHub organization.\\n" "It has many members.\\n" julia> rm("my_file.txt") ``` """ function readlines(filename::AbstractString; kw...) open(filename) do f readlines(f; kw...) end end readlines(s=stdin; kw...) = collect(eachline(s; kw...)) ## byte-order mark, ntoh & hton ## let a = UInt32[0x01020304] endian_bom = GC.@preserve a unsafe_load(convert(Ptr{UInt8}, pointer(a))) global ntoh, hton, ltoh, htol if endian_bom == 0x01 ntoh(x) = x hton(x) = x ltoh(x) = bswap(x) htol(x) = bswap(x) const global ENDIAN_BOM = 0x01020304 elseif endian_bom == 0x04 ntoh(x) = bswap(x) hton(x) = bswap(x) ltoh(x) = x htol(x) = x const global ENDIAN_BOM = 0x04030201 else error("seriously? what is this machine?") end end """ ENDIAN_BOM The 32-bit byte-order-mark indicates the native byte order of the host machine. Little-endian machines will contain the value `0x04030201`. Big-endian machines will contain the value `0x01020304`. """ ENDIAN_BOM """ ntoh(x) Convert the endianness of a value from Network byte order (big-endian) to that used by the Host. """ ntoh(x) """ hton(x) Convert the endianness of a value from that used by the Host to Network byte order (big-endian). """ hton(x) """ ltoh(x) Convert the endianness of a value from Little-endian to that used by the Host. """ ltoh(x) """ htol(x) Convert the endianness of a value from that used by the Host to Little-endian. """ htol(x) """ isreadonly(io) -> Bool Determine whether a stream is read-only. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization"); julia> isreadonly(io) true julia> io = IOBuffer(); julia> isreadonly(io) false ``` """ isreadonly(s) = isreadable(s) && !iswritable(s) ## binary I/O ## write(io::IO, x) = throw(MethodError(write, (io, x))) function write(io::IO, x1, xs...) written::Int = write(io, x1) for x in xs written += write(io, x) end return written end @noinline unsafe_write(s::IO, p::Ref{T}, n::Integer) where {T} = unsafe_write(s, unsafe_convert(Ref{T}, p)::Ptr, n) # mark noinline to ensure ref is gc-rooted somewhere (by the caller) unsafe_write(s::IO, p::Ptr, n::Integer) = unsafe_write(s, convert(Ptr{UInt8}, p), convert(UInt, n)) write(s::IO, x::Ref{T}) where {T} = unsafe_write(s, x, Core.sizeof(T)) write(s::IO, x::Int8) = write(s, reinterpret(UInt8, x)) function write(s::IO, x::Union{Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128,Float16,Float32,Float64}) return write(s, Ref(x)) end write(s::IO, x::Bool) = write(s, UInt8(x)) write(to::IO, p::Ptr) = write(to, convert(UInt, p)) function write(s::IO, A::AbstractArray) if !isbitstype(eltype(A)) error("`write` is not supported on non-isbits arrays") end nb = 0 for a in A nb += write(s, a) end return nb end function write(s::IO, a::Array) if isbitstype(eltype(a)) return GC.@preserve a unsafe_write(s, pointer(a), sizeof(a)) else error("`write` is not supported on non-isbits arrays") end end function write(s::IO, a::SubArray{T,N,<:Array}) where {T,N} if !isbitstype(T) || !isa(a, StridedArray) return invoke(write, Tuple{IO, AbstractArray}, s, a) end elsz = elsize(a) colsz = size(a,1) * elsz GC.@preserve a if stride(a,1) != 1 for idxs in CartesianIndices(size(a)) unsafe_write(s, pointer(a, idxs), elsz) end return elsz * length(a) elseif N <= 1 return unsafe_write(s, pointer(a, 1), colsz) else for colstart in CartesianIndices((1, size(a)[2:end]...)) unsafe_write(s, pointer(a, colstart), colsz) end return colsz * trailingsize(a,2) end end function write(io::IO, c::Char) u = bswap(reinterpret(UInt32, c)) n = 1 while true write(io, u % UInt8) (u >>= 8) == 0 && return n n += 1 end end # write(io, ::AbstractChar) is not defined: implementations # must provide their own encoding-specific method. function write(io::IO, s::Symbol) pname = unsafe_convert(Ptr{UInt8}, s) return unsafe_write(io, pname, ccall(:strlen, Csize_t, (Cstring,), pname)) end function write(to::IO, from::IO) n = 0 while !eof(from) n += write(to, readavailable(from)) end return n end @noinline unsafe_read(s::IO, p::Ref{T}, n::Integer) where {T} = unsafe_read(s, unsafe_convert(Ref{T}, p)::Ptr, n) # mark noinline to ensure ref is gc-rooted somewhere (by the caller) unsafe_read(s::IO, p::Ptr, n::Integer) = unsafe_read(s, convert(Ptr{UInt8}, p), convert(UInt, n)) read!(s::IO, x::Ref{T}) where {T} = (unsafe_read(s, x, Core.sizeof(T)); x) read(s::IO, ::Type{Int8}) = reinterpret(Int8, read(s, UInt8)) function read(s::IO, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) return read!(s, Ref{T}(0))[]::T end read(s::IO, ::Type{Bool}) = (read(s, UInt8) != 0) read(s::IO, ::Type{Ptr{T}}) where {T} = convert(Ptr{T}, read(s, UInt)) function read!(s::IO, a::Array{UInt8}) GC.@preserve a unsafe_read(s, pointer(a), sizeof(a)) return a end function read!(s::IO, a::AbstractArray{T}) where T if isbitstype(T) && (a isa Array || a isa FastContiguousSubArray{T,<:Any,<:Array{T}}) GC.@preserve a unsafe_read(s, pointer(a), sizeof(a)) else for i in eachindex(a) a[i] = read(s, T) end end return a end function read(io::IO, ::Type{Char}) b0 = read(io, UInt8)::UInt8 l = 0x08 * (0x04 - UInt8(leading_ones(b0))) c = UInt32(b0) << 24 if l โ‰ค 0x10 s = 16 while s โ‰ฅ l && !eof(io)::Bool peek(io) & 0xc0 == 0x80 || break b = read(io, UInt8)::UInt8 c |= UInt32(b) << s s -= 8 end end return reinterpret(Char, c) end # read(io, T) is not defined for other AbstractChar: implementations # must provide their own encoding-specific method. # readuntil_string is useful below since it has # an optimized method for s::IOStream readuntil_string(s::IO, delim::UInt8, keep::Bool) = String(readuntil(s, delim, keep=keep))::String function readuntil(s::IO, delim::AbstractChar; keep::Bool=false) if delim โ‰ค '\x7f' return readuntil_string(s, delim % UInt8, keep) end out = IOBuffer() for c in readeach(s, Char) if c == delim keep && write(out, c) break end write(out, c) end return String(take!(out)) end function readuntil(s::IO, delim::T; keep::Bool=false) where T out = (T === UInt8 ? StringVector(0) : Vector{T}()) for c in readeach(s, T) if c == delim keep && push!(out, c) break end push!(out, c) end return out end # requires that indices for target are the integer unit range from firstindex to lastindex # returns whether the delimiter was matched # uses the Knuthโ€“Morrisโ€“Pratt_algorithm, with the first and second cache entries unrolled # For longer targets, the cache improves the big-O efficiency of scanning of sequences # with repeated patterns # Each cache entry tells us which index we should start the search at. # We assume this is unlikely, so we only lazy-initialize as much of the cache as we need to use # When we allocate the cache, we initialize it to 0 (and offset by the first index afterwards). # Suppose target is: # Index: 1245689 # Value: "aฮดcaฮดcx" # We would set the cache to # 0 0 0 1 2 3 4 0 # So after if we mismatch after the second aฮดc sequence, # we can immediately jump back to index 5 (4 + 1). function readuntil_vector!(io::IO, target::AbstractVector{T}, keep::Bool, out) where {T} first = firstindex(target) last = lastindex(target) len = last - first + 1 if len < 1 return true end pos = 0 # array-offset max_pos = 1 # array-offset in cache local cache # will be lazy initialized when needed output! = (isa(out, IO) ? write : push!) for c in readeach(io, T) # Backtrack until the next target character matches what was found while true c1 = target[pos + first] if c == c1 pos += 1 break elseif pos == 0 break elseif pos == 1 if !keep output!(out, target[first]) end pos = 0 else # grow cache to contain up to `pos` if !@isdefined(cache) cache = zeros(Int, len) end while max_pos < pos ci = target[max_pos + first] b = max_pos max_pos += 1 while b != 0 b = cache[b] cb = target[b + first] if ci == cb cache[max_pos] = b + 1 break end end end # read new position from cache pos1 = cache[pos] if !keep # and add the removed prefix from the target to the output # if not always keeping the match for b in 1:(pos - pos1) output!(out, target[b - 1 + first]) end end pos = pos1 end end if keep || pos == 0 output!(out, c) end pos == len && return true end if !keep # failed early without finishing the match, # add the partial match to the output # if not always keeping the match for b in 1:pos output!(out, target[b - 1 + first]) end end return false end function readuntil(io::IO, target::AbstractString; keep::Bool=false) # small-string target optimizations x = Iterators.peel(target) isnothing(x) && return "" c, rest = x if isempty(rest) && c <= '\x7f' return readuntil_string(io, c % UInt8, keep) end # convert String to a utf8-byte-iterator if !(target isa String) && !(target isa SubString{String}) target = String(target) end target = codeunits(target)::AbstractVector return String(readuntil(io, target, keep=keep)) end function readuntil(io::IO, target::AbstractVector{T}; keep::Bool=false) where T out = (T === UInt8 ? StringVector(0) : Vector{T}()) readuntil_vector!(io, target, keep, out) return out end """ readchomp(x) Read the entirety of `x` as a string and remove a single trailing newline if there is one. Equivalent to `chomp(read(x, String))`. # Examples ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); julia> readchomp("my_file.txt") "JuliaLang is a GitHub organization.\\nIt has many members." julia> rm("my_file.txt"); ``` """ readchomp(x) = chomp(read(x, String)) # read up to nb bytes into nb, returning # bytes read """ readbytes!(stream::IO, b::AbstractVector{UInt8}, nb=length(b)) Read at most `nb` bytes from `stream` into `b`, returning the number of bytes read. The size of `b` will be increased if needed (i.e. if `nb` is greater than `length(b)` and enough bytes could be read), but it will never be decreased. """ function readbytes!(s::IO, b::AbstractArray{UInt8}, nb=length(b)) require_one_based_indexing(b) olb = lb = length(b) nr = 0 while nr < nb && !eof(s) a = read(s, UInt8) nr += 1 if nr > lb lb = nr * 2 resize!(b, lb) end b[nr] = a end if lb > olb resize!(b, nr) # shrink to just contain input data if was resized end return nr end """ read(s::IO, nb=typemax(Int)) Read at most `nb` bytes from `s`, returning a `Vector{UInt8}` of the bytes read. """ function read(s::IO, nb::Integer = typemax(Int)) # Let readbytes! grow the array progressively by default # instead of taking of risk of over-allocating b = Vector{UInt8}(undef, nb == typemax(Int) ? 1024 : nb) nr = readbytes!(s, b, nb) return resize!(b, nr) end read(s::IO, ::Type{String}) = String(read(s)::Vector{UInt8}) read(s::IO, T::Type) = error("The IO stream does not support reading objects of type $T.") ## high-level iterator interfaces ## struct EachLine{IOT <: IO} stream::IOT ondone::Function keep::Bool EachLine(stream::IO=stdin; ondone::Function=()->nothing, keep::Bool=false) = new{typeof(stream)}(stream, ondone, keep) end """ eachline(io::IO=stdin; keep::Bool=false) eachline(filename::AbstractString; keep::Bool=false) Create an iterable `EachLine` object that will yield each line from an I/O stream or a file. Iteration calls [`readline`](@ref) on the stream argument repeatedly with `keep` passed through, determining whether trailing end-of-line characters are retained. When called with a file name, the file is opened once at the beginning of iteration and closed at the end. If iteration is interrupted, the file will be closed when the `EachLine` object is garbage collected. To iterate over each line of a `String`, `eachline(IOBuffer(str))` can be used. [`Iterators.reverse`](@ref) can be used on an `EachLine` object to read the lines in reverse order (for files, buffers, and other I/O streams supporting [`seek`](@ref)), and [`first`](@ref) or [`last`](@ref) can be used to extract the initial or final lines, respectively. # Examples ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\n It has many members.\\n"); julia> for line in eachline("my_file.txt") print(line) end JuliaLang is a GitHub organization. It has many members. julia> rm("my_file.txt"); ``` !!! compat "Julia 1.8" Julia 1.8 is required to use `Iterators.reverse` or `last` with `eachline` iterators. """ function eachline(stream::IO=stdin; keep::Bool=false) EachLine(stream, keep=keep)::EachLine end function eachline(filename::AbstractString; keep::Bool=false) s = open(filename) EachLine(s, ondone=()->close(s), keep=keep)::EachLine end function iterate(itr::EachLine, state=nothing) eof(itr.stream) && return (itr.ondone(); nothing) (readline(itr.stream, keep=itr.keep), nothing) end eltype(::Type{<:EachLine}) = String IteratorSize(::Type{<:EachLine}) = SizeUnknown() isdone(itr::EachLine, state...) = eof(itr.stream) # Reverse-order iteration for the EachLine iterator for seekable streams, # which works by reading the stream from the end in 4kiB chunks. function iterate(r::Iterators.Reverse{<:EachLine}) p0 = position(r.itr.stream) seekend(r.itr.stream) # may throw if io is non-seekable p = position(r.itr.stream) # chunks = circular buffer of 4kiB blocks read from end of stream chunks = empty!(Vector{Vector{UInt8}}(undef, 2)) # allocate space for 2 buffers (common case) inewline = jnewline = 0 while p > p0 && inewline == 0 # read chunks until we find a newline or we read whole file chunk = Vector{UInt8}(undef, min(4096, p-p0)) p -= length(chunk) readbytes!(seek(r.itr.stream, p), chunk) pushfirst!(chunks, chunk) inewline = something(findlast(==(UInt8('\n')), chunk), 0) if length(chunks) == 1 && inewline == length(chunks[1]) # found newline at end of file โ€ฆ keep looking jnewline = inewline inewline = something(findprev(==(UInt8('\n')), chunk, inewline-1), 0) end end return iterate(r, (; p0, p, chunks, ichunk=1, inewline, jchunk=length(chunks), jnewline = jnewline == 0 && !isempty(chunks) ? length(chunks[end]) : jnewline)) end function iterate(r::Iterators.Reverse{<:EachLine}, state) function _stripnewline(keep, pos, data) # strip \n or \r\n from data[pos] by decrementing pos if !keep && pos > 0 && data[pos] == UInt8('\n') pos -= 1 pos -= pos > 0 && data[pos] == UInt8('\r') end return pos end # state tuple: p0 = initial file position, p = current position, # chunks = circular array of chunk buffers, # current line is from chunks[ichunk][inewline+1] to chunks[jchunk][jnewline] p0, p, chunks, ichunk, inewline, jchunk, jnewline = state if inewline == 0 # no newline found, remaining line = rest of chunks (if any) isempty(chunks) && return (r.itr.ondone(); nothing) buf = IOBuffer(sizehint = ichunk==jchunk ? jnewline : 4096) while ichunk != jchunk write(buf, chunks[ichunk]) ichunk = ichunk == length(chunks) ? 1 : ichunk + 1 end chunk = chunks[jchunk] write(buf, view(chunk, 1:jnewline)) buf.size = _stripnewline(r.itr.keep, buf.size, buf.data) empty!(chunks) # will cause next iteration to terminate seekend(r.itr.stream) # reposition to end of stream for isdone s = String(take!(buf)) else # extract the string from chunks[ichunk][inewline+1] to chunks[jchunk][jnewline] if ichunk == jchunk # common case: current and previous newline in same chunk chunk = chunks[ichunk] s = String(view(chunk, inewline+1:_stripnewline(r.itr.keep, jnewline, chunk))) else buf = IOBuffer(sizehint=max(128, length(chunks[ichunk])-inewline+jnewline)) write(buf, view(chunks[ichunk], inewline+1:length(chunks[ichunk]))) i = ichunk while true i = i == length(chunks) ? 1 : i + 1 i == jchunk && break write(buf, chunks[i]) end write(buf, view(chunks[jchunk], 1:jnewline)) buf.size = _stripnewline(r.itr.keep, buf.size, buf.data) s = String(take!(buf)) # overwrite obsolete chunks (ichunk+1:jchunk) i = jchunk while i != ichunk chunk = chunks[i] p -= length(resize!(chunk, min(4096, p-p0))) readbytes!(seek(r.itr.stream, p), chunk) i = i == 1 ? length(chunks) : i - 1 end end # find the newline previous to inewline jchunk = ichunk jnewline = inewline while true inewline = something(findprev(==(UInt8('\n')), chunks[ichunk], inewline-1), 0) inewline > 0 && break ichunk = ichunk == 1 ? length(chunks) : ichunk - 1 ichunk == jchunk && break # found nothing โ€” may need to read more chunks inewline = length(chunks[ichunk])+1 # start for next findprev end # read more chunks to look for a newline (should rarely happen) if inewline == 0 && p > p0 ichunk = jchunk + 1 while true chunk = Vector{UInt8}(undef, min(4096, p-p0)) p -= length(chunk) readbytes!(seek(r.itr.stream, p), chunk) insert!(chunks, ichunk, chunk) inewline = something(findlast(==(UInt8('\n')), chunk), 0) (p == p0 || inewline > 0) && break end end end return (s, (; p0, p, chunks, ichunk, inewline, jchunk, jnewline)) end isdone(r::Iterators.Reverse{<:EachLine}, state) = isempty(state.chunks) isdone(r::Iterators.Reverse{<:EachLine}) = isdone(r.itr) # use reverse iteration to get end of EachLines (if possible) last(itr::EachLine) = first(Iterators.reverse(itr)) struct ReadEachIterator{T, IOT <: IO} stream::IOT end """ readeach(io::IO, T) Return an iterable object yielding [`read(io, T)`](@ref). See also [`skipchars`](@ref), [`eachline`](@ref), [`readuntil`](@ref). !!! compat "Julia 1.6" `readeach` requires Julia 1.6 or later. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization.\\n It has many members.\\n"); julia> for c in readeach(io, Char) c == '\\n' && break print(c) end JuliaLang is a GitHub organization. ``` """ readeach(stream::IOT, T::Type) where IOT<:IO = ReadEachIterator{T,IOT}(stream) iterate(itr::ReadEachIterator{T}, state=nothing) where T = eof(itr.stream) ? nothing : (read(itr.stream, T), nothing) eltype(::Type{ReadEachIterator{T}}) where T = T IteratorSize(::Type{<:ReadEachIterator}) = SizeUnknown() isdone(itr::ReadEachIterator, state...) = eof(itr.stream) # IOStream Marking # Note that these functions expect that io.mark exists for # the concrete IO type. This may not be true for IO types # not in base. """ mark(s::IO) Add a mark at the current position of stream `s`. Return the marked position. See also [`unmark`](@ref), [`reset`](@ref), [`ismarked`](@ref). """ function mark(io::IO) io.mark = position(io) end """ unmark(s::IO) Remove a mark from stream `s`. Return `true` if the stream was marked, `false` otherwise. See also [`mark`](@ref), [`reset`](@ref), [`ismarked`](@ref). """ function unmark(io::IO) !ismarked(io) && return false io.mark = -1 return true end """ reset(s::IO) Reset a stream `s` to a previously marked position, and remove the mark. Return the previously marked position. Throw an error if the stream is not marked. See also [`mark`](@ref), [`unmark`](@ref), [`ismarked`](@ref). """ function reset(io::T) where T<:IO ismarked(io) || throw(ArgumentError("$T not marked")) m = io.mark seek(io, m) io.mark = -1 # must be after seek, or seek may fail return m end """ ismarked(s::IO) Return `true` if stream `s` is marked. See also [`mark`](@ref), [`unmark`](@ref), [`reset`](@ref). """ ismarked(io::IO) = io.mark >= 0 # Make sure all IO streams support flush, even if only as a no-op, # to make it easier to write generic I/O code. flush(io::IO) = nothing """ skipchars(predicate, io::IO; linecomment=nothing) Advance the stream `io` such that the next-read character will be the first remaining for which `predicate` returns `false`. If the keyword argument `linecomment` is specified, all characters from that character until the start of the next line are ignored. # Examples ```jldoctest julia> buf = IOBuffer(" text") IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=1, mark=-1) julia> skipchars(isspace, buf) IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=5, mark=-1) julia> String(readavailable(buf)) "text" ``` """ function skipchars(predicate, io::IO; linecomment=nothing) for c in readeach(io, Char) if c === linecomment readline(io) elseif !predicate(c) skip(io, -ncodeunits(c)) break end end return io end """ countlines(io::IO; eol::AbstractChar = '\\n') countlines(filename::AbstractString; eol::AbstractChar = '\\n') Read `io` until the end of the stream/file and count the number of lines. To specify a file pass the filename as the first argument. EOL markers other than `'\\n'` are supported by passing them as the second argument. The last non-empty line of `io` is counted even if it does not end with the EOL, matching the length returned by [`eachline`](@ref) and [`readlines`](@ref). To count lines of a `String`, `countlines(IOBuffer(str))` can be used. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization.\\n"); julia> countlines(io) 1 julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> countlines(io) 1 julia> eof(io) # counting lines moves the file pointer true julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> countlines(io, eol = '.') 1 ``` ```jldoctest julia> write("my_file.txt", "JuliaLang is a GitHub organization.\\n") 36 julia> countlines("my_file.txt") 1 julia> countlines("my_file.txt", eol = 'n') 4 julia> rm("my_file.txt") ``` """ function countlines(io::IO; eol::AbstractChar='\n') isascii(eol) || throw(ArgumentError("only ASCII line terminators are supported")) aeol = UInt8(eol) a = Vector{UInt8}(undef, 8192) nl = nb = 0 while !eof(io) nb = readbytes!(io, a) @simd for i=1:nb @inbounds nl += a[i] == aeol end end if nb > 0 && a[nb] != aeol nl += 1 # final line is not terminated with eol end nl end countlines(f::AbstractString; eol::AbstractChar = '\n') = open(io->countlines(io, eol = eol), f)::Int O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/iobuffer.jlำG# This file is a part of Julia. License is MIT: https://julialang.org/license ## work with AbstractVector{UInt8} via I/O primitives ## # Stateful string mutable struct GenericIOBuffer{T<:AbstractVector{UInt8}} <: IO data::T # T should support: getindex, setindex!, length, copyto!, and resize! reinit::Bool # if true, data needs to be re-allocated (after take!) readable::Bool writable::Bool seekable::Bool # if not seekable, implementation is free to destroy (compact) past read data append::Bool # add data at end instead of at pointer size::Int # end pointer (and write pointer if append == true) maxsize::Int # fixed array size (typically pre-allocated) ptr::Int # read (and maybe write) pointer mark::Int # reset mark location for ptr (or <0 for no mark) function GenericIOBuffer{T}(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool, maxsize::Integer) where T<:AbstractVector{UInt8} require_one_based_indexing(data) new(data,false,readable,writable,seekable,append,length(data),maxsize,1,-1) end end const IOBuffer = GenericIOBuffer{Vector{UInt8}} function GenericIOBuffer(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool, maxsize::Integer) where T<:AbstractVector{UInt8} GenericIOBuffer{T}(data, readable, writable, seekable, append, maxsize) end # allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings StringVector(n::Integer) = unsafe_wrap(Vector{UInt8}, _string_n(n)) # IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable). """ IOBuffer([data::AbstractVector{UInt8}]; keywords...) -> IOBuffer Create an in-memory I/O stream, which may optionally operate on a pre-existing array. It may take optional keyword arguments: - `read`, `write`, `append`: restricts operations to the buffer; see `open` for details. - `truncate`: truncates the buffer size to zero length. - `maxsize`: specifies a size beyond which the buffer may not be grown. - `sizehint`: suggests a capacity of the buffer (`data` must implement `sizehint!(data, size)`). When `data` is not given, the buffer will be both readable and writable by default. # Examples ```jldoctest julia> io = IOBuffer(); julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") 56 julia> String(take!(io)) "JuliaLang is a GitHub organization. It has many members." julia> io = IOBuffer(b"JuliaLang is a GitHub organization.") IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=35, maxsize=Inf, ptr=1, mark=-1) julia> read(io, String) "JuliaLang is a GitHub organization." julia> write(io, "This isn't writable.") ERROR: ArgumentError: ensureroom failed, IOBuffer is not writeable julia> io = IOBuffer(UInt8[], read=true, write=true, maxsize=34) IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=34, ptr=1, mark=-1) julia> write(io, "JuliaLang is a GitHub organization.") 34 julia> String(take!(io)) "JuliaLang is a GitHub organization" julia> length(read(IOBuffer(b"data", read=true, truncate=false))) 4 julia> length(read(IOBuffer(b"data", read=true, truncate=true))) 0 ``` """ function IOBuffer( data::AbstractVector{UInt8}; read::Union{Bool,Nothing}=nothing, write::Union{Bool,Nothing}=nothing, append::Union{Bool,Nothing}=nothing, truncate::Union{Bool,Nothing}=nothing, maxsize::Integer=typemax(Int), sizehint::Union{Integer,Nothing}=nothing) if maxsize < 0 throw(ArgumentError("negative maxsize")) end if sizehint !== nothing sizehint!(data, sizehint) end flags = open_flags(read=read, write=write, append=append, truncate=truncate) buf = GenericIOBuffer(data, flags.read, flags.write, true, flags.append, Int(maxsize)) if flags.truncate buf.size = 0 end return buf end function IOBuffer(; read::Union{Bool,Nothing}=true, write::Union{Bool,Nothing}=true, append::Union{Bool,Nothing}=nothing, truncate::Union{Bool,Nothing}=true, maxsize::Integer=typemax(Int), sizehint::Union{Integer,Nothing}=nothing) size = sizehint !== nothing ? Int(sizehint) : maxsize != typemax(Int) ? Int(maxsize) : 32 flags = open_flags(read=read, write=write, append=append, truncate=truncate) buf = IOBuffer( StringVector(size), read=flags.read, write=flags.write, append=flags.append, truncate=flags.truncate, maxsize=maxsize) fill!(buf.data, 0) return buf end # PipeBuffers behave like Unix Pipes. They are typically readable and writable, they act appendable, and are not seekable. """ PipeBuffer(data::Vector{UInt8}=UInt8[]; maxsize::Integer = typemax(Int)) An [`IOBuffer`](@ref) that allows reading and performs writes by appending. Seeking and truncating are not supported. See [`IOBuffer`](@ref) for the available constructors. If `data` is given, creates a `PipeBuffer` to operate on a data vector, optionally specifying a size beyond which the underlying `Array` may not be grown. """ PipeBuffer(data::Vector{UInt8}=UInt8[]; maxsize::Int = typemax(Int)) = GenericIOBuffer(data,true,true,false,true,maxsize) PipeBuffer(maxsize::Integer) = (x = PipeBuffer(StringVector(maxsize), maxsize = maxsize); x.size=0; x) _similar_data(b::GenericIOBuffer, len::Int) = similar(b.data, len) _similar_data(b::IOBuffer, len::Int) = StringVector(len) function copy(b::GenericIOBuffer) ret = typeof(b)(b.reinit ? _similar_data(b, 0) : b.writable ? copyto!(_similar_data(b, length(b.data)), b.data) : b.data, b.readable, b.writable, b.seekable, b.append, b.maxsize) ret.size = b.size ret.ptr = b.ptr return ret end show(io::IO, b::GenericIOBuffer) = print(io, "IOBuffer(data=UInt8[...], ", "readable=", b.readable, ", ", "writable=", b.writable, ", ", "seekable=", b.seekable, ", ", "append=", b.append, ", ", "size=", b.size, ", ", "maxsize=", b.maxsize == typemax(Int) ? "Inf" : b.maxsize, ", ", "ptr=", b.ptr, ", ", "mark=", b.mark, ")") @noinline function _throw_not_readable() # See https://github.com/JuliaLang/julia/issues/29688. throw(ArgumentError("read failed, IOBuffer is not readable")) end function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) from.readable || _throw_not_readable() avail = bytesavailable(from) adv = min(avail, nb) GC.@preserve from unsafe_copyto!(p, pointer(from.data, from.ptr), adv) from.ptr += adv if nb > avail throw(EOFError()) end nothing end function peek(from::GenericIOBuffer, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) from.readable || _throw_not_readable() avail = bytesavailable(from) nb = sizeof(T) if nb > avail throw(EOFError()) end GC.@preserve from begin ptr::Ptr{T} = pointer(from.data, from.ptr) x = unsafe_load(ptr) end return x end function read(from::GenericIOBuffer, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) x = peek(from, T) from.ptr += sizeof(T) return x end function read_sub(from::GenericIOBuffer, a::AbstractArray{T}, offs, nel) where T require_one_based_indexing(a) from.readable || _throw_not_readable() if offs+nel-1 > length(a) || offs < 1 || nel < 0 throw(BoundsError()) end if isbitstype(T) && isa(a,Array) nb = UInt(nel * sizeof(T)) GC.@preserve a unsafe_read(from, pointer(a, offs), nb) else for i = offs:offs+nel-1 a[i] = read(from, T) end end return a end @inline function read(from::GenericIOBuffer, ::Type{UInt8}) from.readable || _throw_not_readable() ptr = from.ptr size = from.size if ptr > size throw(EOFError()) end @inbounds byte = from.data[ptr]::UInt8 from.ptr = ptr + 1 return byte end function peek(from::GenericIOBuffer, ::Type{UInt8}) from.readable || _throw_not_readable() if from.ptr > from.size throw(EOFError()) end return from.data[from.ptr]::UInt8 end read(from::GenericIOBuffer, ::Type{Ptr{T}}) where {T} = convert(Ptr{T}, read(from, UInt)) isreadable(io::GenericIOBuffer) = io.readable iswritable(io::GenericIOBuffer) = io.writable # TODO: GenericIOBuffer is not iterable, so doesn't really have a length. # This should maybe be sizeof() instead. #length(io::GenericIOBuffer) = (io.seekable ? io.size : bytesavailable(io)) bytesavailable(io::GenericIOBuffer) = io.size - io.ptr + 1 position(io::GenericIOBuffer) = io.ptr-1 function skip(io::GenericIOBuffer, n::Integer) seekto = io.ptr + n n < 0 && return seek(io, seekto-1) # Does error checking io.ptr = min(seekto, io.size+1) return io end function seek(io::GenericIOBuffer, n::Integer) if !io.seekable ismarked(io) || throw(ArgumentError("seek failed, IOBuffer is not seekable and is not marked")) n == io.mark || throw(ArgumentError("seek failed, IOBuffer is not seekable and n != mark")) end # TODO: REPL.jl relies on the fact that this does not throw (by seeking past the beginning or end # of an GenericIOBuffer), so that would need to be fixed in order to throw an error here #(n < 0 || n > io.size) && throw(ArgumentError("Attempted to seek outside IOBuffer boundaries.")) #io.ptr = n+1 io.ptr = max(min(n+1, io.size+1), 1) return io end function seekend(io::GenericIOBuffer) io.ptr = io.size+1 return io end function truncate(io::GenericIOBuffer, n::Integer) io.writable || throw(ArgumentError("truncate failed, IOBuffer is not writeable")) io.seekable || throw(ArgumentError("truncate failed, IOBuffer is not seekable")) n < 0 && throw(ArgumentError("truncate failed, n bytes must be โ‰ฅ 0, got $n")) n > io.maxsize && throw(ArgumentError("truncate failed, $(n) bytes is exceeds IOBuffer maxsize $(io.maxsize)")) if io.reinit io.data = _similar_data(io, n) io.reinit = false elseif n > length(io.data) resize!(io.data, n) end io.data[io.size+1:n] .= 0 io.size = n io.ptr = min(io.ptr, n+1) ismarked(io) && io.mark > n && unmark(io) return io end function compact(io::GenericIOBuffer) io.writable || throw(ArgumentError("compact failed, IOBuffer is not writeable")) io.seekable && throw(ArgumentError("compact failed, IOBuffer is seekable")) local ptr::Int, bytes_to_move::Int if ismarked(io) && io.mark < io.ptr if io.mark == 0 return end ptr = io.mark bytes_to_move = bytesavailable(io) + (io.ptr-io.mark) else ptr = io.ptr bytes_to_move = bytesavailable(io) end copyto!(io.data, 1, io.data, ptr, bytes_to_move) io.size -= ptr - 1 io.ptr -= ptr - 1 io.mark -= ptr - 1 return io end @noinline function ensureroom_slowpath(io::GenericIOBuffer, nshort::UInt) io.writable || throw(ArgumentError("ensureroom failed, IOBuffer is not writeable")) if !io.seekable if !ismarked(io) && io.ptr > 1 && io.size <= io.ptr - 1 io.ptr = 1 io.size = 0 else datastart = ismarked(io) ? io.mark : io.ptr if (io.size+nshort > io.maxsize) || (datastart > 4096 && datastart > io.size - io.ptr) || (datastart > 262144) # apply somewhat arbitrary heuristics to decide when to destroy # old, read data to make more room for new data compact(io) end end end return end @inline ensureroom(io::GenericIOBuffer, nshort::Int) = ensureroom(io, UInt(nshort)) @inline function ensureroom(io::GenericIOBuffer, nshort::UInt) if !io.writable || (!io.seekable && io.ptr > 1) ensureroom_slowpath(io, nshort) end n = min((nshort % Int) + (io.append ? io.size : io.ptr-1), io.maxsize) if io.reinit io.data = _similar_data(io, n) io.reinit = false else l = length(io.data) if n > l _growend!(io.data, (n - l) % UInt) end end return io end eof(io::GenericIOBuffer) = (io.ptr-1 == io.size) function closewrite(io::GenericIOBuffer) io.writable = false # OR throw(_UVError("closewrite", UV_ENOTSOCK)) nothing end @noinline function close(io::GenericIOBuffer{T}) where T io.readable = false io.writable = false io.seekable = false io.size = 0 io.maxsize = 0 io.ptr = 1 io.mark = -1 if io.writable resize!(io.data, 0) end nothing end isopen(io::GenericIOBuffer) = io.readable || io.writable || io.seekable || bytesavailable(io) > 0 """ take!(b::IOBuffer) Obtain the contents of an `IOBuffer` as an array. Afterwards, the `IOBuffer` is reset to its initial state. # Examples ```jldoctest julia> io = IOBuffer(); julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") 56 julia> String(take!(io)) "JuliaLang is a GitHub organization. It has many members." ``` """ function take!(io::GenericIOBuffer) ismarked(io) && unmark(io) if io.seekable nbytes = io.size data = copyto!(StringVector(nbytes), 1, io.data, 1, nbytes) else nbytes = bytesavailable(io) data = read!(io,StringVector(nbytes)) end if io.writable io.ptr = 1 io.size = 0 end return data end function take!(io::IOBuffer) ismarked(io) && unmark(io) if io.seekable if io.writable if io.reinit data = StringVector(0) else data = resize!(io.data, io.size) io.reinit = true end else data = copyto!(StringVector(io.size), 1, io.data, 1, io.size) end else nbytes = bytesavailable(io) if io.writable data = io.data io.reinit = true _deletebeg!(data, io.ptr-1) resize!(data, nbytes) else data = read!(io, StringVector(nbytes)) end end if io.writable io.ptr = 1 io.size = 0 end return data end """ _unsafe_take!(io::IOBuffer) This simply returns the raw resized `io.data`, with no checks to be sure that `io` is readable etcetera, and leaves `io` in an inconsistent state. This should only be used internally for performance-critical `String` routines that immediately discard `io` afterwards, and it *assumes* that `io` is writable and seekable. It saves no allocations compared to `take!`, it just omits some checks. """ _unsafe_take!(io::IOBuffer) = resize!(io.data, io.size) function write(to::IO, from::GenericIOBuffer) if to === from from.ptr = from.size + 1 return 0 end written::Int = GC.@preserve from unsafe_write(to, pointer(from.data, from.ptr), UInt(bytesavailable(from))) from.ptr += written return written end function unsafe_write(to::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) ensureroom(to, nb) ptr = (to.append ? to.size+1 : to.ptr) written = Int(min(nb, Int(length(to.data))::Int - ptr + 1)) towrite = written d = to.data while towrite > 0 @inbounds d[ptr] = unsafe_load(p) ptr += 1 p += 1 towrite -= 1 end to.size = max(to.size, ptr - 1) if !to.append to.ptr += written end return written end @inline function write(to::GenericIOBuffer, a::UInt8) ensureroom(to, UInt(1)) ptr = (to.append ? to.size+1 : to.ptr) if ptr > to.maxsize return 0 else to.data[ptr] = a end to.size = max(to.size, ptr) if !to.append to.ptr += 1 end return sizeof(UInt8) end readbytes!(io::GenericIOBuffer, b::Array{UInt8}, nb=length(b)) = readbytes!(io, b, Int(nb)) function readbytes!(io::GenericIOBuffer, b::Array{UInt8}, nb::Int) nr = min(nb, bytesavailable(io)) if length(b) < nr resize!(b, nr) end read_sub(io, b, 1, nr) return nr end read(io::GenericIOBuffer) = read!(io,StringVector(bytesavailable(io))) readavailable(io::GenericIOBuffer) = read(io) read(io::GenericIOBuffer, nb::Integer) = read!(io,StringVector(min(nb, bytesavailable(io)))) function occursin(delim::UInt8, buf::IOBuffer) p = pointer(buf.data, buf.ptr) q = GC.@preserve buf ccall(:memchr,Ptr{UInt8},(Ptr{UInt8},Int32,Csize_t),p,delim,bytesavailable(buf)) return q != C_NULL end function occursin(delim::UInt8, buf::GenericIOBuffer) data = buf.data for i = buf.ptr:buf.size @inbounds b = data[i] b == delim && return true end return false end function readuntil(io::GenericIOBuffer, delim::UInt8; keep::Bool=false) lb = 70 A = StringVector(lb) nread = 0 nout = 0 data = io.data for i = io.ptr : io.size @inbounds b = data[i] nread += 1 if keep || b != delim nout += 1 if nout > lb lb = nout*2 resize!(A, lb) end @inbounds A[nout] = b end if b == delim break end end io.ptr += nread if lb != nout resize!(A, nout) end A end # copy-free crc32c of IOBuffer: function _crc32c(io::IOBuffer, nb::Integer, crc::UInt32=0x00000000) nb < 0 && throw(ArgumentError("number of bytes to checksum must be โ‰ฅ 0, got $nb")) io.readable || _throw_not_readable() n = min(nb, bytesavailable(io)) n == 0 && return crc crc = GC.@preserve io unsafe_crc32c(pointer(io.data, io.ptr), n, crc) io.ptr += n return crc end _crc32c(io::IOBuffer, crc::UInt32=0x00000000) = _crc32c(io, bytesavailable(io), crc) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/intfuncs.jl”# This file is a part of Julia. License is MIT: https://julialang.org/license ## number-theoretic functions ## """ gcd(x, y...) Greatest common (positive) divisor (or zero if all arguments are zero). The arguments may be integer and rational numbers. !!! compat "Julia 1.4" Rational arguments require Julia 1.4 or later. # Examples ```jldoctest julia> gcd(6, 9) 3 julia> gcd(6, -9) 3 julia> gcd(6, 0) 6 julia> gcd(0, 0) 0 julia> gcd(1//3, 2//3) 1//3 julia> gcd(1//3, -2//3) 1//3 julia> gcd(1//3, 2) 1//3 julia> gcd(0, 0, 10, 15) 5 ``` """ function gcd(a::T, b::T) where T<:Integer while b != 0 t = b b = rem(a, b) a = t end checked_abs(a) end function gcd(a::T, b::T) where T<:BitInteger a == 0 && return Base.checked_abs(b) b == 0 && return Base.checked_abs(a) if a isa Signed && a == typemin(T) if a == b Base.__throw_gcd_overflow(a, b) else a, b = b, a end end return _gcd(a, b) end @noinline __throw_gcd_overflow(a, b) = throw(OverflowError(LazyString("gcd(", a, ", ", b, ") overflows"))) function absdiff(x::T,y::T) where {T<:Unsigned} d = max(x,y) - min(x,y) d, d end function absdiff(x::T,y::T) where {T<:Signed} d = x - y abs(d), d end # binary GCD (aka Stein's) algorithm # about 1.7x (2.1x) faster for random Int64s (Int128s) # Unfortunately, we need to manually annotate this as `@assume_effects :terminates_locally` to work around #41694. # Since this is used in the Rational constructor, constant folding is something we do care about here. @assume_effects :terminates_locally function _gcd(ain::T, bin::T) where T<:BitInteger zb = trailing_zeros(bin) za = trailing_zeros(ain) a = abs(ain) b = abs(bin >> zb) k = min(za, zb) while a != 0 a >>= za absd, diff = absdiff(a, b) za = trailing_zeros(diff) b = min(a, b) a = absd end r = b << k return r % T end """ lcm(x, y...) Least common (positive) multiple (or zero if any argument is zero). The arguments may be integer and rational numbers. !!! compat "Julia 1.4" Rational arguments require Julia 1.4 or later. # Examples ```jldoctest julia> lcm(2, 3) 6 julia> lcm(-2, 3) 6 julia> lcm(0, 3) 0 julia> lcm(0, 0) 0 julia> lcm(1//3, 2//3) 2//3 julia> lcm(1//3, -2//3) 2//3 julia> lcm(1//3, 2) 2//1 julia> lcm(1, 3, 5, 7) 105 ``` """ function lcm(a::T, b::T) where T<:Integer # explicit a==0 test is to handle case of lcm(0, 0) correctly # explicit b==0 test is to handle case of lcm(typemin(T),0) correctly if a == 0 || b == 0 return zero(a) else return checked_abs(checked_mul(a, div(b, gcd(b,a)))) end end gcd(a::Integer) = checked_abs(a) gcd(a::Rational) = checked_abs(a.num) // a.den lcm(a::Union{Integer,Rational}) = gcd(a) gcd(a::Unsigned, b::Signed) = gcd(promote(a, abs(b))...) gcd(a::Signed, b::Unsigned) = gcd(promote(abs(a), b)...) gcd(a::Real, b::Real) = gcd(promote(a,b)...) lcm(a::Real, b::Real) = lcm(promote(a,b)...) gcd(a::Real, b::Real, c::Real...) = gcd(a, gcd(b, c...)) lcm(a::Real, b::Real, c::Real...) = lcm(a, lcm(b, c...)) gcd(a::T, b::T) where T<:Real = throw(MethodError(gcd, (a,b))) lcm(a::T, b::T) where T<:Real = throw(MethodError(lcm, (a,b))) gcd(abc::AbstractArray{<:Real}) = reduce(gcd, abc; init=zero(eltype(abc))) lcm(abc::AbstractArray{<:Real}) = reduce(lcm, abc; init=one(eltype(abc))) function gcd(abc::AbstractArray{<:Integer}) a = zero(eltype(abc)) for b in abc a = gcd(a, b) if a == 1 return a end end return a end # return (gcd(a, b), x, y) such that ax+by == gcd(a, b) """ gcdx(a, b) Computes the greatest common (positive) divisor of `a` and `b` and their Bรฉzout coefficients, i.e. the integer coefficients `u` and `v` that satisfy ``ua+vb = d = gcd(a, b)``. ``gcdx(a, b)`` returns ``(d, u, v)``. The arguments may be integer and rational numbers. !!! compat "Julia 1.4" Rational arguments require Julia 1.4 or later. # Examples ```jldoctest julia> gcdx(12, 42) (6, -3, 1) julia> gcdx(240, 46) (2, -9, 47) ``` !!! note Bรฉzout coefficients are *not* uniquely defined. `gcdx` returns the minimal Bรฉzout coefficients that are computed by the extended Euclidean algorithm. (Ref: D. Knuth, TAoCP, 2/e, p. 325, Algorithm X.) For signed integers, these coefficients `u` and `v` are minimal in the sense that ``|u| < |b/d|`` and ``|v| < |a/d|``. Furthermore, the signs of `u` and `v` are chosen so that `d` is positive. For unsigned integers, the coefficients `u` and `v` might be near their `typemax`, and the identity then holds only via the unsigned integers' modulo arithmetic. """ Base.@assume_effects :terminates_locally function gcdx(a::Integer, b::Integer) T = promote_type(typeof(a), typeof(b)) # a0, b0 = a, b s0, s1 = oneunit(T), zero(T) t0, t1 = s1, s0 # The loop invariant is: s0*a0 + t0*b0 == a && s1*a0 + t1*b0 == b x = a % T y = b % T while y != 0 q, r = divrem(x, y) x, y = y, r s0, s1 = s1, s0 - q*s1 t0, t1 = t1, t0 - q*t1 end x < 0 ? (-x, -s0, -t0) : (x, s0, t0) end gcdx(a::Real, b::Real) = gcdx(promote(a,b)...) gcdx(a::T, b::T) where T<:Real = throw(MethodError(gcdx, (a,b))) # multiplicative inverse of n mod m, error if none """ invmod(n, m) Take the inverse of `n` modulo `m`: `y` such that ``n y = 1 \\pmod m``, and ``div(y,m) = 0``. This will throw an error if ``m = 0``, or if ``gcd(n,m) \\neq 1``. # Examples ```jldoctest julia> invmod(2, 5) 3 julia> invmod(2, 3) 2 julia> invmod(5, 6) 5 ``` """ function invmod(n::Integer, m::Integer) iszero(m) && throw(DomainError(m, "`m` must not be 0.")) if n isa Signed && hastypemax(typeof(n)) # work around inconsistencies in gcdx # https://github.com/JuliaLang/julia/issues/33781 T = promote_type(typeof(n), typeof(m)) n == typemin(typeof(n)) && m == typeof(n)(-1) && return T(0) n == typeof(n)(-1) && m == typemin(typeof(n)) && return T(-1) end g, x, y = gcdx(n, m) g != 1 && throw(DomainError((n, m), LazyString("Greatest common divisor is ", g, "."))) # Note that m might be negative here. if n isa Unsigned && hastypemax(typeof(n)) && x > typemax(n)>>1 # x might have wrapped if it would have been negative # adding back m forces a correction x += m end # The postcondition is: mod(result * n, m) == mod(T(1), m) && div(result, m) == 0 return mod(x, m) end # ^ for any x supporting * to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) @noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, LazyString( "Cannot raise an integer x to a negative power ", p, ".", "\nConvert input to float."))) @noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p, LazyString( "Cannot raise an integer x to a negative power ", p, ".", "\nMake x or ", p, " a float by adding a zero decimal ", "(e.g., 2.0^", p, " or 2^", float(p), " instead of 2^", p, ")", "or write 1/x^", -p, ", float(x)^", p, ", x^float(", p, ") or (x//1)^", p, "."))) @noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p, LazyString( "Cannot raise an integer matrix x to a negative power ", p, ".", "\nMake x a float matrix by adding a zero decimal ", "(e.g., [2.0 1.0;1.0 0.0]^", p, " instead of [2 1;1 0]^", p, ")", "or write float(x)^", p, " or Rational.(x)^", p, "."))) @assume_effects :terminates_locally function power_by_squaring(x_, p::Integer) x = to_power_type(x_) if p == 1 return copy(x) elseif p == 0 return one(x) elseif p == 2 return x*x elseif p < 0 isone(x) && return copy(x) isone(-x) && return iseven(p) ? one(x) : copy(x) throw_domerr_powbysq(x, p) end t = trailing_zeros(p) + 1 p >>= t while (t -= 1) > 0 x *= x end y = x while p > 0 t = trailing_zeros(p) + 1 p >>= t while (t -= 1) >= 0 x *= x end y *= x end return y end power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x) function power_by_squaring(x::Bool, p::Integer) p < 0 && !x && throw_domerr_powbysq(x, p) return (p==0) | x end ^(x::T, p::T) where {T<:Integer} = power_by_squaring(x,p) ^(x::Number, p::Integer) = power_by_squaring(x,p) # x^p for any literal integer p is lowered to Base.literal_pow(^, x, Val(p)) # to enable compile-time optimizations specialized to p. # However, we still need a fallback that calls the function ^ which may either # mean Base.^ or something else, depending on context. # We mark these @inline since if the target is marked @inline, # we want to make sure that gets propagated, # even if it is over the inlining threshold. @inline literal_pow(f, x, ::Val{p}) where {p} = f(x,p) # Restrict inlining to hardware-supported arithmetic types, which # are fast enough to benefit from inlining. const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64} const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}} # Inline x^2 and x^3 for Val # (The first argument prevents unexpected behavior if a function ^ # is defined that is not equal to Base.^) @inline literal_pow(::typeof(^), x::HWNumber, ::Val{0}) = one(x) @inline literal_pow(::typeof(^), x::HWNumber, ::Val{1}) = x @inline literal_pow(::typeof(^), x::HWNumber, ::Val{2}) = x*x @inline literal_pow(::typeof(^), x::HWNumber, ::Val{3}) = x*x*x @inline literal_pow(::typeof(^), x::HWNumber, ::Val{-1}) = inv(x) @inline literal_pow(::typeof(^), x::HWNumber, ::Val{-2}) = (i=inv(x); i*i) # don't use the inv(x) transformation here since float^p is slightly more accurate @inline literal_pow(::typeof(^), x::AbstractFloat, ::Val{p}) where {p} = x^p @inline literal_pow(::typeof(^), x::AbstractFloat, ::Val{-1}) = inv(x) # for other types, define x^-n as inv(x)^n so that negative literal powers can # be computed in a type-stable way even for e.g. integers. @inline function literal_pow(f::typeof(^), x, ::Val{p}) where {p} if p < 0 if x isa BitInteger64 f(Float64(x), p) # inv would cause rounding, while Float64^Integer is able to compensate the inverse else f(inv(x), -p) end else f(x, p) end end # note: it is tempting to add optimized literal_pow(::typeof(^), x, ::Val{n}) # methods here for various n, but this easily leads to method ambiguities # if anyone has defined literal_pow(::typeof(^), x::T, ::Val). # b^p mod m """ powermod(x::Integer, p::Integer, m) Compute ``x^p \\pmod m``. # Examples ```jldoctest julia> powermod(2, 6, 5) 4 julia> mod(2^6, 5) 4 julia> powermod(5, 2, 20) 5 julia> powermod(5, 2, 19) 6 julia> powermod(5, 3, 19) 11 ``` """ function powermod(x::Integer, p::Integer, m::T) where T<:Integer p == 0 && return mod(one(m),m) # When the concrete type of p is signed and has the lowest value, # `p != 0 && p == -p` is equivalent to `p == typemin(typeof(p))` for 2's complement representation. # but will work for integer types like `BigInt` that don't have `typemin` defined # It needs special handling otherwise will cause overflow problem. if p == -p imod = invmod(x, m) rhalf = powermod(imod, -(pรท2), m) r::T = mod(widemul(rhalf, rhalf), m) isodd(p) && (r = mod(widemul(r, imod), m)) #else odd return r elseif p < 0 return powermod(invmod(x, m), -p, m) end (m == 1 || m == -1) && return zero(m) b = oftype(m,mod(x,m)) # this also checks for divide by zero t = prevpow(2, p) r = 1 while true if p >= t r = mod(widemul(r,b),m) p -= t end t >>>= 1 t <= 0 && break r = mod(widemul(r,r),m) end return r end # optimization: promote the modulus m to BigInt only once (cf. widemul in generic powermod above) powermod(x::Integer, p::Integer, m::Union{Int128,UInt128}) = oftype(m, powermod(x, p, big(m))) _nextpow2(x::Unsigned) = oneunit(x)<<(top_set_bit(x-oneunit(x))) _nextpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -_nextpow2(unsigned(-x)) : _nextpow2(unsigned(x))) _prevpow2(x::Unsigned) = one(x) << unsigned(top_set_bit(x)-1) _prevpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -_prevpow2(unsigned(-x)) : _prevpow2(unsigned(x))) """ ispow2(n::Number) -> Bool Test whether `n` is an integer power of two. See also [`count_ones`](@ref), [`prevpow`](@ref), [`nextpow`](@ref). # Examples ```jldoctest julia> ispow2(4) true julia> ispow2(5) false julia> ispow2(4.5) false julia> ispow2(0.25) true julia> ispow2(1//8) true ``` !!! compat "Julia 1.6" Support for non-`Integer` arguments was added in Julia 1.6. """ ispow2(x::Number) = isreal(x) && ispow2(real(x)) ispow2(x::Integer) = x > 0 && count_ones(x) == 1 """ nextpow(a, x) The smallest `a^n` not less than `x`, where `n` is a non-negative integer. `a` must be greater than 1, and `x` must be greater than 0. See also [`prevpow`](@ref). # Examples ```jldoctest julia> nextpow(2, 7) 8 julia> nextpow(2, 9) 16 julia> nextpow(5, 20) 25 julia> nextpow(4, 16) 16 ``` """ function nextpow(a::Real, x::Real) x <= 0 && throw(DomainError(x, "`x` must be positive.")) # Special case fast path for x::Integer, a == 2. # This is a very common case. Constant prop will make sure that a call site # specified as `nextpow(2, x)` will get this special case inlined. a == 2 && isa(x, Integer) && return _nextpow2(x) a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) x <= 1 && return one(a) n = ceil(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^(n-1) x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) p >= x && return p wp = a^n wp > p || throw(OverflowError("result is beyond the range of type of the base")) wp >= x && return wp wwp = a^(n+1) wwp > wp || throw(OverflowError("result is beyond the range of type of the base")) return wwp end """ prevpow(a, x) The largest `a^n` not greater than `x`, where `n` is a non-negative integer. `a` must be greater than 1, and `x` must not be less than 1. See also [`nextpow`](@ref), [`isqrt`](@ref). # Examples ```jldoctest julia> prevpow(2, 7) 4 julia> prevpow(2, 9) 8 julia> prevpow(5, 20) 5 julia> prevpow(4, 16) 16 ``` """ function prevpow(a::T, x::Real) where T <: Real x < 1 && throw(DomainError(x, "`x` must be โ‰ฅ 1.")) # See comment in nextpos() for a == special case. a == 2 && isa(x, Integer) && return _prevpow2(x) a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) n = floor(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^n x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) if a isa Integer wp, overflow = mul_with_overflow(a, p) wp <= x && !overflow && return wp else wp = a^(n+1) wp <= x && return wp end p <= x && return p return a^(n-1) end ## ndigits (number of digits) in base 10 ## # decimal digits in an unsigned integer const powers_of_ten = [ 0x0000000000000001, 0x000000000000000a, 0x0000000000000064, 0x00000000000003e8, 0x0000000000002710, 0x00000000000186a0, 0x00000000000f4240, 0x0000000000989680, 0x0000000005f5e100, 0x000000003b9aca00, 0x00000002540be400, 0x000000174876e800, 0x000000e8d4a51000, 0x000009184e72a000, 0x00005af3107a4000, 0x00038d7ea4c68000, 0x002386f26fc10000, 0x016345785d8a0000, 0x0de0b6b3a7640000, 0x8ac7230489e80000, ] function bit_ndigits0z(x::Base.BitUnsigned64) lz = top_set_bit(x) nd = (1233*lz)>>12+1 nd -= x < powers_of_ten[nd] end function bit_ndigits0z(x::UInt128) n = 0 while x > 0x8ac7230489e80000 # 10e18 x = div(x,0x8ac7230489e80000) n += 19 end return n + ndigits0z(UInt64(x)) end ndigits0z(x::BitSigned) = bit_ndigits0z(unsigned(abs(x))) ndigits0z(x::BitUnsigned) = bit_ndigits0z(x) ndigits0z(x::Integer) = ndigits0zpb(x, 10) ## ndigits with specified base ## # The suffix "nb" stands for "negative base" function ndigits0znb(x::Integer, b::Integer) d = 0 if x isa Unsigned d += (x != 0)::Bool x = -signed(fld(x, -b)) end # precondition: b < -1 && !(typeof(x) <: Unsigned) while x != 0 x = cld(x,b) d += 1 end return d end # do first division before conversion with signed here, which can otherwise overflow ndigits0znb(x::Bool, b::Integer) = x % Int # The suffix "pb" stands for "positive base" function ndigits0zpb(x::Integer, b::Integer) # precondition: b > 1 x == 0 && return 0 b = Int(b) x = abs(x) if x isa Base.BitInteger x = unsigned(x)::Unsigned b == 2 && return top_set_bit(x) b == 8 && return (top_set_bit(x) + 2) รท 3 b == 16 && return sizeof(x)<<1 - leading_zeros(x)>>2 b == 10 && return bit_ndigits0z(x) if ispow2(b) dv, rm = divrem(top_set_bit(x), trailing_zeros(b)) return iszero(rm) ? dv : dv + 1 end end d = 0 while x > typemax(Int) x = div(x,b) d += 1 end x = div(x,b) d += 1 m = 1 while m <= x m *= b d += 1 end return d end ndigits0zpb(x::Bool, b::Integer) = x % Int # The suffix "0z" means that the output is 0 on input zero (cf. #16841) """ ndigits0z(n::Integer, b::Integer=10) Return 0 if `n == 0`, otherwise compute the number of digits in integer `n` written in base `b` (i.e. equal to `ndigits(n, base=b)` in this case). The base `b` must not be in `[-1, 0, 1]`. # Examples ```jldoctest julia> Base.ndigits0z(0, 16) 0 julia> Base.ndigits(0, base=16) 1 julia> Base.ndigits0z(0) 0 julia> Base.ndigits0z(10, 2) 4 julia> Base.ndigits0z(10) 2 ``` See also [`ndigits`](@ref). """ function ndigits0z(x::Integer, b::Integer) if b < -1 ndigits0znb(x, b) elseif b > 1 ndigits0zpb(x, b) else throw(DomainError(b, "The base must not be in `[-1, 0, 1]`.")) end end # Extends the definition in base/int.jl top_set_bit(x::Integer) = ceil(Integer, log2(x + oneunit(x))) """ ndigits(n::Integer; base::Integer=10, pad::Integer=1) Compute the number of digits in integer `n` written in base `base` (`base` must not be in `[-1, 0, 1]`), optionally padded with zeros to a specified size (the result will never be less than `pad`). See also [`digits`](@ref), [`count_ones`](@ref). # Examples ```jldoctest julia> ndigits(0) 1 julia> ndigits(12345) 5 julia> ndigits(1022, base=16) 3 julia> string(1022, base=16) "3fe" julia> ndigits(123, pad=5) 5 julia> ndigits(-123) 3 ``` """ ndigits(x::Integer; base::Integer=10, pad::Integer=1) = max(pad, ndigits0z(x, base)) ## integer to string functions ## function bin(x::Unsigned, pad::Int, neg::Bool) m = top_set_bit(x) n = neg + max(pad, m) a = StringVector(n) # for i in 0x0:UInt(n-1) # automatic vectorization produces redundant codes # @inbounds a[n - i] = 0x30 + (((x >> i) % UInt8)::UInt8 & 0x1) # end i = n @inbounds while i >= 4 b = UInt32((x % UInt8)::UInt8) d = 0x30303030 + ((b * 0x08040201) >> 0x3) & 0x01010101 a[i-3] = (d >> 0x00) % UInt8 a[i-2] = (d >> 0x08) % UInt8 a[i-1] = (d >> 0x10) % UInt8 a[i] = (d >> 0x18) % UInt8 x >>= 0x4 i -= 4 end while i > neg @inbounds a[i] = 0x30 + ((x % UInt8)::UInt8 & 0x1) x >>= 0x1 i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') String(a) end function oct(x::Unsigned, pad::Int, neg::Bool) m = div(top_set_bit(x) + 2, 3) n = neg + max(pad, m) a = StringVector(n) i = n while i > neg @inbounds a[i] = 0x30 + ((x % UInt8)::UInt8 & 0x7) x >>= 0x3 i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') String(a) end # 2-digit decimal characters ("00":"99") const _dec_d100 = UInt16[ # generating expression: UInt16[(0x30 + i % 10) << 0x8 + (0x30 + i รท 10) for i = 0:99] # 0 0, 0 1, 0 2, 0 3, and so on in little-endian 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930, 0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931, 0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932, 0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933, 0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934, 0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935, 0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936, 0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937, 0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938, 0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939 ] function append_c_digits(olength::Int, digits::Unsigned, buf, pos::Int) i = olength while i >= 2 d, c = divrem(digits, 0x64) digits = oftype(digits, d) @inbounds d100 = _dec_d100[(c % Int) + 1] @inbounds buf[pos + i - 2] = d100 % UInt8 @inbounds buf[pos + i - 1] = (d100 >> 0x8) % UInt8 i -= 2 end if i == 1 @inbounds buf[pos] = UInt8('0') + rem(digits, 0xa) % UInt8 i -= 1 end return pos + olength end function append_nine_digits(digits::Unsigned, buf, pos::Int) if digits == 0 for _ = 1:9 @inbounds buf[pos] = UInt8('0') pos += 1 end return pos end return @inline append_c_digits(9, digits, buf, pos) # force loop-unrolling on the length end function append_c_digits_fast(olength::Int, digits::Unsigned, buf, pos::Int) i = olength # n.b. olength may be larger than required to print all of `digits` (and will be padded # with zeros), but the printed number will be undefined if it is smaller, and may include # bits of both the high and low bytes. maxpow10 = 0x3b9aca00 # 10e9 as UInt32 while i > 9 && digits > typemax(UInt) # do everything in cheap math chunks, using the processor's native math size d, c = divrem(digits, maxpow10) digits = oftype(digits, d) append_nine_digits(c % UInt32, buf, pos + i - 9) i -= 9 end append_c_digits(i, digits % UInt, buf, pos) return pos + olength end function dec(x::Unsigned, pad::Int, neg::Bool) n = neg + ndigits(x, pad=pad) a = StringVector(n) append_c_digits_fast(n, x, a, 1) neg && (@inbounds a[1] = 0x2d) # UInt8('-') String(a) end function hex(x::Unsigned, pad::Int, neg::Bool) m = 2 * sizeof(x) - (leading_zeros(x) >> 2) n = neg + max(pad, m) a = StringVector(n) i = n while i >= 2 b = (x % UInt8)::UInt8 d1, d2 = b >> 0x4, b & 0xf @inbounds a[i-1] = d1 + ifelse(d1 > 0x9, 0x57, 0x30) @inbounds a[i] = d2 + ifelse(d2 > 0x9, 0x57, 0x30) x >>= 0x8 i -= 2 end if i > neg d = (x % UInt8)::UInt8 & 0xf @inbounds a[i] = d + ifelse(d > 0x9, 0x57, 0x30) end neg && (@inbounds a[1] = 0x2d) # UInt8('-') String(a) end const base36digits = UInt8['0':'9';'a':'z'] const base62digits = UInt8['0':'9';'A':'Z';'a':'z'] function _base(base::Integer, x::Integer, pad::Int, neg::Bool) (x >= 0) | (base < 0) || throw(DomainError(x, "For negative `x`, `base` must be negative.")) 2 <= abs(base) <= 62 || throw(DomainError(base, "base must satisfy 2 โ‰ค abs(base) โ‰ค 62")) b = (base % Int)::Int digits = abs(b) <= 36 ? base36digits : base62digits n = neg + ndigits(x, base=b, pad=pad) a = StringVector(n) i = n @inbounds while i > neg if b > 0 a[i] = digits[1 + (rem(x, b) % Int)::Int] x = div(x,b) else a[i] = digits[1 + (mod(x, -b) % Int)::Int] x = cld(x,b) end i -= 1 end neg && (@inbounds a[1] = 0x2d) # UInt8('-') String(a) end split_sign(n::Integer) = unsigned(abs(n)), n < 0 split_sign(n::Unsigned) = n, false """ string(n::Integer; base::Integer = 10, pad::Integer = 1) Convert an integer `n` to a string in the given `base`, optionally specifying a number of digits to pad to. See also [`digits`](@ref), [`bitstring`](@ref), [`count_zeros`](@ref). # Examples ```jldoctest julia> string(5, base = 13, pad = 4) "0005" julia> string(-13, base = 5, pad = 4) "-0023" ``` """ function string(n::Integer; base::Integer = 10, pad::Integer = 1) pad = (min(max(pad, typemin(Int)), typemax(Int)) % Int)::Int if base == 2 (n_positive, neg) = split_sign(n) bin(n_positive, pad, neg) elseif base == 8 (n_positive, neg) = split_sign(n) oct(n_positive, pad, neg) elseif base == 10 (n_positive, neg) = split_sign(n) dec(n_positive, pad, neg) elseif base == 16 (n_positive, neg) = split_sign(n) hex(n_positive, pad, neg) else _base(base, base > 0 ? unsigned(abs(n)) : convert(Signed, n), pad, (base>0) & (n<0)) end end string(b::Bool) = b ? "true" : "false" """ bitstring(n) A string giving the literal bit representation of a primitive type. See also [`count_ones`](@ref), [`count_zeros`](@ref), [`digits`](@ref). # Examples ```jldoctest julia> bitstring(Int32(4)) "00000000000000000000000000000100" julia> bitstring(2.2) "0100000000000001100110011001100110011001100110011001100110011010" ``` """ function bitstring(x::T) where {T} isprimitivetype(T) || throw(ArgumentError("$T not a primitive type")) sz = sizeof(T) * 8 str = StringVector(sz) i = sz @inbounds while i >= 4 b = UInt32(sizeof(T) == 1 ? bitcast(UInt8, x) : trunc_int(UInt8, x)) d = 0x30303030 + ((b * 0x08040201) >> 0x3) & 0x01010101 str[i-3] = (d >> 0x00) % UInt8 str[i-2] = (d >> 0x08) % UInt8 str[i-1] = (d >> 0x10) % UInt8 str[i] = (d >> 0x18) % UInt8 x = lshr_int(x, 4) i -= 4 end return String(str) end """ digits([T<:Integer], n::Integer; base::T = 10, pad::Integer = 1) Return an array with element type `T` (default `Int`) of the digits of `n` in the given base, optionally padded with zeros to a specified size. More significant digits are at higher indices, such that `n == sum(digits[k]*base^(k-1) for k=1:length(digits))`. See also [`ndigits`](@ref), [`digits!`](@ref), and for base 2 also [`bitstring`](@ref), [`count_ones`](@ref). # Examples ```jldoctest julia> digits(10) 2-element Vector{Int64}: 0 1 julia> digits(10, base = 2) 4-element Vector{Int64}: 0 1 0 1 julia> digits(-256, base = 10, pad = 5) 5-element Vector{Int64}: -6 -5 -2 0 0 julia> n = rand(-999:999); julia> n == evalpoly(13, digits(n, base = 13)) true ``` """ digits(n::Integer; base::Integer = 10, pad::Integer = 1) = digits(typeof(base), n, base = base, pad = pad) function digits(T::Type{<:Integer}, n::Integer; base::Integer = 10, pad::Integer = 1) digits!(zeros(T, ndigits(n, base=base, pad=pad)), n, base=base) end """ hastypemax(T::Type) -> Bool Return `true` if and only if the extrema `typemax(T)` and `typemin(T)` are defined. """ hastypemax(::Base.BitIntegerType) = true hastypemax(::Type{Bool}) = true hastypemax(::Type{T}) where {T} = applicable(typemax, T) && applicable(typemin, T) """ digits!(array, n::Integer; base::Integer = 10) Fills an array of the digits of `n` in the given base. More significant digits are at higher indices. If the array length is insufficient, the least significant digits are filled up to the array length. If the array length is excessive, the excess portion is filled with zeros. # Examples ```jldoctest julia> digits!([2, 2, 2, 2], 10, base = 2) 4-element Vector{Int64}: 0 1 0 1 julia> digits!([2, 2, 2, 2, 2, 2], 10, base = 2) 6-element Vector{Int64}: 0 1 0 1 0 0 ``` """ function digits!(a::AbstractVector{T}, n::Integer; base::Integer = 10) where T<:Integer 2 <= abs(base) || throw(DomainError(base, "base must be โ‰ฅ 2 or โ‰ค -2")) hastypemax(T) && abs(base) - 1 > typemax(T) && throw(ArgumentError("type $T too small for base $base")) isempty(a) && return a if base > 0 if ispow2(base) && n >= 0 && n isa Base.BitInteger && base <= typemax(Int) base = Int(base) k = trailing_zeros(base) c = base - 1 for i in eachindex(a) a[i] = (n >> (k * (i - firstindex(a)))) & c end else for i in eachindex(a) n, d = divrem(n, base) a[i] = d end end else # manually peel one loop iteration for type stability n, d = fldmod(n, -base) a[firstindex(a)] = d n = -signed(n) for i in firstindex(a)+1:lastindex(a) n, d = fldmod(n, -base) a[i] = d n = -n end end return a end """ isqrt(n::Integer) Integer square root: the largest integer `m` such that `m*m <= n`. ```jldoctest julia> isqrt(5) 2 ``` """ isqrt(x::Integer) = oftype(x, trunc(sqrt(x))) function isqrt(x::Union{Int64,UInt64,Int128,UInt128}) x==0 && return x s = oftype(x, trunc(sqrt(x))) # fix with a Newton iteration, since conversion to float discards # too many bits. s = (s + div(x,s)) >> 1 s*s > x ? s-1 : s end """ factorial(n::Integer) Factorial of `n`. If `n` is an [`Integer`](@ref), the factorial is computed as an integer (promoted to at least 64 bits). Note that this may overflow if `n` is not small, but you can use `factorial(big(n))` to compute the result exactly in arbitrary precision. See also [`binomial`](@ref). # Examples ```jldoctest julia> factorial(6) 720 julia> factorial(21) ERROR: OverflowError: 21 is too large to look up in the table; consider using `factorial(big(21))` instead Stacktrace: [...] julia> factorial(big(21)) 51090942171709440000 ``` # External links * [Factorial](https://en.wikipedia.org/wiki/Factorial) on Wikipedia. """ function factorial(n::Integer) n < 0 && throw(DomainError(n, "`n` must be nonnegative.")) f::typeof(n*n) = 1 for i::typeof(n*n) = 2:n f *= i end return f end """ binomial(n::Integer, k::Integer) The _binomial coefficient_ ``\\binom{n}{k}``, being the coefficient of the ``k``th term in the polynomial expansion of ``(1+x)^n``. If ``n`` is non-negative, then it is the number of ways to choose `k` out of `n` items: ```math \\binom{n}{k} = \\frac{n!}{k! (n-k)!} ``` where ``n!`` is the [`factorial`](@ref) function. If ``n`` is negative, then it is defined in terms of the identity ```math \\binom{n}{k} = (-1)^k \\binom{k-n-1}{k} ``` See also [`factorial`](@ref). # Examples ```jldoctest julia> binomial(5, 3) 10 julia> factorial(5) รท (factorial(5-3) * factorial(3)) 10 julia> binomial(-5, 3) -35 ``` # External links * [Binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient) on Wikipedia. """ Base.@assume_effects :terminates_locally function binomial(n::T, k::T) where T<:Integer n0, k0 = n, k k < 0 && return zero(T) sgn = one(T) if n < 0 n = -n + k - one(T) if isodd(k) sgn = -sgn end end k > n && return zero(T) (k == 0 || k == n) && return sgn k == 1 && return sgn*n if k > (n>>1) k = (n - k) end x = nn = n - k + one(T) nn += one(T) rr = T(2) while rr <= k xt = div(widemul(x, nn), rr) x = xt % T x == xt || throw(OverflowError(LazyString("binomial(", n0, ", ", k0, ") overflows"))) rr += one(T) nn += one(T) end copysign(x, sgn) end """ binomial(x::Number, k::Integer) The generalized binomial coefficient, defined for `k โ‰ฅ 0` by the polynomial ```math \\frac{1}{k!} \\prod_{j=0}^{k-1} (x - j) ``` When `k < 0` it returns zero. For the case of integer `x`, this is equivalent to the ordinary integer binomial coefficient ```math \\binom{n}{k} = \\frac{n!}{k! (n-k)!} ``` Further generalizations to non-integer `k` are mathematically possible, but involve the Gamma function and/or the beta function, which are not provided by the Julia standard library but are available in external packages such as [SpecialFunctions.jl](https://github.com/JuliaMath/SpecialFunctions.jl). # External links * [Binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient) on Wikipedia. """ function binomial(x::Number, k::Integer) k < 0 && return zero(x)/one(k) # we don't use prod(i -> (x-i+1), 1:k) / factorial(k), # and instead divide each term by i, to avoid spurious overflow. return prod(i -> (x-(i-1))/i, OneTo(k), init=oneunit(x)/one(k)) end V/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/strings.jlฌ# This file is a part of Julia. License is MIT: https://julialang.org/license include("strings/search.jl") include("strings/unicode.jl") import .Unicode: textwidth, islowercase, isuppercase, isletter, isdigit, isnumeric, iscntrl, ispunct, isspace, isprint, isxdigit, lowercase, uppercase, titlecase, lowercasefirst, uppercasefirst import .Iterators: PartitionIterator include("strings/util.jl") include("strings/io.jl") U/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/search.jlN# This file is a part of Julia. License is MIT: https://julialang.org/license """ An abstract type representing any sort of pattern matching expression (typically a regular expression). `AbstractPattern` objects can be used to match strings with [`match`](@ref). !!! compat "Julia 1.6" This type is available in Julia 1.6 and later. """ abstract type AbstractPattern end nothing_sentinel(i) = i == 0 ? nothing : i function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar}, s::String, i::Integer) if i < 1 || i > sizeof(s) i == sizeof(s) + 1 && return nothing throw(BoundsError(s, i)) end @inbounds isvalid(s, i) || string_index_err(s, i) c = pred.x c โ‰ค '\x7f' && return nothing_sentinel(_search(s, c % UInt8, i)) while true i = _search(s, first_utf8_byte(c), i) i == 0 && return nothing pred(s[i]) && return i i = nextind(s, i) end end findfirst(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = nothing_sentinel(_search(a, pred.x)) findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = nothing_sentinel(_search(a, pred.x, i)) findfirst(::typeof(iszero), a::ByteArray) = nothing_sentinel(_search(a, zero(UInt8))) findnext(::typeof(iszero), a::ByteArray, i::Integer) = nothing_sentinel(_search(a, zero(UInt8), i)) function _search(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = 1) if i < 1 throw(BoundsError(a, i)) end n = sizeof(a) if i > n return i == n+1 ? 0 : throw(BoundsError(a, i)) end p = pointer(a) q = GC.@preserve a ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-1, b, n-i+1) return q == C_NULL ? 0 : Int(q-p+1) end function _search(a::ByteArray, b::AbstractChar, i::Integer = 1) if isascii(b) _search(a,UInt8(b),i) else _search(a,codeunits(string(b)),i).start end end function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar}, s::String, i::Integer) c = pred.x c โ‰ค '\x7f' && return nothing_sentinel(_rsearch(s, c % UInt8, i)) b = first_utf8_byte(c) while true i = _rsearch(s, b, i) i == 0 && return nothing pred(s[i]) && return i i = prevind(s, i) end end findlast(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = nothing_sentinel(_rsearch(a, pred.x)) findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = nothing_sentinel(_rsearch(a, pred.x, i)) findlast(::typeof(iszero), a::ByteArray) = nothing_sentinel(_rsearch(a, zero(UInt8))) findprev(::typeof(iszero), a::ByteArray, i::Integer) = nothing_sentinel(_rsearch(a, zero(UInt8), i)) function _rsearch(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = sizeof(a)) if i < 1 return i == 0 ? 0 : throw(BoundsError(a, i)) end n = sizeof(a) if i > n return i == n+1 ? 0 : throw(BoundsError(a, i)) end p = pointer(a) q = GC.@preserve a ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i) return q == C_NULL ? 0 : Int(q-p+1) end function _rsearch(a::ByteArray, b::AbstractChar, i::Integer = length(a)) if isascii(b) _rsearch(a,UInt8(b),i) else _rsearch(a,codeunits(string(b)),i).start end end """ findfirst(pattern::AbstractString, string::AbstractString) findfirst(pattern::AbstractPattern, string::String) Find the first occurrence of `pattern` in `string`. Equivalent to [`findnext(pattern, string, firstindex(s))`](@ref). # Examples ```jldoctest julia> findfirst("z", "Hello to the world") # returns nothing, but not printed in the REPL julia> findfirst("Julia", "JuliaLang") 1:5 ``` """ findfirst(pattern::AbstractString, string::AbstractString) = findnext(pattern, string, firstindex(string)) """ findfirst(ch::AbstractChar, string::AbstractString) Find the first occurrence of character `ch` in `string`. !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> findfirst('a', "happy") 2 julia> findfirst('z', "happy") === nothing true ``` """ findfirst(ch::AbstractChar, string::AbstractString) = findfirst(==(ch), string) """ findfirst(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}) Find the first occurrence of sequence `pattern` in vector `A`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> findfirst([0x52, 0x62], [0x40, 0x52, 0x62, 0x63]) 2:3 ``` """ findfirst(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}) = _search(A, pattern, firstindex(A)) # AbstractString implementation of the generic findnext interface function findnext(testf::Function, s::AbstractString, i::Integer) i = Int(i) z = ncodeunits(s) + 1 1 โ‰ค i โ‰ค z || throw(BoundsError(s, i)) @inbounds i == z || isvalid(s, i) || string_index_err(s, i) e = lastindex(s) while i <= e testf(@inbounds s[i]) && return i i = @inbounds nextind(s, i) end return nothing end in(c::AbstractChar, s::AbstractString) = (findfirst(isequal(c),s)!==nothing) function _searchindex(s::Union{AbstractString,ByteArray}, t::Union{AbstractString,AbstractChar,Int8,UInt8}, i::Integer) x = Iterators.peel(t) if isnothing(x) return 1 <= i <= nextind(s,lastindex(s))::Int ? i : throw(BoundsError(s, i)) end t1, trest = x while true i = findnext(isequal(t1),s,i) if i === nothing return 0 end ii = nextind(s, i)::Int a = Iterators.Stateful(trest) matched = all(splat(==), zip(SubString(s, ii), a)) (isempty(a) && matched) && return i i = ii end end _searchindex(s::AbstractString, t::AbstractChar, i::Integer) = something(findnext(isequal(t), s, i), 0) function _search_bloom_mask(c) UInt64(1) << (c & 63) end _nthbyte(s::String, i) = codeunit(s, i) _nthbyte(t::AbstractVector, index) = t[index + (firstindex(t)-1)] function _searchindex(s::String, t::String, i::Integer) # Check for fast case of a single byte lastindex(t) == 1 && return something(findnext(isequal(t[1]), s, i), 0) _searchindex(codeunits(s), codeunits(t), i) end function _searchindex(s::AbstractVector{<:Union{Int8,UInt8}}, t::AbstractVector{<:Union{Int8,UInt8}}, _i::Integer) sentinel = firstindex(s) - 1 n = length(t) m = length(s) i = Int(_i) - sentinel (i < 1 || i > m+1) && throw(BoundsError(s, _i)) if n == 0 return 1 <= i <= m+1 ? max(1, i) : sentinel elseif m == 0 return sentinel elseif n == 1 return something(findnext(isequal(_nthbyte(t,1)), s, i), sentinel) end w = m - n if w < 0 || i - 1 > w return sentinel end bloom_mask = UInt64(0) skip = n - 1 tlast = _nthbyte(t,n) for j in 1:n bloom_mask |= _search_bloom_mask(_nthbyte(t,j)) if _nthbyte(t,j) == tlast && j < n skip = n - j - 1 end end i -= 1 while i <= w if _nthbyte(s,i+n) == tlast # check candidate j = 0 while j < n - 1 if _nthbyte(s,i+j+1) != _nthbyte(t,j+1) break end j += 1 end # match found if j == n - 1 # restore in case `s` is an OffSetArray return i+firstindex(s) end # no match, try to rule out the next character if i < w && bloom_mask & _search_bloom_mask(_nthbyte(s,i+n+1)) == 0 i += n else i += skip end elseif i < w if bloom_mask & _search_bloom_mask(_nthbyte(s,i+n+1)) == 0 i += n end end i += 1 end sentinel end function _search(s::Union{AbstractString,AbstractVector{<:Union{Int8,UInt8}}}, t::Union{AbstractString,AbstractChar,AbstractVector{<:Union{Int8,UInt8}}}, i::Integer) idx = _searchindex(s,t,i) if isempty(t) idx:idx-1 elseif idx >= firstindex(s) idx:(idx + lastindex(t) - 1) else nothing end end """ findnext(pattern::AbstractString, string::AbstractString, start::Integer) findnext(pattern::AbstractPattern, string::String, start::Integer) Find the next occurrence of `pattern` in `string` starting at position `start`. `pattern` can be either a string, or a regular expression, in which case `string` must be of type `String`. The return value is a range of indices where the matching sequence is found, such that `s[findnext(x, s, i)] == x`: `findnext("substring", string, i)` == `start:stop` such that `string[start:stop] == "substring"` and `i <= start`, or `nothing` if unmatched. # Examples ```jldoctest julia> findnext("z", "Hello to the world", 1) === nothing true julia> findnext("o", "Hello to the world", 6) 8:8 julia> findnext("Lang", "JuliaLang", 2) 6:9 ``` """ findnext(t::AbstractString, s::AbstractString, start::Integer) = _search(s, t, Int(start)) """ findnext(ch::AbstractChar, string::AbstractString, start::Integer) Find the next occurrence of character `ch` in `string` starting at position `start`. !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> findnext('z', "Hello to the world", 1) === nothing true julia> findnext('o', "Hello to the world", 6) 8 ``` """ findnext(ch::AbstractChar, string::AbstractString, start::Integer) = findnext(==(ch), string, start) """ findnext(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}, start::Integer) Find the next occurrence of the sequence `pattern` in vector `A` starting at position `start`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> findnext([0x52, 0x62], [0x52, 0x62, 0x72], 3) === nothing true julia> findnext([0x52, 0x62], [0x40, 0x52, 0x62, 0x52, 0x62], 3) 4:5 ``` """ findnext(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}, start::Integer) = _search(A, pattern, start) """ findlast(pattern::AbstractString, string::AbstractString) Find the last occurrence of `pattern` in `string`. Equivalent to [`findprev(pattern, string, lastindex(string))`](@ref). # Examples ```jldoctest julia> findlast("o", "Hello to the world") 15:15 julia> findfirst("Julia", "JuliaLang") 1:5 ``` """ findlast(pattern::AbstractString, string::AbstractString) = findprev(pattern, string, lastindex(string)) """ findlast(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}) Find the last occurrence of `pattern` in array `A`. Equivalent to [`findprev(pattern, A, lastindex(A))`](@ref). # Examples ```jldoctest julia> findlast([0x52, 0x62], [0x52, 0x62, 0x52, 0x62]) 3:4 ``` """ findlast(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}) = findprev(pattern, A, lastindex(A)) """ findlast(ch::AbstractChar, string::AbstractString) Find the last occurrence of character `ch` in `string`. !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> findlast('p', "happy") 4 julia> findlast('z', "happy") === nothing true ``` """ findlast(ch::AbstractChar, string::AbstractString) = findlast(==(ch), string) """ findall( pattern::Union{AbstractString,AbstractPattern}, string::AbstractString; overlap::Bool = false, ) findall( pattern::Vector{UInt8} A::Vector{UInt8}; overlap::Bool = false, ) Return a `Vector{UnitRange{Int}}` of all the matches for `pattern` in `string`. Each element of the returned vector is a range of indices where the matching sequence is found, like the return value of [`findnext`](@ref). If `overlap=true`, the matching sequences are allowed to overlap indices in the original string, otherwise they must be from disjoint character ranges. # Examples ```jldoctest julia> findall("a", "apple") 1-element Vector{UnitRange{Int64}}: 1:1 julia> findall("nana", "banana") 1-element Vector{UnitRange{Int64}}: 3:6 julia> findall("a", "banana") 3-element Vector{UnitRange{Int64}}: 2:2 4:4 6:6 julia> findall(UInt8[1,2], UInt8[1,2,3,1,2]) 2-element Vector{UnitRange{Int64}}: 1:2 4:5 ``` !!! compat "Julia 1.3" This method requires at least Julia 1.3. """ function findall(t::Union{AbstractString, AbstractPattern, AbstractVector{<:Union{Int8,UInt8}}}, s::Union{AbstractString, AbstractPattern, AbstractVector{<:Union{Int8,UInt8}}}, ; overlap::Bool=false) found = UnitRange{Int}[] i, e = firstindex(s), lastindex(s) while true r = findnext(t, s, i) isnothing(r) && break push!(found, r) j = overlap || isempty(r) ? first(r) : last(r) j > e && break @inbounds i = nextind(s, j) end return found end # AbstractString implementation of the generic findprev interface function findprev(testf::Function, s::AbstractString, i::Integer) i = Int(i) z = ncodeunits(s) + 1 0 โ‰ค i โ‰ค z || throw(BoundsError(s, i)) i == z && return nothing @inbounds i == 0 || isvalid(s, i) || string_index_err(s, i) while i >= 1 testf(@inbounds s[i]) && return i i = @inbounds prevind(s, i) end return nothing end function _rsearchindex(s::AbstractString, t::Union{AbstractString,AbstractChar,Int8,UInt8}, i::Integer) if isempty(t) return 1 <= i <= nextind(s, lastindex(s))::Int ? i : throw(BoundsError(s, i)) end t1, trest = Iterators.peel(Iterators.reverse(t))::NTuple{2,Any} while true i = findprev(isequal(t1), s, i) i === nothing && return 0 ii = prevind(s, i)::Int a = Iterators.Stateful(trest) b = Iterators.Stateful(Iterators.reverse( pairs(SubString(s, 1, ii)))) matched = all(splat(==), zip(a, (x[2] for x in b))) if matched && isempty(a) isempty(b) && return firstindex(s) return nextind(s, popfirst!(b)[1])::Int end i = ii end end function _rsearchindex(s::String, t::String, i::Integer) # Check for fast case of a single byte if lastindex(t) == 1 return something(findprev(isequal(t[1]), s, i), 0) elseif lastindex(t) != 0 j = i โ‰ค ncodeunits(s) ? nextind(s, i)-1 : i return _rsearchindex(codeunits(s), codeunits(t), j) elseif i > sizeof(s) return 0 elseif i == 0 return 1 else return i end end function _rsearchindex(s::AbstractVector{<:Union{Int8,UInt8}}, t::AbstractVector{<:Union{Int8,UInt8}}, _k::Integer) sentinel = firstindex(s) - 1 n = length(t) m = length(s) k = Int(_k) - sentinel k < 0 && throw(BoundsError(s, _k)) if n == 0 return 0 <= k <= m ? max(k, 1) : sentinel elseif m == 0 return sentinel elseif n == 1 return something(findprev(isequal(_nthbyte(t,1)), s, k), sentinel) end w = m - n if w < 0 || k <= 0 return sentinel end bloom_mask = UInt64(0) skip = n - 1 tfirst = _nthbyte(t,1) for j in n:-1:1 bloom_mask |= _search_bloom_mask(_nthbyte(t,j)) if _nthbyte(t,j) == tfirst && j > 1 skip = j - 2 end end i = min(k - n + 1, w + 1) while i > 0 if _nthbyte(s,i) == tfirst # check candidate j = 1 while j < n if _nthbyte(s,i+j) != _nthbyte(t,j+1) break end j += 1 end # match found, restore in case `s` is an OffsetArray if j == n return i + sentinel end # no match, try to rule out the next character if i > 1 && bloom_mask & _search_bloom_mask(_nthbyte(s,i-1)) == 0 i -= n else i -= skip end elseif i > 1 if bloom_mask & _search_bloom_mask(_nthbyte(s,i-1)) == 0 i -= n end end i -= 1 end sentinel end function _rsearch(s::Union{AbstractString,AbstractVector{<:Union{Int8,UInt8}}}, t::Union{AbstractString,AbstractChar,AbstractVector{<:Union{Int8,UInt8}}}, i::Integer) idx = _rsearchindex(s,t,i) if isempty(t) idx:idx-1 elseif idx > firstindex(s) - 1 idx:(idx + lastindex(t) - 1) else nothing end end """ findprev(pattern::AbstractString, string::AbstractString, start::Integer) Find the previous occurrence of `pattern` in `string` starting at position `start`. The return value is a range of indices where the matching sequence is found, such that `s[findprev(x, s, i)] == x`: `findprev("substring", string, i)` == `start:stop` such that `string[start:stop] == "substring"` and `stop <= i`, or `nothing` if unmatched. # Examples ```jldoctest julia> findprev("z", "Hello to the world", 18) === nothing true julia> findprev("o", "Hello to the world", 18) 15:15 julia> findprev("Julia", "JuliaLang", 6) 1:5 ``` """ findprev(t::AbstractString, s::AbstractString, i::Integer) = _rsearch(s, t, Int(i)) """ findprev(ch::AbstractChar, string::AbstractString, start::Integer) Find the previous occurrence of character `ch` in `string` starting at position `start`. !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> findprev('z', "Hello to the world", 18) === nothing true julia> findprev('o', "Hello to the world", 18) 15 ``` """ findprev(ch::AbstractChar, string::AbstractString, start::Integer) = findprev(==(ch), string, start) """ findprev(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}, start::Integer) Find the previous occurrence of the sequence `pattern` in vector `A` starting at position `start`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. # Examples ```jldoctest julia> findprev([0x52, 0x62], [0x40, 0x52, 0x62, 0x52, 0x62], 3) 2:3 ``` """ findprev(pattern::AbstractVector{<:Union{Int8,UInt8}}, A::AbstractVector{<:Union{Int8,UInt8}}, start::Integer) = _rsearch(A, pattern, start) """ occursin(needle::Union{AbstractString,AbstractPattern,AbstractChar}, haystack::AbstractString) Determine whether the first argument is a substring of the second. If `needle` is a regular expression, checks whether `haystack` contains a match. # Examples ```jldoctest julia> occursin("Julia", "JuliaLang is pretty cool!") true julia> occursin('a', "JuliaLang is pretty cool!") true julia> occursin(r"a.a", "aba") true julia> occursin(r"a.a", "abba") false ``` See also [`contains`](@ref). """ occursin(needle::Union{AbstractString,AbstractChar}, haystack::AbstractString) = _searchindex(haystack, needle, firstindex(haystack)) != 0 """ occursin(haystack) Create a function that checks whether its argument occurs in `haystack`, i.e. a function equivalent to `needle -> occursin(needle, haystack)`. The returned function is of type `Base.Fix2{typeof(occursin)}`. !!! compat "Julia 1.6" This method requires Julia 1.6 or later. # Examples ```jldoctest julia> search_f = occursin("JuliaLang is a programming language"); julia> search_f("JuliaLang") true julia> search_f("Python") false ``` """ occursin(haystack) = Base.Fix2(occursin, haystack) in(::AbstractString, ::AbstractString) = error("use occursin(needle, haystack) for string containment") V/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/unicode.jlฃR# This file is a part of Julia. License is MIT: https://julialang.org/license # Various Unicode functionality from the utf8proc library module Unicode import Base: show, ==, hash, string, Symbol, isless, length, eltype, convert, isvalid, ismalformed, isoverlong, iterate # whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff """ isvalid(value) -> Bool Return `true` if the given value is valid for its type, which currently can be either `AbstractChar` or `String` or `SubString{String}`. # Examples ```jldoctest julia> isvalid(Char(0xd800)) false julia> isvalid(SubString(String(UInt8[0xfe,0x80,0x80,0x80,0x80,0x80]),1,2)) false julia> isvalid(Char(0xd799)) true ``` """ isvalid(value) """ isvalid(T, value) -> Bool Return `true` if the given value is valid for that type. Types currently can be either `AbstractChar` or `String`. Values for `AbstractChar` can be of type `AbstractChar` or [`UInt32`](@ref). Values for `String` can be of that type, `SubString{String}`, `Vector{UInt8}`, or a contiguous subarray thereof. # Examples ```jldoctest julia> isvalid(Char, 0xd800) false julia> isvalid(String, SubString("thisisvalid",1,5)) true julia> isvalid(Char, 0xd799) true ``` !!! compat "Julia 1.6" Support for subarray values was added in Julia 1.6. """ isvalid(T,value) isvalid(c::AbstractChar) = !ismalformed(c) & !isoverlong(c) & ((c โ‰ค '\ud7ff') | ('\ue000' โ‰ค c) & (c โ‰ค '\U10ffff')) isvalid(::Type{<:AbstractChar}, c::Unsigned) = ((c โ‰ค 0xd7ff ) | ( 0xe000 โ‰ค c) & (c โ‰ค 0x10ffff )) isvalid(::Type{T}, c::Integer) where {T<:AbstractChar} = isvalid(T, Unsigned(c)) isvalid(::Type{<:AbstractChar}, c::AbstractChar) = isvalid(c) # utf8 category constants const UTF8PROC_CATEGORY_CN = 0 const UTF8PROC_CATEGORY_LU = 1 const UTF8PROC_CATEGORY_LL = 2 const UTF8PROC_CATEGORY_LT = 3 const UTF8PROC_CATEGORY_LM = 4 const UTF8PROC_CATEGORY_LO = 5 const UTF8PROC_CATEGORY_MN = 6 const UTF8PROC_CATEGORY_MC = 7 const UTF8PROC_CATEGORY_ME = 8 const UTF8PROC_CATEGORY_ND = 9 const UTF8PROC_CATEGORY_NL = 10 const UTF8PROC_CATEGORY_NO = 11 const UTF8PROC_CATEGORY_PC = 12 const UTF8PROC_CATEGORY_PD = 13 const UTF8PROC_CATEGORY_PS = 14 const UTF8PROC_CATEGORY_PE = 15 const UTF8PROC_CATEGORY_PI = 16 const UTF8PROC_CATEGORY_PF = 17 const UTF8PROC_CATEGORY_PO = 18 const UTF8PROC_CATEGORY_SM = 19 const UTF8PROC_CATEGORY_SC = 20 const UTF8PROC_CATEGORY_SK = 21 const UTF8PROC_CATEGORY_SO = 22 const UTF8PROC_CATEGORY_ZS = 23 const UTF8PROC_CATEGORY_ZL = 24 const UTF8PROC_CATEGORY_ZP = 25 const UTF8PROC_CATEGORY_CC = 26 const UTF8PROC_CATEGORY_CF = 27 const UTF8PROC_CATEGORY_CS = 28 const UTF8PROC_CATEGORY_CO = 29 # strings corresponding to the category constants const category_strings = [ "Other, not assigned", "Letter, uppercase", "Letter, lowercase", "Letter, titlecase", "Letter, modifier", "Letter, other", "Mark, nonspacing", "Mark, spacing combining", "Mark, enclosing", "Number, decimal digit", "Number, letter", "Number, other", "Punctuation, connector", "Punctuation, dash", "Punctuation, open", "Punctuation, close", "Punctuation, initial quote", "Punctuation, final quote", "Punctuation, other", "Symbol, math", "Symbol, currency", "Symbol, modifier", "Symbol, other", "Separator, space", "Separator, line", "Separator, paragraph", "Other, control", "Other, format", "Other, surrogate", "Other, private use", "Invalid, too high", "Malformed, bad data", ] const UTF8PROC_STABLE = (1<<1) const UTF8PROC_COMPAT = (1<<2) const UTF8PROC_COMPOSE = (1<<3) const UTF8PROC_DECOMPOSE = (1<<4) const UTF8PROC_IGNORE = (1<<5) const UTF8PROC_REJECTNA = (1<<6) const UTF8PROC_NLF2LS = (1<<7) const UTF8PROC_NLF2PS = (1<<8) const UTF8PROC_NLF2LF = (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS) const UTF8PROC_STRIPCC = (1<<9) const UTF8PROC_CASEFOLD = (1<<10) const UTF8PROC_CHARBOUND = (1<<11) const UTF8PROC_LUMP = (1<<12) const UTF8PROC_STRIPMARK = (1<<13) ############################################################################ utf8proc_error(result) = error(unsafe_string(ccall(:utf8proc_errmsg, Cstring, (Cssize_t,), result))) # static wrapper around user callback function utf8proc_custom_func(codepoint::UInt32, callback::Any) = UInt32(callback(codepoint))::UInt32 function utf8proc_decompose(str, options, buffer, nwords, chartransform::typeof(identity)) ret = ccall(:utf8proc_decompose, Int, (Ptr{UInt8}, Int, Ptr{UInt8}, Int, Cint), str, sizeof(str), buffer, nwords, options) ret < 0 && utf8proc_error(ret) return ret end function utf8proc_decompose(str, options, buffer, nwords, chartransform::T) where T ret = ccall(:utf8proc_decompose_custom, Int, (Ptr{UInt8}, Int, Ptr{UInt8}, Int, Cint, Ptr{Cvoid}, Ref{T}), str, sizeof(str), buffer, nwords, options, @cfunction(utf8proc_custom_func, UInt32, (UInt32, Ref{T})), chartransform) ret < 0 && utf8proc_error(ret) return ret end function utf8proc_map(str::Union{String,SubString{String}}, options::Integer, chartransform=identity) nwords = utf8proc_decompose(str, options, C_NULL, 0, chartransform) buffer = Base.StringVector(nwords*4) nwords = utf8proc_decompose(str, options, buffer, nwords, chartransform) nbytes = ccall(:utf8proc_reencode, Int, (Ptr{UInt8}, Int, Cint), buffer, nwords, options) nbytes < 0 && utf8proc_error(nbytes) return String(resize!(buffer, nbytes)) end # from julia_charmap.h, used by julia_chartransform in the Unicode stdlib const _julia_charmap = Dict{UInt32,UInt32}( 0x025B => 0x03B5, 0x00B5 => 0x03BC, 0x00B7 => 0x22C5, 0x0387 => 0x22C5, 0x2212 => 0x002D, 0x210F => 0x0127, ) utf8proc_map(s::AbstractString, flags::Integer, chartransform=identity) = utf8proc_map(String(s), flags, chartransform) # Documented in Unicode module function normalize( s::AbstractString; stable::Bool=false, compat::Bool=false, compose::Bool=true, decompose::Bool=false, stripignore::Bool=false, rejectna::Bool=false, newline2ls::Bool=false, newline2ps::Bool=false, newline2lf::Bool=false, stripcc::Bool=false, casefold::Bool=false, lump::Bool=false, stripmark::Bool=false, chartransform=identity, ) flags = 0 stable && (flags = flags | UTF8PROC_STABLE) compat && (flags = flags | UTF8PROC_COMPAT) # TODO: error if compose & decompose? if decompose flags = flags | UTF8PROC_DECOMPOSE elseif compose flags = flags | UTF8PROC_COMPOSE elseif compat || stripmark throw(ArgumentError("compat=true or stripmark=true require compose=true or decompose=true")) end stripignore && (flags = flags | UTF8PROC_IGNORE) rejectna && (flags = flags | UTF8PROC_REJECTNA) newline2ls + newline2ps + newline2lf > 1 && throw(ArgumentError("only one newline conversion may be specified")) newline2ls && (flags = flags | UTF8PROC_NLF2LS) newline2ps && (flags = flags | UTF8PROC_NLF2PS) newline2lf && (flags = flags | UTF8PROC_NLF2LF) stripcc && (flags = flags | UTF8PROC_STRIPCC) casefold && (flags = flags | UTF8PROC_CASEFOLD) lump && (flags = flags | UTF8PROC_LUMP) stripmark && (flags = flags | UTF8PROC_STRIPMARK) utf8proc_map(s, flags, chartransform) end function normalize(s::AbstractString, nf::Symbol) utf8proc_map(s, nf === :NFC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE) : nf === :NFD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE) : nf === :NFKC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_COMPAT) : nf === :NFKD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT) : throw(ArgumentError(":$nf is not one of :NFC, :NFD, :NFKC, :NFKD"))) end ############################################################################ ## character column width function ## """ textwidth(c) Give the number of columns needed to print a character. # Examples ```jldoctest julia> textwidth('ฮฑ') 1 julia> textwidth('โ›ต') 2 ``` """ function textwidth(c::AbstractChar) ismalformed(c) && return 1 Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), c)) end """ textwidth(s::AbstractString) Give the number of columns needed to print a string. # Examples ```jldoctest julia> textwidth("March") 5 ``` """ textwidth(s::AbstractString) = mapreduce(textwidth, +, s; init=0) """ lowercase(c::AbstractChar) Convert `c` to lowercase. See also [`uppercase`](@ref), [`titlecase`](@ref). # Examples ```jldoctest julia> lowercase('A') 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> lowercase('ร–') 'รถ': Unicode U+00F6 (category Ll: Letter, lowercase) ``` """ lowercase(c::T) where {T<:AbstractChar} = isascii(c) ? ('A' <= c <= 'Z' ? c + 0x20 : c) : T(ccall(:utf8proc_tolower, UInt32, (UInt32,), c)) """ uppercase(c::AbstractChar) Convert `c` to uppercase. See also [`lowercase`](@ref), [`titlecase`](@ref). # Examples ```jldoctest julia> uppercase('a') 'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase) julia> uppercase('รช') 'รŠ': Unicode U+00CA (category Lu: Letter, uppercase) ``` """ uppercase(c::T) where {T<:AbstractChar} = isascii(c) ? ('a' <= c <= 'z' ? c - 0x20 : c) : T(ccall(:utf8proc_toupper, UInt32, (UInt32,), c)) """ titlecase(c::AbstractChar) Convert `c` to titlecase. This may differ from uppercase for digraphs, compare the example below. See also [`uppercase`](@ref), [`lowercase`](@ref). # Examples ```jldoctest julia> titlecase('a') 'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase) julia> titlecase('ว†') 'ว…': Unicode U+01C5 (category Lt: Letter, titlecase) julia> uppercase('ว†') 'ว„': Unicode U+01C4 (category Lu: Letter, uppercase) ``` """ titlecase(c::T) where {T<:AbstractChar} = isascii(c) ? ('a' <= c <= 'z' ? c - 0x20 : c) : T(ccall(:utf8proc_totitle, UInt32, (UInt32,), c)) ############################################################################ # returns UTF8PROC_CATEGORY code in 0:30 giving Unicode category function category_code(c::AbstractChar) !ismalformed(c) ? category_code(UInt32(c)) : Cint(31) end function category_code(x::Integer) x โ‰ค 0x10ffff ? ccall(:utf8proc_category, Cint, (UInt32,), x) : Cint(30) end # more human-readable representations of the category code function category_abbrev(c::AbstractChar) ismalformed(c) && return "Ma" c โ‰ค '\U10ffff' || return "In" unsafe_string(ccall(:utf8proc_category_string, Cstring, (UInt32,), c)) end category_string(c) = category_strings[category_code(c)+1] isassigned(c) = UTF8PROC_CATEGORY_CN < category_code(c) <= UTF8PROC_CATEGORY_CO ## libc character class predicates ## """ islowercase(c::AbstractChar) -> Bool Tests whether a character is a lowercase letter (according to the Unicode standard's `Lowercase` derived property). See also [`isuppercase`](@ref). # Examples ```jldoctest julia> islowercase('ฮฑ') true julia> islowercase('ฮ“') false julia> islowercase('โค') false ``` """ islowercase(c::AbstractChar) = ismalformed(c) ? false : Bool(ccall(:utf8proc_islower, Cint, (UInt32,), UInt32(c))) # true for Unicode upper and mixed case """ isuppercase(c::AbstractChar) -> Bool Tests whether a character is an uppercase letter (according to the Unicode standard's `Uppercase` derived property). See also [`islowercase`](@ref). # Examples ```jldoctest julia> isuppercase('ฮณ') false julia> isuppercase('ฮ“') true julia> isuppercase('โค') false ``` """ isuppercase(c::AbstractChar) = ismalformed(c) ? false : Bool(ccall(:utf8proc_isupper, Cint, (UInt32,), UInt32(c))) """ iscased(c::AbstractChar) -> Bool Tests whether a character is cased, i.e. is lower-, upper- or title-cased. See also [`islowercase`](@ref), [`isuppercase`](@ref). """ function iscased(c::AbstractChar) cat = category_code(c) return cat == UTF8PROC_CATEGORY_LU || cat == UTF8PROC_CATEGORY_LT || cat == UTF8PROC_CATEGORY_LL end """ isdigit(c::AbstractChar) -> Bool Tests whether a character is a decimal digit (0-9). See also: [`isletter`](@ref). # Examples ```jldoctest julia> isdigit('โค') false julia> isdigit('9') true julia> isdigit('ฮฑ') false ``` """ isdigit(c::AbstractChar) = (c >= '0') & (c <= '9') """ isletter(c::AbstractChar) -> Bool Test whether a character is a letter. A character is classified as a letter if it belongs to the Unicode general category Letter, i.e. a character whose category code begins with 'L'. See also: [`isdigit`](@ref). # Examples ```jldoctest julia> isletter('โค') false julia> isletter('ฮฑ') true julia> isletter('9') false ``` """ isletter(c::AbstractChar) = UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_LO """ isnumeric(c::AbstractChar) -> Bool Tests whether a character is numeric. A character is classified as numeric if it belongs to the Unicode general category Number, i.e. a character whose category code begins with 'N'. Note that this broad category includes characters such as ยพ and เฏฐ. Use [`isdigit`](@ref) to check whether a character is a decimal digit between 0 and 9. # Examples ```jldoctest julia> isnumeric('เฏฐ') true julia> isnumeric('9') true julia> isnumeric('ฮฑ') false julia> isnumeric('โค') false ``` """ isnumeric(c::AbstractChar) = UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGORY_NO # following C++ only control characters from the Latin-1 subset return true """ iscntrl(c::AbstractChar) -> Bool Tests whether a character is a control character. Control characters are the non-printing characters of the Latin-1 subset of Unicode. # Examples ```jldoctest julia> iscntrl('\\x01') true julia> iscntrl('a') false ``` """ iscntrl(c::AbstractChar) = c <= '\x1f' || '\x7f' <= c <= '\u9f' """ ispunct(c::AbstractChar) -> Bool Tests whether a character belongs to the Unicode general category Punctuation, i.e. a character whose category code begins with 'P'. # Examples ```jldoctest julia> ispunct('ฮฑ') false julia> ispunct('/') true julia> ispunct(';') true ``` """ ispunct(c::AbstractChar) = UTF8PROC_CATEGORY_PC <= category_code(c) <= UTF8PROC_CATEGORY_PO # \u85 is the Unicode Next Line (NEL) character """ isspace(c::AbstractChar) -> Bool Tests whether a character is any whitespace character. Includes ASCII characters '\\t', '\\n', '\\v', '\\f', '\\r', and ' ', Latin-1 character U+0085, and characters in Unicode category Zs. # Examples ```jldoctest julia> isspace('\\n') true julia> isspace('\\r') true julia> isspace(' ') true julia> isspace('\\x20') true ``` """ @inline isspace(c::AbstractChar) = c == ' ' || '\t' <= c <= '\r' || c == '\u85' || '\ua0' <= c && category_code(c) == UTF8PROC_CATEGORY_ZS """ isprint(c::AbstractChar) -> Bool Tests whether a character is printable, including spaces, but not a control character. # Examples ```jldoctest julia> isprint('\\x01') false julia> isprint('A') true ``` """ isprint(c::AbstractChar) = UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_ZS # true in principal if a printer would use ink """ isxdigit(c::AbstractChar) -> Bool Test whether a character is a valid hexadecimal digit. Note that this does not include `x` (as in the standard `0x` prefix). # Examples ```jldoctest julia> isxdigit('a') true julia> isxdigit('x') false ``` """ isxdigit(c::AbstractChar) = '0'<=c<='9' || 'a'<=c<='f' || 'A'<=c<='F' ## uppercase, lowercase, and titlecase transformations ## """ uppercase(s::AbstractString) Return `s` with all characters converted to uppercase. See also [`lowercase`](@ref), [`titlecase`](@ref), [`uppercasefirst`](@ref). # Examples ```jldoctest julia> uppercase("Julia") "JULIA" ``` """ uppercase(s::AbstractString) = map(uppercase, s) """ lowercase(s::AbstractString) Return `s` with all characters converted to lowercase. See also [`uppercase`](@ref), [`titlecase`](@ref), [`lowercasefirst`](@ref). # Examples ```jldoctest julia> lowercase("STRINGS AND THINGS") "strings and things" ``` """ lowercase(s::AbstractString) = map(lowercase, s) """ titlecase(s::AbstractString; [wordsep::Function], strict::Bool=true) -> String Capitalize the first character of each word in `s`; if `strict` is true, every other character is converted to lowercase, otherwise they are left unchanged. By default, all non-letters beginning a new grapheme are considered as word separators; a predicate can be passed as the `wordsep` keyword to determine which characters should be considered as word separators. See also [`uppercasefirst`](@ref) to capitalize only the first character in `s`. See also [`uppercase`](@ref), [`lowercase`](@ref), [`uppercasefirst`](@ref). # Examples ```jldoctest julia> titlecase("the JULIA programming language") "The Julia Programming Language" julia> titlecase("ISS - international space station", strict=false) "ISS - International Space Station" julia> titlecase("a-a b-b", wordsep = c->c==' ') "A-a B-b" ``` """ function titlecase(s::AbstractString; wordsep::Function = !isletter, strict::Bool=true) startword = true state = Ref{Int32}(0) c0 = eltype(s)(0x00000000) b = IOBuffer() for c in s # Note: It would be better to have a word iterator following UAX#29, # similar to our grapheme iterator, but utf8proc does not yet have # this information. At the very least we shouldn't break inside graphemes. if isgraphemebreak!(state, c0, c) && wordsep(c) print(b, c) startword = true else print(b, startword ? titlecase(c) : strict ? lowercase(c) : c) startword = false end c0 = c end return String(take!(b)) end """ uppercasefirst(s::AbstractString) -> String Return `s` with the first character converted to uppercase (technically "title case" for Unicode). See also [`titlecase`](@ref) to capitalize the first character of every word in `s`. See also [`lowercasefirst`](@ref), [`uppercase`](@ref), [`lowercase`](@ref), [`titlecase`](@ref). # Examples ```jldoctest julia> uppercasefirst("python") "Python" ``` """ function uppercasefirst(s::AbstractString) isempty(s) && return "" c = s[1] cโ€ฒ = titlecase(c) c == cโ€ฒ ? convert(String, s) : string(cโ€ฒ, SubString(s, nextind(s, 1))) end """ lowercasefirst(s::AbstractString) Return `s` with the first character converted to lowercase. See also [`uppercasefirst`](@ref), [`uppercase`](@ref), [`lowercase`](@ref), [`titlecase`](@ref). # Examples ```jldoctest julia> lowercasefirst("Julia") "julia" ``` """ function lowercasefirst(s::AbstractString) isempty(s) && return "" c = s[1] cโ€ฒ = lowercase(c) c == cโ€ฒ ? convert(String, s) : string(cโ€ฒ, SubString(s, nextind(s, 1))) end ############################################################################ # iterators for grapheme segmentation isgraphemebreak(c1::AbstractChar, c2::AbstractChar) = ismalformed(c1) || ismalformed(c2) || ccall(:utf8proc_grapheme_break, Bool, (UInt32, UInt32), c1, c2) # Stateful grapheme break required by Unicode-9 rules: the string # must be processed in sequence, with state initialized to Ref{Int32}(0). # Requires utf8proc v2.0 or later. function isgraphemebreak!(state::Ref{Int32}, c1::AbstractChar, c2::AbstractChar) if ismalformed(c1) || ismalformed(c2) state[] = 0 return true end ccall(:utf8proc_grapheme_break_stateful, Bool, (UInt32, UInt32, Ref{Int32}), c1, c2, state) end struct GraphemeIterator{S<:AbstractString} s::S # original string (for generation of SubStrings) end # Documented in Unicode module graphemes(s::AbstractString) = GraphemeIterator{typeof(s)}(s) eltype(::Type{GraphemeIterator{S}}) where {S} = SubString{S} eltype(::Type{GraphemeIterator{SubString{S}}}) where {S} = SubString{S} function length(g::GraphemeIterator{S}) where {S} c0 = eltype(S)(0x00000000) n = 0 state = Ref{Int32}(0) for c in g.s n += isgraphemebreak!(state, c0, c) c0 = c end return n end function iterate(g::GraphemeIterator, i_=(Int32(0),firstindex(g.s))) s = g.s statei, i = i_ state = Ref{Int32}(statei) j = i y = iterate(s, i) y === nothing && return nothing c0, k = y while k <= ncodeunits(s) # loop until next grapheme is s[i:j] c, โ„“ = iterate(s, k)::NTuple{2,Any} isgraphemebreak!(state, c0, c) && break j = k k = โ„“ c0 = c end return (SubString(s, i, j), (state[], k)) end ==(g1::GraphemeIterator, g2::GraphemeIterator) = g1.s == g2.s hash(g::GraphemeIterator, h::UInt) = hash(g.s, h) isless(g1::GraphemeIterator, g2::GraphemeIterator) = isless(g1.s, g2.s) show(io::IO, g::GraphemeIterator{S}) where {S} = print(io, "length-$(length(g)) GraphemeIterator{$S} for \"$(g.s)\"") ############################################################################ end # module S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/util.jlท€# This file is a part of Julia. License is MIT: https://julialang.org/license const Chars = Union{AbstractChar,Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}} # starts with and ends with predicates """ startswith(s::AbstractString, prefix::AbstractString) Return `true` if `s` starts with `prefix`. If `prefix` is a vector or set of characters, test whether the first character of `s` belongs to that set. See also [`endswith`](@ref), [`contains`](@ref). # Examples ```jldoctest julia> startswith("JuliaLang", "Julia") true ``` """ function startswith(a::AbstractString, b::AbstractString) i, j = iterate(a), iterate(b) while true j === nothing && return true # ran out of prefix: success! i === nothing && return false # ran out of source: failure i[1] == j[1] || return false # mismatch: failure i, j = iterate(a, i[2]), iterate(b, j[2]) end end startswith(str::AbstractString, chars::Chars) = !isempty(str) && first(str)::AbstractChar in chars """ endswith(s::AbstractString, suffix::AbstractString) Return `true` if `s` ends with `suffix`. If `suffix` is a vector or set of characters, test whether the last character of `s` belongs to that set. See also [`startswith`](@ref), [`contains`](@ref). # Examples ```jldoctest julia> endswith("Sunday", "day") true ``` """ function endswith(a::AbstractString, b::AbstractString) a, b = Iterators.Reverse(a), Iterators.Reverse(b) i, j = iterate(a), iterate(b) while true j === nothing && return true # ran out of suffix: success! i === nothing && return false # ran out of source: failure i[1] == j[1] || return false # mismatch: failure i, j = iterate(a, i[2]), iterate(b, j[2]) end end endswith(str::AbstractString, chars::Chars) = !isempty(str) && last(str) in chars function startswith(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) cub = ncodeunits(b) if ncodeunits(a) < cub false elseif _memcmp(a, b, sizeof(b)) == 0 nextind(a, cub) == cub + 1 # check that end of `b` doesn't match a partial character in `a` else false end end """ startswith(io::IO, prefix::Union{AbstractString,Base.Chars}) Check if an `IO` object starts with a prefix. See also [`peek`](@ref). """ function Base.startswith(io::IO, prefix::Base.Chars) mark(io) c = read(io, Char) reset(io) return c in prefix end function Base.startswith(io::IO, prefix::Union{String,SubString{String}}) mark(io) s = read(io, ncodeunits(prefix)) reset(io) return s == codeunits(prefix) end Base.startswith(io::IO, prefix::AbstractString) = startswith(io, String(prefix)) function endswith(a::Union{String, SubString{String}}, b::Union{String, SubString{String}}) cub = ncodeunits(b) astart = ncodeunits(a) - ncodeunits(b) + 1 if astart < 1 false elseif GC.@preserve(a, _memcmp(pointer(a, astart), b, sizeof(b))) == 0 thisind(a, astart) == astart # check that end of `b` doesn't match a partial character in `a` else false end end """ contains(haystack::AbstractString, needle) Return `true` if `haystack` contains `needle`. This is the same as `occursin(needle, haystack)`, but is provided for consistency with `startswith(haystack, needle)` and `endswith(haystack, needle)`. See also [`occursin`](@ref), [`in`](@ref), [`issubset`](@ref). # Examples ```jldoctest julia> contains("JuliaLang is pretty cool!", "Julia") true julia> contains("JuliaLang is pretty cool!", 'a') true julia> contains("aba", r"a.a") true julia> contains("abba", r"a.a") false ``` !!! compat "Julia 1.5" The `contains` function requires at least Julia 1.5. """ contains(haystack::AbstractString, needle) = occursin(needle, haystack) """ endswith(suffix) Create a function that checks whether its argument ends with `suffix`, i.e. a function equivalent to `y -> endswith(y, suffix)`. The returned function is of type `Base.Fix2{typeof(endswith)}`, which can be used to implement specialized methods. !!! compat "Julia 1.5" The single argument `endswith(suffix)` requires at least Julia 1.5. # Examples ```jldoctest julia> endswith("Julia")("Ends with Julia") true julia> endswith("Julia")("JuliaLang") false ``` """ endswith(s) = Base.Fix2(endswith, s) """ startswith(prefix) Create a function that checks whether its argument starts with `prefix`, i.e. a function equivalent to `y -> startswith(y, prefix)`. The returned function is of type `Base.Fix2{typeof(startswith)}`, which can be used to implement specialized methods. !!! compat "Julia 1.5" The single argument `startswith(prefix)` requires at least Julia 1.5. # Examples ```jldoctest julia> startswith("Julia")("JuliaLang") true julia> startswith("Julia")("Ends with Julia") false ``` """ startswith(s) = Base.Fix2(startswith, s) """ contains(needle) Create a function that checks whether its argument contains `needle`, i.e. a function equivalent to `haystack -> contains(haystack, needle)`. The returned function is of type `Base.Fix2{typeof(contains)}`, which can be used to implement specialized methods. """ contains(needle) = Base.Fix2(contains, needle) """ chop(s::AbstractString; head::Integer = 0, tail::Integer = 1) Remove the first `head` and the last `tail` characters from `s`. The call `chop(s)` removes the last character from `s`. If it is requested to remove more characters than `length(s)` then an empty string is returned. See also [`chomp`](@ref), [`startswith`](@ref), [`first`](@ref). # Examples ```jldoctest julia> a = "March" "March" julia> chop(a) "Marc" julia> chop(a, head = 1, tail = 2) "ar" julia> chop(a, head = 5, tail = 5) "" ``` """ function chop(s::AbstractString; head::Integer = 0, tail::Integer = 1) if isempty(s) return SubString(s) end SubString(s, nextind(s, firstindex(s), head), prevind(s, lastindex(s), tail)) end # TODO: optimization for the default case based on # chop(s::AbstractString) = SubString(s, firstindex(s), prevind(s, lastindex(s))) """ chopprefix(s::AbstractString, prefix::Union{AbstractString,Regex}) -> SubString Remove the prefix `prefix` from `s`. If `s` does not start with `prefix`, a string equal to `s` is returned. See also [`chopsuffix`](@ref). !!! compat "Julia 1.8" This function is available as of Julia 1.8. # Examples ```jldoctest julia> chopprefix("Hamburger", "Ham") "burger" julia> chopprefix("Hamburger", "hotdog") "Hamburger" ``` """ function chopprefix(s::AbstractString, prefix::AbstractString) k = firstindex(s) i, j = iterate(s), iterate(prefix) while true j === nothing && i === nothing && return SubString(s, 1, 0) # s == prefix: empty result j === nothing && return @inbounds SubString(s, k) # ran out of prefix: success! i === nothing && return SubString(s) # ran out of source: failure i[1] == j[1] || return SubString(s) # mismatch: failure k = i[2] i, j = iterate(s, k), iterate(prefix, j[2]) end end function chopprefix(s::Union{String, SubString{String}}, prefix::Union{String, SubString{String}}) if startswith(s, prefix) SubString(s, 1 + ncodeunits(prefix)) else SubString(s) end end """ chopsuffix(s::AbstractString, suffix::Union{AbstractString,Regex}) -> SubString Remove the suffix `suffix` from `s`. If `s` does not end with `suffix`, a string equal to `s` is returned. See also [`chopprefix`](@ref). !!! compat "Julia 1.8" This function is available as of Julia 1.8. # Examples ```jldoctest julia> chopsuffix("Hamburger", "er") "Hamburg" julia> chopsuffix("Hamburger", "hotdog") "Hamburger" ``` """ function chopsuffix(s::AbstractString, suffix::AbstractString) a, b = Iterators.Reverse(s), Iterators.Reverse(suffix) k = lastindex(s) i, j = iterate(a), iterate(b) while true j === nothing && i === nothing && return SubString(s, 1, 0) # s == suffix: empty result j === nothing && return @inbounds SubString(s, firstindex(s), k) # ran out of suffix: success! i === nothing && return SubString(s) # ran out of source: failure i[1] == j[1] || return SubString(s) # mismatch: failure k = i[2] i, j = iterate(a, k), iterate(b, j[2]) end end function chopsuffix(s::Union{String, SubString{String}}, suffix::Union{String, SubString{String}}) if !isempty(suffix) && endswith(s, suffix) astart = ncodeunits(s) - ncodeunits(suffix) + 1 @inbounds SubString(s, firstindex(s), prevind(s, astart)) else SubString(s) end end """ chomp(s::AbstractString) -> SubString Remove a single trailing newline from a string. See also [`chop`](@ref). # Examples ```jldoctest julia> chomp("Hello\\n") "Hello" ``` """ function chomp(s::AbstractString) i = lastindex(s) (i < 1 || s[i] != '\n') && (return SubString(s, 1, i)) j = prevind(s,i) (j < 1 || s[j] != '\r') && (return SubString(s, 1, j)) return SubString(s, 1, prevind(s,j)) end function chomp(s::String) i = lastindex(s) if i < 1 || codeunit(s,i) != 0x0a return @inbounds SubString(s, 1, i) elseif i < 2 || codeunit(s,i-1) != 0x0d return @inbounds SubString(s, 1, prevind(s, i)) else return @inbounds SubString(s, 1, prevind(s, i-1)) end end """ lstrip([pred=isspace,] str::AbstractString) -> SubString lstrip(str::AbstractString, chars) -> SubString Remove leading characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. The default behaviour is to remove leading whitespace and delimiters: see [`isspace`](@ref) for precise details. The optional `chars` argument specifies which characters to remove: it can be a single character, or a vector or set of characters. See also [`strip`](@ref) and [`rstrip`](@ref). # Examples ```jldoctest julia> a = lpad("March", 20) " March" julia> lstrip(a) "March" ``` """ function lstrip(f, s::AbstractString) e = lastindex(s) for (i::Int, c::AbstractChar) in pairs(s) !f(c) && return @inbounds SubString(s, i, e) end SubString(s, e+1, e) end lstrip(s::AbstractString) = lstrip(isspace, s) lstrip(s::AbstractString, chars::Chars) = lstrip(in(chars), s) lstrip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s")) """ rstrip([pred=isspace,] str::AbstractString) -> SubString rstrip(str::AbstractString, chars) -> SubString Remove trailing characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. The default behaviour is to remove trailing whitespace and delimiters: see [`isspace`](@ref) for precise details. The optional `chars` argument specifies which characters to remove: it can be a single character, or a vector or set of characters. See also [`strip`](@ref) and [`lstrip`](@ref). # Examples ```jldoctest julia> a = rpad("March", 20) "March " julia> rstrip(a) "March" ``` """ function rstrip(f, s::AbstractString) for (i, c) in Iterators.reverse(pairs(s)) f(c::AbstractChar) || return @inbounds SubString(s, 1, i::Int) end SubString(s, 1, 0) end rstrip(s::AbstractString) = rstrip(isspace, s) rstrip(s::AbstractString, chars::Chars) = rstrip(in(chars), s) rstrip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s")) """ strip([pred=isspace,] str::AbstractString) -> SubString strip(str::AbstractString, chars) -> SubString Remove leading and trailing characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. The default behaviour is to remove leading and trailing whitespace and delimiters: see [`isspace`](@ref) for precise details. The optional `chars` argument specifies which characters to remove: it can be a single character, vector or set of characters. See also [`lstrip`](@ref) and [`rstrip`](@ref). !!! compat "Julia 1.2" The method which accepts a predicate function requires Julia 1.2 or later. # Examples ```jldoctest julia> strip("{3, 5}\\n", ['{', '}', '\\n']) "3, 5" ``` """ strip(s::AbstractString) = lstrip(rstrip(s)) strip(s::AbstractString, chars::Chars) = lstrip(rstrip(s, chars), chars) strip(::AbstractString, ::AbstractString) = throw(ArgumentError("Both arguments are strings. The second argument should be a `Char` or collection of `Char`s")) strip(f, s::AbstractString) = lstrip(f, rstrip(f, s)) ## string padding functions ## """ lpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') -> String Stringify `s` and pad the resulting string on the left with `p` to make it `n` characters (in [`textwidth`](@ref)) long. If `s` is already `n` characters long, an equal string is returned. Pad with spaces by default. # Examples ```jldoctest julia> lpad("March", 10) " March" ``` !!! compat "Julia 1.7" In Julia 1.7, this function was changed to use `textwidth` rather than a raw character (codepoint) count. """ lpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') = lpad(string(s)::AbstractString, n, string(p)) function lpad( s::Union{AbstractChar,AbstractString}, n::Integer, p::Union{AbstractChar,AbstractString}=' ', ) :: String n = Int(n)::Int m = signed(n) - Int(textwidth(s))::Int m โ‰ค 0 && return string(s) l = textwidth(p) q, r = divrem(m, l) r == 0 ? string(p^q, s) : string(p^q, first(p, r), s) end """ rpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') -> String Stringify `s` and pad the resulting string on the right with `p` to make it `n` characters (in [`textwidth`](@ref)) long. If `s` is already `n` characters long, an equal string is returned. Pad with spaces by default. # Examples ```jldoctest julia> rpad("March", 20) "March " ``` !!! compat "Julia 1.7" In Julia 1.7, this function was changed to use `textwidth` rather than a raw character (codepoint) count. """ rpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') = rpad(string(s)::AbstractString, n, string(p)) function rpad( s::Union{AbstractChar,AbstractString}, n::Integer, p::Union{AbstractChar,AbstractString}=' ', ) :: String n = Int(n)::Int m = signed(n) - Int(textwidth(s))::Int m โ‰ค 0 && return string(s) l = textwidth(p) q, r = divrem(m, l) r == 0 ? string(s, p^q) : string(s, p^q, first(p, r)) end """ eachsplit(str::AbstractString, dlm; limit::Integer=0, keepempty::Bool=true) eachsplit(str::AbstractString; limit::Integer=0, keepempty::Bool=false) Split `str` on occurrences of the delimiter(s) `dlm` and return an iterator over the substrings. `dlm` can be any of the formats allowed by [`findnext`](@ref)'s first argument (i.e. as a string, regular expression or a function), or as a single character or collection of characters. If `dlm` is omitted, it defaults to [`isspace`](@ref). The optional keyword arguments are: - `limit`: the maximum size of the result. `limit=0` implies no maximum (default) - `keepempty`: whether empty fields should be kept in the result. Default is `false` without a `dlm` argument, `true` with a `dlm` argument. See also [`split`](@ref). !!! compat "Julia 1.8" The `eachsplit` function requires at least Julia 1.8. # Examples ```jldoctest julia> a = "Ma.rch" "Ma.rch" julia> b = eachsplit(a, ".") Base.SplitIterator{String, String}("Ma.rch", ".", 0, true) julia> collect(b) 2-element Vector{SubString{String}}: "Ma" "rch" ``` """ function eachsplit end # Forcing specialization on `splitter` improves performance (roughly 30% decrease in runtime) # and prevents a major invalidation risk (1550 MethodInstances) struct SplitIterator{S<:AbstractString,F} str::S splitter::F limit::Int keepempty::Bool end eltype(::Type{<:SplitIterator{T}}) where T = SubString{T} eltype(::Type{<:SplitIterator{<:SubString{T}}}) where T = SubString{T} IteratorSize(::Type{<:SplitIterator}) = SizeUnknown() # i: the starting index of the substring to be extracted # k: the starting index of the next substring to be extracted # n: the number of splits returned so far; always less than iter.limit - 1 (1 for the rest) function iterate(iter::SplitIterator, (i, k, n)=(firstindex(iter.str), firstindex(iter.str), 0)) i - 1 > ncodeunits(iter.str)::Int && return nothing r = findnext(iter.splitter, iter.str, k)::Union{Nothing,Int,UnitRange{Int}} while r !== nothing && n != iter.limit - 1 && first(r) <= ncodeunits(iter.str) j, k = first(r), nextind(iter.str, last(r))::Int k_ = k <= j ? nextind(iter.str, j)::Int : k if i < k substr = @inbounds SubString(iter.str, i, prevind(iter.str, j)::Int) (iter.keepempty || i < j) && return (substr, (k, k_, n + 1)) i = k end k = k_ r = findnext(iter.splitter, iter.str, k)::Union{Nothing,Int,UnitRange{Int}} end iter.keepempty || i <= ncodeunits(iter.str) || return nothing @inbounds SubString(iter.str, i), (ncodeunits(iter.str) + 2, k, n + 1) end # Specialization for partition(s,n) to return a SubString eltype(::Type{PartitionIterator{T}}) where {T<:AbstractString} = SubString{T} # SubStrings do not nest eltype(::Type{PartitionIterator{T}}) where {T<:SubString} = T function iterate(itr::PartitionIterator{<:AbstractString}, state = firstindex(itr.c)) state > ncodeunits(itr.c) && return nothing r = min(nextind(itr.c, state, itr.n - 1), lastindex(itr.c)) return SubString(itr.c, state, r), nextind(itr.c, r) end eachsplit(str::T, splitter; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} = SplitIterator(str, splitter, limit, keepempty) eachsplit(str::T, splitter::Union{Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}; limit::Integer=0, keepempty=true) where {T<:AbstractString} = eachsplit(str, in(splitter); limit, keepempty) eachsplit(str::T, splitter::AbstractChar; limit::Integer=0, keepempty=true) where {T<:AbstractString} = eachsplit(str, isequal(splitter); limit, keepempty) # a bit oddball, but standard behavior in Perl, Ruby & Python: eachsplit(str::AbstractString; limit::Integer=0, keepempty=false) = eachsplit(str, isspace; limit, keepempty) """ split(str::AbstractString, dlm; limit::Integer=0, keepempty::Bool=true) split(str::AbstractString; limit::Integer=0, keepempty::Bool=false) Split `str` into an array of substrings on occurrences of the delimiter(s) `dlm`. `dlm` can be any of the formats allowed by [`findnext`](@ref)'s first argument (i.e. as a string, regular expression or a function), or as a single character or collection of characters. If `dlm` is omitted, it defaults to [`isspace`](@ref). The optional keyword arguments are: - `limit`: the maximum size of the result. `limit=0` implies no maximum (default) - `keepempty`: whether empty fields should be kept in the result. Default is `false` without a `dlm` argument, `true` with a `dlm` argument. See also [`rsplit`](@ref), [`eachsplit`](@ref). # Examples ```jldoctest julia> a = "Ma.rch" "Ma.rch" julia> split(a, ".") 2-element Vector{SubString{String}}: "Ma" "rch" ``` """ function split(str::T, splitter; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} collect(eachsplit(str, splitter; limit, keepempty)) end # a bit oddball, but standard behavior in Perl, Ruby & Python: split(str::AbstractString; limit::Integer=0, keepempty::Bool=false) = split(str, isspace; limit, keepempty) """ rsplit(s::AbstractString; limit::Integer=0, keepempty::Bool=false) rsplit(s::AbstractString, chars; limit::Integer=0, keepempty::Bool=true) Similar to [`split`](@ref), but starting from the end of the string. # Examples ```jldoctest julia> a = "M.a.r.c.h" "M.a.r.c.h" julia> rsplit(a, ".") 5-element Vector{SubString{String}}: "M" "a" "r" "c" "h" julia> rsplit(a, "."; limit=1) 1-element Vector{SubString{String}}: "M.a.r.c.h" julia> rsplit(a, "."; limit=2) 2-element Vector{SubString{String}}: "M.a.r.c" "h" ``` """ function rsplit end function rsplit(str::T, splitter; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} _rsplit(str, splitter, limit, keepempty, T <: SubString ? T[] : SubString{T}[]) end function rsplit(str::T, splitter::Union{Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} _rsplit(str, in(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) end function rsplit(str::T, splitter::AbstractChar; limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} _rsplit(str, isequal(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) end function _rsplit(str::AbstractString, splitter, limit::Integer, keepempty::Bool, strs::Array) n = lastindex(str)::Int r = something(findlast(splitter, str)::Union{Nothing,Int,UnitRange{Int}}, 0) j, k = first(r), last(r) while j > 0 && k > 0 && length(strs) != limit-1 (keepempty || k < n) && pushfirst!(strs, @inbounds SubString(str,nextind(str,k)::Int,n)) n = prevind(str, j)::Int r = something(findprev(splitter,str,n)::Union{Nothing,Int,UnitRange{Int}}, 0) j, k = first(r), last(r) end (keepempty || n > 0) && pushfirst!(strs, SubString(str,1,n)) return strs end rsplit(str::AbstractString; limit::Integer=0, keepempty::Bool=false) = rsplit(str, isspace; limit=limit, keepempty=keepempty) _replace(io, repl, str, r, pattern) = print(io, repl) _replace(io, repl::Function, str, r, pattern) = print(io, repl(SubString(str, first(r), last(r)))) _replace(io, repl::Function, str, r, pattern::Function) = print(io, repl(str[first(r)])) _pat_replacer(x) = x _free_pat_replacer(x) = nothing _pat_replacer(x::AbstractChar) = isequal(x) _pat_replacer(x::Union{Tuple{Vararg{AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}) = in(x) # note: leave str untyped here to make it easier for packages like StringViews to hook in function _replace_init(str, pat_repl::NTuple{N, Pair}, count::Int) where N count < 0 && throw(DomainError(count, "`count` must be non-negative.")) e1 = nextind(str, lastindex(str)) # sizeof(str)+1 a = firstindex(str) patterns = map(p -> _pat_replacer(first(p)), pat_repl) replaces = map(last, pat_repl) rs = map(patterns) do p r = findnext(p, str, a) if r === nothing || first(r) == 0 return e1+1:0 end r isa Int && (r = r:r) # findnext / performance fix return r end return e1, patterns, replaces, rs, all(>(e1), map(first, rs)) end # note: leave str untyped here to make it easier for packages like StringViews to hook in function _replace_finish(io::IO, str, count::Int, e1::Int, patterns::Tuple, replaces::Tuple, rs::Tuple) n = 1 i = a = firstindex(str) while true p = argmin(map(first, rs)) # TODO: or argmin(rs), to pick the shortest first match ? r = rs[p] j, k = first(r), last(r) j > e1 && break if i == a || i <= k # copy out preserved portion GC.@preserve str unsafe_write(io, pointer(str, i), UInt(j-i)) # copy out replacement string _replace(io, replaces[p], str, r, patterns[p]) end if k < j i = j j == e1 && break k = nextind(str, j) else i = k = nextind(str, k) end n == count && break let k = k rs = map(patterns, rs) do p, r if first(r) < k r = findnext(p, str, k) if r === nothing || first(r) == 0 return e1+1:0 end r isa Int && (r = r:r) # findnext / performance fix end return r end end n += 1 end foreach(_free_pat_replacer, patterns) write(io, SubString(str, i)) return io end # note: leave str untyped here to make it easier for packages like StringViews to hook in function _replace_(io::IO, str, pat_repl::NTuple{N, Pair}, count::Int) where N if count == 0 write(io, str) return io end e1, patterns, replaces, rs, notfound = _replace_init(str, pat_repl, count) if notfound foreach(_free_pat_replacer, patterns) write(io, str) return io end return _replace_finish(io, str, count, e1, patterns, replaces, rs) end # note: leave str untyped here to make it easier for packages like StringViews to hook in function _replace_(str, pat_repl::NTuple{N, Pair}, count::Int) where N count == 0 && return String(str) e1, patterns, replaces, rs, notfound = _replace_init(str, pat_repl, count) if notfound foreach(_free_pat_replacer, patterns) return String(str) end out = IOBuffer(sizehint=floor(Int, 1.2sizeof(str))) return String(take!(_replace_finish(out, str, count, e1, patterns, replaces, rs))) end """ replace([io::IO], s::AbstractString, pat=>r, [pat2=>r2, ...]; [count::Integer]) Search for the given pattern `pat` in `s`, and replace each occurrence with `r`. If `count` is provided, replace at most `count` occurrences. `pat` may be a single character, a vector or a set of characters, a string, or a regular expression. If `r` is a function, each occurrence is replaced with `r(s)` where `s` is the matched substring (when `pat` is a `AbstractPattern` or `AbstractString`) or character (when `pat` is an `AbstractChar` or a collection of `AbstractChar`). If `pat` is a regular expression and `r` is a [`SubstitutionString`](@ref), then capture group references in `r` are replaced with the corresponding matched text. To remove instances of `pat` from `string`, set `r` to the empty `String` (`""`). The return value is a new string after the replacements. If the `io::IO` argument is supplied, the transformed string is instead written to `io` (returning `io`). (For example, this can be used in conjunction with an [`IOBuffer`](@ref) to re-use a pre-allocated buffer array in-place.) Multiple patterns can be specified, and they will be applied left-to-right simultaneously, so only one pattern will be applied to any character, and the patterns will only be applied to the input text, not the replacements. !!! compat "Julia 1.7" Support for multiple patterns requires version 1.7. !!! compat "Julia 1.10" The `io::IO` argument requires version 1.10. # Examples ```jldoctest julia> replace("Python is a programming language.", "Python" => "Julia") "Julia is a programming language." julia> replace("The quick foxes run quickly.", "quick" => "slow", count=1) "The slow foxes run quickly." julia> replace("The quick foxes run quickly.", "quick" => "", count=1) "The foxes run quickly." julia> replace("The quick foxes run quickly.", r"fox(es)?" => s"bus\\1") "The quick buses run quickly." julia> replace("abcabc", "a" => "b", "b" => "c", r".+" => "a") "bca" ``` """ replace(io::IO, s::AbstractString, pat_f::Pair...; count=typemax(Int)) = _replace_(io, String(s), pat_f, Int(count)) replace(s::AbstractString, pat_f::Pair...; count=typemax(Int)) = _replace_(String(s), pat_f, Int(count)) # TODO: allow transform as the first argument to replace? # hex <-> bytes conversion """ hex2bytes(itr) Given an iterable `itr` of ASCII codes for a sequence of hexadecimal digits, returns a `Vector{UInt8}` of bytes corresponding to the binary representation: each successive pair of hexadecimal digits in `itr` gives the value of one byte in the return vector. The length of `itr` must be even, and the returned array has half of the length of `itr`. See also [`hex2bytes!`](@ref) for an in-place version, and [`bytes2hex`](@ref) for the inverse. !!! compat "Julia 1.7" Calling `hex2bytes` with iterators producing `UInt8` values requires Julia 1.7 or later. In earlier versions, you can `collect` the iterator before calling `hex2bytes`. # Examples ```jldoctest julia> s = string(12345, base = 16) "3039" julia> hex2bytes(s) 2-element Vector{UInt8}: 0x30 0x39 julia> a = b"01abEF" 6-element Base.CodeUnits{UInt8, String}: 0x30 0x31 0x61 0x62 0x45 0x46 julia> hex2bytes(a) 3-element Vector{UInt8}: 0x01 0xab 0xef ``` """ function hex2bytes end hex2bytes(s) = hex2bytes!(Vector{UInt8}(undef, length(s)::Int >> 1), s) # special case - valid bytes are checked in the generic implementation function hex2bytes!(dest::AbstractArray{UInt8}, s::String) sizeof(s) != length(s) && throw(ArgumentError("input string must consist of hexadecimal characters only")) hex2bytes!(dest, transcode(UInt8, s)) end """ hex2bytes!(dest::AbstractVector{UInt8}, itr) Convert an iterable `itr` of bytes representing a hexadecimal string to its binary representation, similar to [`hex2bytes`](@ref) except that the output is written in-place to `dest`. The length of `dest` must be half the length of `itr`. !!! compat "Julia 1.7" Calling hex2bytes! with iterators producing UInt8 requires version 1.7. In earlier versions, you can collect the iterable before calling instead. """ function hex2bytes!(dest::AbstractArray{UInt8}, itr) isodd(length(itr)) && throw(ArgumentError("length of iterable must be even")) @boundscheck 2*length(dest) != length(itr) && throw(ArgumentError("length of output array must be half of the length of input iterable")) iszero(length(itr)) && return dest next = iterate(itr) @inbounds for i in eachindex(dest) x,state = next::NTuple{2,Any} y,state = iterate(itr, state)::NTuple{2,Any} next = iterate(itr, state) dest[i] = number_from_hex(x) << 4 + number_from_hex(y) end return dest end @inline number_from_hex(c::AbstractChar) = number_from_hex(Char(c)) @inline number_from_hex(c::Char) = number_from_hex(UInt8(c)) @inline function number_from_hex(c::UInt8) UInt8('0') <= c <= UInt8('9') && return c - UInt8('0') c |= 0b0100000 UInt8('a') <= c <= UInt8('f') && return c - UInt8('a') + 0x0a throw(ArgumentError("byte is not an ASCII hexadecimal digit")) end """ bytes2hex(itr) -> String bytes2hex(io::IO, itr) Convert an iterator `itr` of bytes to its hexadecimal string representation, either returning a `String` via `bytes2hex(itr)` or writing the string to an `io` stream via `bytes2hex(io, itr)`. The hexadecimal characters are all lowercase. !!! compat "Julia 1.7" Calling `bytes2hex` with arbitrary iterators producing `UInt8` values requires Julia 1.7 or later. In earlier versions, you can `collect` the iterator before calling `bytes2hex`. # Examples ```jldoctest julia> a = string(12345, base = 16) "3039" julia> b = hex2bytes(a) 2-element Vector{UInt8}: 0x30 0x39 julia> bytes2hex(b) "3039" ``` """ function bytes2hex end function bytes2hex(itr) eltype(itr) === UInt8 || throw(ArgumentError("eltype of iterator not UInt8")) b = Base.StringVector(2*length(itr)) @inbounds for (i, x) in enumerate(itr) b[2i - 1] = hex_chars[1 + x >> 4] b[2i ] = hex_chars[1 + x & 0xf] end return String(b) end function bytes2hex(io::IO, itr) eltype(itr) === UInt8 || throw(ArgumentError("eltype of iterator not UInt8")) for x in itr print(io, Char(hex_chars[1 + x >> 4]), Char(hex_chars[1 + x & 0xf])) end end # check for pure ASCII-ness function ascii(s::String) for i in 1:sizeof(s) @inbounds codeunit(s, i) < 0x80 || __throw_invalid_ascii(s, i) end return s end @noinline __throw_invalid_ascii(s::String, i::Int) = throw(ArgumentError("invalid ASCII at index $i in $(repr(s))")) """ ascii(s::AbstractString) Convert a string to `String` type and check that it contains only ASCII data, otherwise throwing an `ArgumentError` indicating the position of the first non-ASCII byte. See also the [`isascii`](@ref) predicate to filter or replace non-ASCII characters. # Examples ```jldoctest julia> ascii("abcdeฮณfgh") ERROR: ArgumentError: invalid ASCII at index 6 in "abcdeฮณfgh" Stacktrace: [...] julia> ascii("abcdefgh") "abcdefgh" ``` """ ascii(x::AbstractString) = ascii(String(x)) Base.rest(s::Union{String,SubString{String}}, i=1) = SubString(s, i) function Base.rest(s::AbstractString, st...) io = IOBuffer() for c in Iterators.rest(s, st...) print(io, c) end return String(take!(io)) end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/strings/io.jlศX# This file is a part of Julia. License is MIT: https://julialang.org/license ## core text I/O ## """ print([io::IO], xs...) Write to `io` (or to the default output stream [`stdout`](@ref) if `io` is not given) a canonical (un-decorated) text representation. The representation used by `print` includes minimal formatting and tries to avoid Julia-specific details. `print` falls back to calling `show`, so most types should just define `show`. Define `print` if your type has a separate "plain" representation. For example, `show` displays strings with quotes, and `print` displays strings without quotes. See also [`println`](@ref), [`string`](@ref), [`printstyled`](@ref). # Examples ```jldoctest julia> print("Hello World!") Hello World! julia> io = IOBuffer(); julia> print(io, "Hello", ' ', :World!) julia> String(take!(io)) "Hello World!" ``` """ function print(io::IO, x) lock(io) try show(io, x) finally unlock(io) end return nothing end function print(io::IO, xs...) lock(io) try for x in xs print(io, x) end finally unlock(io) end return nothing end """ println([io::IO], xs...) Print (using [`print`](@ref)) `xs` to `io` followed by a newline. If `io` is not supplied, prints to the default output stream [`stdout`](@ref). See also [`printstyled`](@ref) to add colors etc. # Examples ```jldoctest julia> println("Hello, world") Hello, world julia> io = IOBuffer(); julia> println(io, "Hello", ',', " world.") julia> String(take!(io)) "Hello, world.\\n" ``` """ println(io::IO, xs...) = print(io, xs..., "\n") ## conversion of general objects to strings ## """ sprint(f::Function, args...; context=nothing, sizehint=0) Call the given function with an I/O stream and the supplied extra arguments. Everything written to this I/O stream is returned as a string. `context` can be an [`IOContext`](@ref) whose properties will be used, a `Pair` specifying a property and its value, or a tuple of `Pair` specifying multiple properties and their values. `sizehint` suggests the capacity of the buffer (in bytes). The optional keyword argument `context` can be set to a `:key=>value` pair, a tuple of `:key=>value` pairs, or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O stream passed to `f`. The optional `sizehint` is a suggested size (in bytes) to allocate for the buffer used to write the string. !!! compat "Julia 1.7" Passing a tuple to keyword `context` requires Julia 1.7 or later. # Examples ```jldoctest julia> sprint(show, 66.66666; context=:compact => true) "66.6667" julia> sprint(showerror, BoundsError([1], 100)) "BoundsError: attempt to access 1-element Vector{Int64} at index [100]" ``` """ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) s = IOBuffer(sizehint=sizehint) if context isa Tuple f(IOContext(s, context...), args...) elseif context !== nothing f(IOContext(s, context), args...) else f(s, args...) end String(_unsafe_take!(s)) end function _str_sizehint(x) if x isa Float64 return 20 elseif x isa Float32 return 12 elseif x isa String || x isa SubString{String} return sizeof(x) elseif x isa Char return ncodeunits(x) elseif x isa UInt64 || x isa UInt32 return ndigits(x) elseif x isa Int64 || x isa Int32 return ndigits(x) + (x < zero(x)) else return 8 end end function print_to_string(xs...) if isempty(xs) return "" end siz::Int = 0 for x in xs siz += _str_sizehint(x) end # specialized for performance reasons s = IOBuffer(sizehint=siz) for x in xs print(s, x) end String(_unsafe_take!(s)) end function string_with_env(env, xs...) if isempty(xs) return "" end siz::Int = 0 for x in xs siz += _str_sizehint(x) end # specialized for performance reasons s = IOBuffer(sizehint=siz) env_io = IOContext(s, env) for x in xs print(env_io, x) end String(_unsafe_take!(s)) end """ string(xs...) Create a string from any values using the [`print`](@ref) function. `string` should usually not be defined directly. Instead, define a method `print(io::IO, x::MyType)`. If `string(x)` for a certain type needs to be highly efficient, then it may make sense to add a method to `string` and define `print(io::IO, x::MyType) = print(io, string(x))` to ensure the functions are consistent. See also: [`String`](@ref), [`repr`](@ref), [`sprint`](@ref), [`show`](@ref @show). # Examples ```jldoctest julia> string("a", 1, true) "a1true" ``` """ string(xs...) = print_to_string(xs...) string(a::Symbol) = String(a) # note: print uses an encoding determined by `io` (defaults to UTF-8), whereas # write uses an encoding determined by `s` (UTF-8 for `String`) print(io::IO, s::AbstractString) = for c in s; print(io, c); end write(io::IO, s::AbstractString) = (len = 0; for c in s; len += Int(write(io, c))::Int; end; len) show(io::IO, s::AbstractString) = print_quoted(io, s) # show elided string if more than `limit` characters function show( io :: IO, mime :: MIME"text/plain", str :: AbstractString; limit :: Union{Int, Nothing} = nothing, ) # compute limit in default case if limit === nothing get(io, :limit, false)::Bool || return show(io, str) limit = max(20, displaysize(io)[2]) # one line in collection, seven otherwise get(io, :typeinfo, nothing) === nothing && (limit *= 7) end # early out for short strings len = ncodeunits(str) len โ‰ค limit - 2 && # quote chars return show(io, str) # these don't depend on string data units = codeunit(str) == UInt8 ? "bytes" : "code units" skip_text(skip) = " โ‹ฏ $skip $units โ‹ฏ " short = length(skip_text("")) + 4 # quote chars chars = max(limit, short + 1) - short # at least 1 digit # figure out how many characters to print in elided case chars -= d = ndigits(len - chars) # first adjustment chars += d - ndigits(len - chars) # second if needed chars = max(0, chars) # find head & tail, avoiding O(length(str)) computation head = nextind(str, 0, 1 + (chars + 1) รท 2) tail = prevind(str, len + 1, chars รท 2) # threshold: min chars skipped to make elision worthwhile t = short + ndigits(len - chars) - 1 n = tail - head # skipped code units if 4t โ‰ค n || t โ‰ค n && t โ‰ค length(str, head, tail-1) skip = skip_text(n) show(io, SubString(str, 1:prevind(str, head))) printstyled(io, skip; color=:light_yellow, bold=true) show(io, SubString(str, tail)) else show(io, str) end end # optimized methods to avoid iterating over chars write(io::IO, s::Union{String,SubString{String}}) = GC.@preserve s Int(unsafe_write(io, pointer(s), reinterpret(UInt, sizeof(s))))::Int print(io::IO, s::Union{String,SubString{String}}) = (write(io, s); nothing) """ repr(x; context=nothing) Create a string from any value using the [`show`](@ref) function. You should not add methods to `repr`; define a `show` method instead. The optional keyword argument `context` can be set to a `:key=>value` pair, a tuple of `:key=>value` pairs, or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O stream passed to `show`. Note that `repr(x)` is usually similar to how the value of `x` would be entered in Julia. See also [`repr(MIME("text/plain"), x)`](@ref) to instead return a "pretty-printed" version of `x` designed more for human consumption, equivalent to the REPL display of `x`. !!! compat "Julia 1.7" Passing a tuple to keyword `context` requires Julia 1.7 or later. # Examples ```jldoctest julia> repr(1) "1" julia> repr(zeros(3)) "[0.0, 0.0, 0.0]" julia> repr(big(1/3)) "0.333333333333333314829616256247390992939472198486328125" julia> repr(big(1/3), context=:compact => true) "0.333333" ``` """ repr(x; context=nothing) = sprint(show, x; context=context) limitrepr(x) = repr(x, context = :limit=>true) # IOBuffer views of a (byte)string: """ IOBuffer(string::String) Create a read-only `IOBuffer` on the data underlying the given string. # Examples ```jldoctest julia> io = IOBuffer("Haho"); julia> String(take!(io)) "Haho" julia> String(take!(io)) "Haho" ``` """ IOBuffer(str::String) = IOBuffer(unsafe_wrap(Vector{UInt8}, str)) IOBuffer(s::SubString{String}) = IOBuffer(view(unsafe_wrap(Vector{UInt8}, s.string), s.offset + 1 : s.offset + sizeof(s))) # join is implemented using IO """ join([io::IO,] iterator [, delim [, last]]) Join any `iterator` into a single string, inserting the given delimiter (if any) between adjacent items. If `last` is given, it will be used instead of `delim` between the last two items. Each item of `iterator` is converted to a string via `print(io::IOBuffer, x)`. If `io` is given, the result is written to `io` rather than returned as a `String`. # Examples ```jldoctest julia> join(["apples", "bananas", "pineapples"], ", ", " and ") "apples, bananas and pineapples" julia> join([1,2,3,4,5]) "12345" ``` """ function join(io::IO, iterator, delim, last) first = true local prev for item in iterator if @isdefined prev first ? (first = false) : print(io, delim) print(io, prev) end prev = item end if @isdefined prev first || print(io, last) print(io, prev) end nothing end function join(io::IO, iterator, delim="") # Specialization of the above code when delim==last, # which lets us emit (compile) less code first = true for item in iterator first ? (first = false) : print(io, delim) print(io, item) end end join(iterator) = sprint(join, iterator) join(iterator, delim) = sprint(join, iterator, delim) join(iterator, delim, last) = sprint(join, iterator, delim, last) ## string escaping & unescaping ## need_full_hex(c::Union{Nothing, AbstractChar}) = c !== nothing && isxdigit(c) escape_nul(c::Union{Nothing, AbstractChar}) = (c !== nothing && '0' <= c <= '7') ? "\\x00" : "\\0" """ escape_string(str::AbstractString[, esc]; keep = ())::AbstractString escape_string(io, str::AbstractString[, esc]; keep = ())::Nothing General escaping of traditional C and Unicode escape sequences. The first form returns the escaped string, the second prints the result to `io`. Backslashes (`\\`) are escaped with a double-backslash (`"\\\\"`). Non-printable characters are escaped either with their standard C escape codes, `"\\0"` for NUL (if unambiguous), unicode code point (`"\\u"` prefix) or hex (`"\\x"` prefix). The optional `esc` argument specifies any additional characters that should also be escaped by a prepending backslash (`\"` is also escaped by default in the first form). The argument `keep` specifies a collection of characters which are to be kept as they are. Notice that `esc` has precedence here. See also [`unescape_string`](@ref) for the reverse operation. !!! compat "Julia 1.7" The `keep` argument is available as of Julia 1.7. # Examples ```jldoctest julia> escape_string("aaa\\nbbb") "aaa\\\\nbbb" julia> escape_string("aaa\\nbbb"; keep = '\\n') "aaa\\nbbb" julia> escape_string("\\xfe\\xff") # invalid utf-8 "\\\\xfe\\\\xff" julia> escape_string(string('\\u2135','\\0')) # unambiguous "โ„ต\\\\0" julia> escape_string(string('\\u2135','\\0','0')) # \\0 would be ambiguous "โ„ต\\\\x000" ``` """ function escape_string(io::IO, s::AbstractString, esc=""; keep = ()) a = Iterators.Stateful(s) for c::AbstractChar in a if c in esc print(io, '\\', c) elseif c in keep print(io, c) elseif isascii(c) c == '\0' ? print(io, escape_nul(peek(a)::Union{AbstractChar,Nothing})) : c == '\e' ? print(io, "\\e") : c == '\\' ? print(io, "\\\\") : '\a' <= c <= '\r' ? print(io, '\\', "abtnvfr"[Int(c)-6]) : isprint(c) ? print(io, c) : print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) elseif !isoverlong(c) && !ismalformed(c) isprint(c) ? print(io, c) : c <= '\x7f' ? print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) : c <= '\uffff' ? print(io, "\\u", string(UInt32(c), base = 16, pad = need_full_hex(peek(a)::Union{AbstractChar,Nothing}) ? 4 : 2)) : print(io, "\\U", string(UInt32(c), base = 16, pad = need_full_hex(peek(a)::Union{AbstractChar,Nothing}) ? 8 : 4)) else # malformed or overlong u = bswap(reinterpret(UInt32, c)::UInt32) while true print(io, "\\x", string(u % UInt8, base = 16, pad = 2)) (u >>= 8) == 0 && break end end end end escape_string(s::AbstractString, esc=('\"',); keep = ()) = sprint((io)->escape_string(io, s, esc; keep = keep), sizehint=lastindex(s)) function print_quoted(io, s::AbstractString) print(io, '"') escape_string(io, s, ('\"','$')) #"# work around syntax highlighting problem print(io, '"') end # general unescaping of traditional C and Unicode escape sequences # TODO: handle unescaping invalid UTF-8 sequences """ unescape_string(str::AbstractString, keep = ())::AbstractString unescape_string(io, s::AbstractString, keep = ())::Nothing General unescaping of traditional C and Unicode escape sequences. The first form returns the escaped string, the second prints the result to `io`. The argument `keep` specifies a collection of characters which (along with backlashes) are to be kept as they are. The following escape sequences are recognised: - Escaped backslash (`\\\\`) - Escaped double-quote (`\\\"`) - Standard C escape sequences (`\\a`, `\\b`, `\\t`, `\\n`, `\\v`, `\\f`, `\\r`, `\\e`) - Unicode BMP code points (`\\u` with 1-4 trailing hex digits) - All Unicode code points (`\\U` with 1-8 trailing hex digits; max value = 0010ffff) - Hex bytes (`\\x` with 1-2 trailing hex digits) - Octal bytes (`\\` with 1-3 trailing octal digits) See also [`escape_string`](@ref). # Examples ```jldoctest julia> unescape_string("aaa\\\\nbbb") # C escape sequence "aaa\\nbbb" julia> unescape_string("\\\\u03c0") # unicode "ฯ€" julia> unescape_string("\\\\101") # octal "A" julia> unescape_string("aaa \\\\g \\\\n", ['g']) # using `keep` argument "aaa \\\\g \\n" ``` """ function unescape_string(io::IO, s::AbstractString, keep = ()) a = Iterators.Stateful(s) for c in a if !isempty(a) && c == '\\' c = popfirst!(a) if c in keep print(io, '\\', c) elseif c == 'x' || c == 'u' || c == 'U' n = k = 0 m = c == 'x' ? 2 : c == 'u' ? 4 : 8 while (k += 1) <= m && !isempty(a) nc = peek(a)::AbstractChar n = '0' <= nc <= '9' ? n<<4 + (nc-'0') : 'a' <= nc <= 'f' ? n<<4 + (nc-'a'+10) : 'A' <= nc <= 'F' ? n<<4 + (nc-'A'+10) : break popfirst!(a) end if k == 1 || n > 0x10ffff u = m == 4 ? 'u' : 'U' throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" : "unicode (\\$u)") escape sequence")) end if m == 2 # \x escape sequence write(io, UInt8(n)) else print(io, Char(n)) end elseif '0' <= c <= '7' k = 1 n = c-'0' while (k += 1) <= 3 && !isempty(a) c = peek(a)::AbstractChar n = ('0' <= c <= '7') ? n<<3 + c-'0' : break popfirst!(a) end if n > 255 throw(ArgumentError("octal escape sequence out of range")) end write(io, UInt8(n)) else print(io, c == 'a' ? '\a' : c == 'b' ? '\b' : c == 't' ? '\t' : c == 'n' ? '\n' : c == 'v' ? '\v' : c == 'f' ? '\f' : c == 'r' ? '\r' : c == 'e' ? '\e' : (c == '\\' || c == '"') ? c : throw(ArgumentError("invalid escape sequence \\$c"))) end else print(io, c) end end end unescape_string(s::AbstractString, keep = ()) = sprint(unescape_string, s, keep; sizehint=lastindex(s)) """ @b_str Create an immutable byte (`UInt8`) vector using string syntax. # Examples ```jldoctest julia> v = b"12\\x01\\x02" 4-element Base.CodeUnits{UInt8, String}: 0x31 0x32 0x01 0x02 julia> v[2] 0x32 ``` """ macro b_str(s) v = codeunits(unescape_string(s)) QuoteNode(v) end """ @raw_str -> String Create a raw string without interpolation and unescaping. The exception is that quotation marks still must be escaped. Backslashes escape both quotation marks and other backslashes, but only when a sequence of backslashes precedes a quote character. Thus, 2n backslashes followed by a quote encodes n backslashes and the end of the literal while 2n+1 backslashes followed by a quote encodes n backslashes followed by a quote character. # Examples ```jldoctest julia> println(raw"\\ \$x") \\ \$x julia> println(raw"\\"") " julia> println(raw"\\\\\\"") \\" julia> println(raw"\\\\x \\\\\\"") \\\\x \\" ``` """ macro raw_str(s); s; end """ escape_raw_string(s::AbstractString) escape_raw_string(io, s::AbstractString) Escape a string in the manner used for parsing raw string literals. For each double-quote (`"`) character in input string `s`, this function counts the number _n_ of preceding backslash (`\\`) characters, and then increases there the number of backslashes from _n_ to 2_n_+1 (even for _n_ = 0). It also doubles a sequence of backslashes at the end of the string. This escaping convention is used in raw strings and other non-standard string literals. (It also happens to be the escaping convention expected by the Microsoft C/C++ compiler runtime when it parses a command-line string into the argv[] array.) See also [`escape_string`](@ref). """ function escape_raw_string(io, str::AbstractString) escapes = 0 for c in str if c == '\\' escapes += 1 else if c == '"' # if one or more backslashes are followed by # a double quote then escape all backslashes # and the double quote escapes = escapes * 2 + 1 end while escapes > 0 write(io, '\\') escapes -= 1 end escapes = 0 write(io, c) end end # also escape any trailing backslashes, # so they do not affect the closing quote while escapes > 0 write(io, '\\') write(io, '\\') escapes -= 1 end end escape_raw_string(str::AbstractString) = sprint(escape_raw_string, str; sizehint = lastindex(str) + 2) ## multiline strings ## """ indentation(str::AbstractString; tabwidth=8) -> (Int, Bool) Calculate the width of leading white space. Return the width and a flag to indicate if the string is empty. # Examples ```jldoctest julia> Base.indentation("") (0, true) julia> Base.indentation(" a") (2, false) julia> Base.indentation("\\ta"; tabwidth=3) (3, false) ``` """ function indentation(str::AbstractString; tabwidth=8) count = 0 for ch in str if ch == ' ' count += 1 elseif ch == '\t' count = div(count + tabwidth, tabwidth) * tabwidth else return count, false end end count, true end """ unindent(str::AbstractString, indent::Int; tabwidth=8) Remove leading indentation from string. See also `indent` from the [`MultilineStrings` package](https://github.com/invenia/MultilineStrings.jl). # Examples ```jldoctest julia> Base.unindent(" a\\n b", 2) " a\\n b" julia> Base.unindent("\\ta\\n\\tb", 2, tabwidth=8) " a\\n b" ``` """ function unindent(str::AbstractString, indent::Int; tabwidth=8) indent == 0 && return str # Note: this loses the type of the original string buf = IOBuffer(sizehint=sizeof(str)) cutting = true col = 0 # current column (0 based) for ch in str if cutting if ch == ' ' col += 1 elseif ch == '\t' col = div(col + tabwidth, tabwidth) * tabwidth elseif ch == '\n' # Now we need to output enough indentation for i = 1:col-indent print(buf, ' ') end col = 0 print(buf, '\n') else cutting = false # Now we need to output enough indentation to get to # correct place for i = 1:col-indent print(buf, ' ') end col += 1 print(buf, ch) end elseif ch == '\t' # Handle internal tabs upd = div(col + tabwidth, tabwidth) * tabwidth # output the number of spaces that would have been seen # with original indentation for i = 1:(upd-col) print(buf, ' ') end col = upd elseif ch == '\n' cutting = true col = 0 print(buf, '\n') else col += 1 print(buf, ch) end end # If we were still "cutting" when we hit the end of the string, # we need to output the right number of spaces for the indentation if cutting for i = 1:col-indent print(buf, ' ') end end String(take!(buf)) end function String(a::AbstractVector{Char}) n = 0 for v in a n += ncodeunits(v) end out = _string_n(n) offs = 1 for v in a offs += __unsafe_string!(out, v, offs) end return out end function String(chars::AbstractVector{<:AbstractChar}) sprint(sizehint=length(chars)) do io for c in chars print(io, c) end end end L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/regex.jlzk# This file is a part of Julia. License is MIT: https://julialang.org/license ## object-oriented Regex interface ## include("pcre.jl") const DEFAULT_COMPILER_OPTS = PCRE.UTF | PCRE.MATCH_INVALID_UTF | PCRE.ALT_BSUX | PCRE.UCP const DEFAULT_MATCH_OPTS = PCRE.NO_UTF_CHECK """ Regex(pattern[, flags]) <: AbstractPattern A type representing a regular expression. `Regex` objects can be used to match strings with [`match`](@ref). `Regex` objects can be created using the [`@r_str`](@ref) string macro. The `Regex(pattern[, flags])` constructor is usually used if the `pattern` string needs to be interpolated. See the documentation of the string macro for details on flags. !!! note To escape interpolated variables use `\\Q` and `\\E` (e.g. `Regex("\\\\Q\$x\\\\E")`) """ mutable struct Regex <: AbstractPattern pattern::String compile_options::UInt32 match_options::UInt32 regex::Ptr{Cvoid} function Regex(pattern::AbstractString, compile_options::Integer, match_options::Integer) pattern = String(pattern) compile_options = UInt32(compile_options) match_options = UInt32(match_options) if (compile_options & ~PCRE.COMPILE_MASK) != 0 throw(ArgumentError("invalid regex compile options: $compile_options")) end if (match_options & ~PCRE.EXECUTE_MASK) !=0 throw(ArgumentError("invalid regex match options: $match_options")) end re = compile(new(pattern, compile_options, match_options, C_NULL)) finalizer(re) do re re.regex == C_NULL || PCRE.free_re(re.regex) end re end end function Regex(pattern::AbstractString, flags::AbstractString) compile_options = DEFAULT_COMPILER_OPTS match_options = DEFAULT_MATCH_OPTS for f in flags if f == 'a' # instruct pcre2 to treat the strings as simple bytes (aka "ASCII"), not char encodings compile_options &= ~PCRE.UCP # user can re-enable with (*UCP) compile_options &= ~PCRE.UTF # user can re-enable with (*UTF) compile_options &= ~PCRE.MATCH_INVALID_UTF # this would force on UTF match_options &= ~PCRE.NO_UTF_CHECK # if the user did force on UTF, we should check it for safety else compile_options |= f=='i' ? PCRE.CASELESS : f=='m' ? PCRE.MULTILINE : f=='s' ? PCRE.DOTALL : f=='x' ? PCRE.EXTENDED : throw(ArgumentError("unknown regex flag: $f")) end end Regex(pattern, compile_options, match_options) end Regex(pattern::AbstractString) = Regex(pattern, DEFAULT_COMPILER_OPTS, DEFAULT_MATCH_OPTS) function compile(regex::Regex) if regex.regex == C_NULL if PCRE.PCRE_COMPILE_LOCK === nothing regex.regex = PCRE.compile(regex.pattern, regex.compile_options) PCRE.jit_compile(regex.regex) else l = PCRE.PCRE_COMPILE_LOCK::Threads.SpinLock lock(l) try if regex.regex == C_NULL regex.regex = PCRE.compile(regex.pattern, regex.compile_options) PCRE.jit_compile(regex.regex) end finally unlock(l) end end end regex end """ @r_str -> Regex Construct a regex, such as `r"^[a-z]*\$"`, without interpolation and unescaping (except for quotation mark `"` which still has to be escaped). The regex also accepts one or more flags, listed after the ending quote, to change its behaviour: - `i` enables case-insensitive matching - `m` treats the `^` and `\$` tokens as matching the start and end of individual lines, as opposed to the whole string. - `s` allows the `.` modifier to match newlines. - `x` enables "comment mode": whitespace is enabled except when escaped with `\\`, and `#` is treated as starting a comment. - `a` enables ASCII mode (disables `UTF` and `UCP` modes). By default `\\B`, `\\b`, `\\D`, `\\d`, `\\S`, `\\s`, `\\W`, `\\w`, etc. match based on Unicode character properties. With this option, these sequences only match ASCII characters. This includes `\\u` also, which will emit the specified character value directly as a single byte, and not attempt to encode it into UTF-8. Importantly, this option allows matching against invalid UTF-8 strings, by treating both matcher and target as simple bytes (as if they were ISO/IEC 8859-1 / Latin-1 bytes) instead of as character encodings. In this case, this option is often combined with `s`. This option can be further refined by starting the pattern with (*UCP) or (*UTF). See [`Regex`](@ref) if interpolation is needed. # Examples ```jldoctest julia> match(r"a+.*b+.*?d\$"ism, "Goodbye,\\nOh, angry,\\nBad world\\n") RegexMatch("angry,\\nBad world") ``` This regex has the first three flags enabled. """ macro r_str(pattern, flags...) Regex(pattern, flags...) end function show(io::IO, re::Regex) imsx = PCRE.CASELESS|PCRE.MULTILINE|PCRE.DOTALL|PCRE.EXTENDED ac = PCRE.UTF|PCRE.MATCH_INVALID_UTF|PCRE.UCP am = PCRE.NO_UTF_CHECK opts = re.compile_options mopts = re.match_options default = ((opts & ~imsx) | ac) == DEFAULT_COMPILER_OPTS if default if (opts & ac) == ac default = mopts == DEFAULT_MATCH_OPTS elseif (opts & ac) == 0 default = mopts == (DEFAULT_MATCH_OPTS & ~am) else default = false end end if default print(io, "r\"") escape_raw_string(io, re.pattern) print(io, "\"") if (opts & PCRE.CASELESS ) != 0; print(io, "i"); end if (opts & PCRE.MULTILINE) != 0; print(io, "m"); end if (opts & PCRE.DOTALL ) != 0; print(io, "s"); end if (opts & PCRE.EXTENDED ) != 0; print(io, "x"); end if (opts & ac ) == 0; print(io, "a"); end else print(io, "Regex(") show(io, re.pattern) print(io, ", ") show(io, opts) print(io, ", ") show(io, mopts) print(io, ")") end end """ `AbstractMatch` objects are used to represent information about matches found in a string using an `AbstractPattern`. """ abstract type AbstractMatch end """ RegexMatch <: AbstractMatch A type representing a single match to a [`Regex`](@ref) found in a string. Typically created from the [`match`](@ref) function. The `match` field stores the substring of the entire matched string. The `captures` field stores the substrings for each capture group, indexed by number. To index by capture group name, the entire match object should be indexed instead, as shown in the examples. The location of the start of the match is stored in the `offset` field. The `offsets` field stores the locations of the start of each capture group, with 0 denoting a group that was not captured. This type can be used as an iterator over the capture groups of the `Regex`, yielding the substrings captured in each group. Because of this, the captures of a match can be destructured. If a group was not captured, `nothing` will be yielded instead of a substring. Methods that accept a `RegexMatch` object are defined for [`iterate`](@ref), [`length`](@ref), [`eltype`](@ref), [`keys`](@ref keys(::RegexMatch)), [`haskey`](@ref), and [`getindex`](@ref), where keys are the the names or numbers of a capture group. See [`keys`](@ref keys(::RegexMatch)) for more information. # Examples ```jldoctest julia> m = match(r"(?\\d+):(?\\d+)(am|pm)?", "11:30 in the morning") RegexMatch("11:30", hour="11", minute="30", 3=nothing) julia> m.match "11:30" julia> m.captures 3-element Vector{Union{Nothing, SubString{String}}}: "11" "30" nothing julia> m["minute"] "30" julia> hr, min, ampm = m; # destructure capture groups by iteration julia> hr "11" ``` """ struct RegexMatch <: AbstractMatch match::SubString{String} captures::Vector{Union{Nothing,SubString{String}}} offset::Int offsets::Vector{Int} regex::Regex end """ keys(m::RegexMatch) -> Vector Return a vector of keys for all capture groups of the underlying regex. A key is included even if the capture group fails to match. That is, `idx` will be in the return value even if `m[idx] == nothing`. Unnamed capture groups will have integer keys corresponding to their index. Named capture groups will have string keys. !!! compat "Julia 1.7" This method was added in Julia 1.7 # Examples ```jldoctest julia> keys(match(r"(?\\d+):(?\\d+)(am|pm)?", "11:30")) 3-element Vector{Any}: "hour" "minute" 3 ``` """ function keys(m::RegexMatch) idx_to_capture_name = PCRE.capture_names(m.regex.regex) return map(eachindex(m.captures)) do i # If the capture group is named, return it's name, else return it's index get(idx_to_capture_name, i, i) end end function show(io::IO, m::RegexMatch) print(io, "RegexMatch(") show(io, m.match) capture_keys = keys(m) if !isempty(capture_keys) print(io, ", ") for (i, capture_name) in enumerate(capture_keys) print(io, capture_name, "=") show(io, m.captures[i]) if i < length(m) print(io, ", ") end end end print(io, ")") end # Capture group extraction getindex(m::RegexMatch, idx::Integer) = m.captures[idx] function getindex(m::RegexMatch, name::Union{AbstractString,Symbol}) idx = PCRE.substring_number_from_name(m.regex.regex, name) idx <= 0 && error("no capture group named $name found in regex") m[idx] end haskey(m::RegexMatch, idx::Integer) = idx in eachindex(m.captures) function haskey(m::RegexMatch, name::Union{AbstractString,Symbol}) idx = PCRE.substring_number_from_name(m.regex.regex, name) return idx > 0 end iterate(m::RegexMatch, args...) = iterate(m.captures, args...) length(m::RegexMatch) = length(m.captures) eltype(m::RegexMatch) = eltype(m.captures) function occursin(r::Regex, s::AbstractString; offset::Integer=0) compile(r) return PCRE.exec_r(r.regex, String(s), offset, r.match_options) end function occursin(r::Regex, s::SubString{String}; offset::Integer=0) compile(r) return PCRE.exec_r(r.regex, s, offset, r.match_options) end """ startswith(s::AbstractString, prefix::Regex) Return `true` if `s` starts with the regex pattern, `prefix`. !!! note `startswith` does not compile the anchoring into the regular expression, but instead passes the anchoring as `match_option` to PCRE. If compile time is amortized, `occursin(r"^...", s)` is faster than `startswith(s, r"...")`. See also [`occursin`](@ref) and [`endswith`](@ref). !!! compat "Julia 1.2" This method requires at least Julia 1.2. # Examples ```jldoctest julia> startswith("JuliaLang", r"Julia|Romeo") true ``` """ function startswith(s::AbstractString, r::Regex) compile(r) return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ANCHORED) end function startswith(s::SubString{String}, r::Regex) compile(r) return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ANCHORED) end """ endswith(s::AbstractString, suffix::Regex) Return `true` if `s` ends with the regex pattern, `suffix`. !!! note `endswith` does not compile the anchoring into the regular expression, but instead passes the anchoring as `match_option` to PCRE. If compile time is amortized, `occursin(r"...\$", s)` is faster than `endswith(s, r"...")`. See also [`occursin`](@ref) and [`startswith`](@ref). !!! compat "Julia 1.2" This method requires at least Julia 1.2. # Examples ```jldoctest julia> endswith("JuliaLang", r"Lang|Roberts") true ``` """ function endswith(s::AbstractString, r::Regex) compile(r) return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ENDANCHORED) end function endswith(s::SubString{String}, r::Regex) compile(r) return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ENDANCHORED) end function chopprefix(s::AbstractString, prefix::Regex) m = match(prefix, s, firstindex(s), PCRE.ANCHORED) m === nothing && return SubString(s) return SubString(s, ncodeunits(m.match) + 1) end function chopsuffix(s::AbstractString, suffix::Regex) m = match(suffix, s, firstindex(s), PCRE.ENDANCHORED) m === nothing && return SubString(s) isempty(m.match) && return SubString(s) return SubString(s, firstindex(s), prevind(s, m.offset)) end """ match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) Search for the first match of the regular expression `r` in `s` and return a [`RegexMatch`](@ref) object containing the match, or nothing if the match failed. The matching substring can be retrieved by accessing `m.match` and the captured sequences can be retrieved by accessing `m.captures` The optional `idx` argument specifies an index at which to start the search. # Examples ```jldoctest julia> rx = r"a(.)a" r"a(.)a" julia> m = match(rx, "cabac") RegexMatch("aba", 1="b") julia> m.captures 1-element Vector{Union{Nothing, SubString{String}}}: "b" julia> m.match "aba" julia> match(rx, "cabac", 3) === nothing true ``` """ function match end function match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32=UInt32(0)) compile(re) opts = re.match_options | add_opts matched, data = PCRE.exec_r_data(re.regex, str, idx-1, opts) if !matched PCRE.free_match_data(data) return nothing end n = div(PCRE.ovec_length(data), 2) - 1 p = PCRE.ovec_ptr(data) mat = SubString(str, unsafe_load(p, 1)+1, prevind(str, unsafe_load(p, 2)+1)) cap = Union{Nothing,SubString{String}}[unsafe_load(p,2i+1) == PCRE.UNSET ? nothing : SubString(str, unsafe_load(p,2i+1)+1, prevind(str, unsafe_load(p,2i+2)+1)) for i=1:n] off = Int[ unsafe_load(p,2i+1)+1 for i=1:n ] result = RegexMatch(mat, cap, unsafe_load(p,1)+1, off, re) PCRE.free_match_data(data) return result end match(r::Regex, s::AbstractString) = match(r, s, firstindex(s)) match(r::Regex, s::AbstractString, i::Integer) = throw(ArgumentError( "regex matching is only available for the String type; use String(s) to convert" )) findnext(re::Regex, str::Union{String,SubString}, idx::Integer) = _findnext_re(re, str, idx, C_NULL) # TODO: return only start index and update deprecation function _findnext_re(re::Regex, str::Union{String,SubString}, idx::Integer, match_data::Ptr{Cvoid}) if idx > nextind(str,lastindex(str)) throw(BoundsError()) end opts = re.match_options compile(re) alloc = match_data == C_NULL if alloc matched, data = PCRE.exec_r_data(re.regex, str, idx-1, opts) else matched = PCRE.exec(re.regex, str, idx-1, opts, match_data) data = match_data end if matched p = PCRE.ovec_ptr(data) ans = (Int(unsafe_load(p,1))+1):prevind(str,Int(unsafe_load(p,2))+1) else ans = nothing end alloc && PCRE.free_match_data(data) return ans end findnext(r::Regex, s::AbstractString, idx::Integer) = throw(ArgumentError( "regex search is only available for the String type; use String(s) to convert" )) findfirst(r::Regex, s::AbstractString) = findnext(r,s,firstindex(s)) """ findall(c::AbstractChar, s::AbstractString) Return a vector `I` of the indices of `s` where `s[i] == c`. If there are no such elements in `s`, return an empty array. # Examples ```jldoctest julia> findall('a', "batman") 2-element Vector{Int64}: 2 5 ``` !!! compat "Julia 1.7" This method requires at least Julia 1.7. """ findall(c::AbstractChar, s::AbstractString) = findall(isequal(c),s) """ count( pattern::Union{AbstractChar,AbstractString,AbstractPattern}, string::AbstractString; overlap::Bool = false, ) Return the number of matches for `pattern` in `string`. This is equivalent to calling `length(findall(pattern, string))` but more efficient. If `overlap=true`, the matching sequences are allowed to overlap indices in the original string, otherwise they must be from disjoint character ranges. !!! compat "Julia 1.3" This method requires at least Julia 1.3. !!! compat "Julia 1.7" Using a character as the pattern requires at least Julia 1.7. # Examples ```jldoctest julia> count('a', "JuliaLang") 2 julia> count(r"a(.)a", "cabacabac", overlap=true) 3 julia> count(r"a(.)a", "cabacabac") 2 ``` """ function count(t::Union{AbstractChar,AbstractString,AbstractPattern}, s::AbstractString; overlap::Bool=false) n = 0 i, e = firstindex(s), lastindex(s) while true r = findnext(t, s, i) isnothing(r) && break n += 1 j = overlap || isempty(r) ? first(r) : last(r) j > e && break @inbounds i = nextind(s, j) end return n end """ SubstitutionString(substr) <: AbstractString Stores the given string `substr` as a `SubstitutionString`, for use in regular expression substitutions. Most commonly constructed using the [`@s_str`](@ref) macro. # Examples ```jldoctest julia> SubstitutionString("Hello \\\\g, it's \\\\1") s"Hello \\g, it's \\1" julia> subst = s"Hello \\g, it's \\1" s"Hello \\g, it's \\1" julia> typeof(subst) SubstitutionString{String} ``` """ struct SubstitutionString{T<:AbstractString} <: AbstractString string::T end ncodeunits(s::SubstitutionString) = ncodeunits(s.string)::Int codeunit(s::SubstitutionString) = codeunit(s.string)::CodeunitType codeunit(s::SubstitutionString, i::Integer) = codeunit(s.string, i)::Union{UInt8, UInt16, UInt32} isvalid(s::SubstitutionString, i::Integer) = isvalid(s.string, i)::Bool iterate(s::SubstitutionString, i::Integer...) = iterate(s.string, i...)::Union{Nothing,Tuple{AbstractChar,Int}} function show(io::IO, s::SubstitutionString) print(io, "s\"") escape_raw_string(io, s.string) print(io, "\"") end """ @s_str -> SubstitutionString Construct a substitution string, used for regular expression substitutions. Within the string, sequences of the form `\\N` refer to the Nth capture group in the regex, and `\\g` refers to a named capture group with name `groupname`. # Examples ```jldoctest julia> msg = "#Hello# from Julia"; julia> replace(msg, r"#(.+)# from (?\\w+)" => s"FROM: \\g; MESSAGE: \\1") "FROM: Julia; MESSAGE: Hello" ``` """ macro s_str(string) SubstitutionString(string) end # replacement struct RegexAndMatchData re::Regex match_data::Ptr{Cvoid} RegexAndMatchData(re::Regex) = (compile(re); new(re, PCRE.create_match_data(re.regex))) end findnext(pat::RegexAndMatchData, str, i) = _findnext_re(pat.re, str, i, pat.match_data) _pat_replacer(r::Regex) = RegexAndMatchData(r) _free_pat_replacer(r::RegexAndMatchData) = PCRE.free_match_data(r.match_data) replace_err(repl) = error("Bad replacement string: $repl") function _write_capture(io::IO, group::Int, str, r, re::RegexAndMatchData) len = PCRE.substring_length_bynumber(re.match_data, group) # in the case of an optional group that doesn't match, len == 0 len == 0 && return ensureroom(io, len+1) PCRE.substring_copy_bynumber(re.match_data, group, pointer(io.data, io.ptr), len+1) io.ptr += len io.size = max(io.size, io.ptr - 1) nothing end function _write_capture(io::IO, group::Int, str, r, re) group == 0 || replace_err("pattern is not a Regex") return print(io, SubString(str, r)) end const SUB_CHAR = '\\' const GROUP_CHAR = 'g' const KEEP_ESC = [SUB_CHAR, GROUP_CHAR, '0':'9'...] function _replace(io, repl_s::SubstitutionString, str, r, re) LBRACKET = '<' RBRACKET = '>' repl = unescape_string(repl_s.string, KEEP_ESC) i = firstindex(repl) e = lastindex(repl) while i <= e if repl[i] == SUB_CHAR next_i = nextind(repl, i) next_i > e && replace_err(repl) if repl[next_i] == SUB_CHAR write(io, SUB_CHAR) i = nextind(repl, next_i) elseif isdigit(repl[next_i]) group = parse(Int, repl[next_i]) i = nextind(repl, next_i) while i <= e if isdigit(repl[i]) group = 10group + parse(Int, repl[i]) i = nextind(repl, i) else break end end _write_capture(io, group, str, r, re) elseif repl[next_i] == GROUP_CHAR i = nextind(repl, next_i) if i > e || repl[i] != LBRACKET replace_err(repl) end i = nextind(repl, i) i > e && replace_err(repl) groupstart = i while repl[i] != RBRACKET i = nextind(repl, i) i > e && replace_err(repl) end groupname = SubString(repl, groupstart, prevind(repl, i)) if all(isdigit, groupname) group = parse(Int, groupname) elseif re isa RegexAndMatchData group = PCRE.substring_number_from_name(re.re.regex, groupname) group < 0 && replace_err("Group $groupname not found in regex $(re.re)") else group = -1 end _write_capture(io, group, str, r, re) i = nextind(repl, i) else replace_err(repl) end else write(io, repl[i]) i = nextind(repl, i) end end end struct RegexMatchIterator regex::Regex string::String overlap::Bool function RegexMatchIterator(regex::Regex, string::AbstractString, ovr::Bool=false) new(regex, string, ovr) end end compile(itr::RegexMatchIterator) = (compile(itr.regex); itr) eltype(::Type{RegexMatchIterator}) = RegexMatch IteratorSize(::Type{RegexMatchIterator}) = SizeUnknown() function iterate(itr::RegexMatchIterator, (offset,prevempty)=(1,false)) opts_nonempty = UInt32(PCRE.ANCHORED | PCRE.NOTEMPTY_ATSTART) while true mat = match(itr.regex, itr.string, offset, prevempty ? opts_nonempty : UInt32(0)) if mat === nothing if prevempty && offset <= sizeof(itr.string) offset = nextind(itr.string, offset) prevempty = false continue else break end else if itr.overlap if !isempty(mat.match) offset = nextind(itr.string, mat.offset) else offset = mat.offset end else offset = mat.offset + ncodeunits(mat.match) end return (mat, (offset, isempty(mat.match))) end end nothing end """ eachmatch(r::Regex, s::AbstractString; overlap::Bool=false) Search for all matches of the regular expression `r` in `s` and return an iterator over the matches. If `overlap` is `true`, the matching sequences are allowed to overlap indices in the original string, otherwise they must be from distinct character ranges. # Examples ```jldoctest julia> rx = r"a.a" r"a.a" julia> m = eachmatch(rx, "a1a2a3a") Base.RegexMatchIterator(r"a.a", "a1a2a3a", false) julia> collect(m) 2-element Vector{RegexMatch}: RegexMatch("a1a") RegexMatch("a3a") julia> collect(eachmatch(rx, "a1a2a3a", overlap = true)) 3-element Vector{RegexMatch}: RegexMatch("a1a") RegexMatch("a2a") RegexMatch("a3a") ``` """ eachmatch(re::Regex, str::AbstractString; overlap = false) = RegexMatchIterator(re, str, overlap) ## comparison ## function ==(a::Regex, b::Regex) a.pattern == b.pattern && a.compile_options == b.compile_options && a.match_options == b.match_options end ## hash ## const hashre_seed = UInt === UInt64 ? 0x67e195eb8555e72d : 0xe32373e4 function hash(r::Regex, h::UInt) h += hashre_seed h = hash(r.pattern, h) h = hash(r.compile_options, h) h = hash(r.match_options, h) end ## String operations ## """ *(s::Regex, t::Union{Regex,AbstractString,AbstractChar}) -> Regex *(s::Union{Regex,AbstractString,AbstractChar}, t::Regex) -> Regex Concatenate regexes, strings and/or characters, producing a [`Regex`](@ref). String and character arguments must be matched exactly in the resulting regex, meaning that the contained characters are devoid of any special meaning (they are quoted with "\\Q" and "\\E"). !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> match(r"Hello|Good bye" * ' ' * "world", "Hello world") RegexMatch("Hello world") julia> r = r"a|b" * "c|d" r"(?:a|b)\\Qc|d\\E" julia> match(r, "ac") == nothing true julia> match(r, "ac|d") RegexMatch("ac|d") ``` """ function *(r1::Union{Regex,AbstractString,AbstractChar}, rs::Union{Regex,AbstractString,AbstractChar}...) mask = PCRE.CASELESS | PCRE.MULTILINE | PCRE.DOTALL | PCRE.EXTENDED # imsx match_opts = nothing # all args must agree on this compile_opts = nothing # all args must agree on this shared = mask for r in (r1, rs...) r isa Regex || continue if match_opts === nothing match_opts = r.match_options compile_opts = r.compile_options & ~mask else r.match_options == match_opts && r.compile_options & ~mask == compile_opts || throw(ArgumentError("cannot multiply regexes: incompatible options")) end shared &= r.compile_options end unshared = mask & ~shared Regex(string(wrap_string(r1, unshared), wrap_string.(rs, Ref(unshared))...), compile_opts | shared, match_opts) end *(r::Regex) = r # avoids wrapping r in a useless subpattern wrap_string(r::Regex, unshared::UInt32) = string("(?", regex_opts_str(r.compile_options & unshared), ':', r.pattern, ')') # if s contains raw"\E", split '\' and 'E' within two distinct \Q...\E groups: wrap_string(s::AbstractString, ::UInt32) = string("\\Q", replace(s, raw"\E" => raw"\\E\QE"), "\\E") wrap_string(s::AbstractChar, ::UInt32) = string("\\Q", s, "\\E") regex_opts_str(opts) = (isassigned(_regex_opts_str) ? _regex_opts_str[] : init_regex())[opts] # UInt32 to String mapping for some compile options const _regex_opts_str = Ref{ImmutableDict{UInt32,String}}() @noinline init_regex() = _regex_opts_str[] = foldl(0:15, init=ImmutableDict{UInt32,String}()) do d, o opt = UInt32(0) str = "" if o & 1 != 0 opt |= PCRE.CASELESS str *= 'i' end if o & 2 != 0 opt |= PCRE.MULTILINE str *= 'm' end if o & 4 != 0 opt |= PCRE.DOTALL str *= 's' end if o & 8 != 0 opt |= PCRE.EXTENDED str *= 'x' end ImmutableDict(d, opt => str) end """ ^(s::Regex, n::Integer) -> Regex Repeat a regex `n` times. !!! compat "Julia 1.3" This method requires at least Julia 1.3. # Examples ```jldoctest julia> r"Test "^2 r"(?:Test ){2}" julia> match(r"Test "^2, "Test Test ") RegexMatch("Test Test ") ``` """ ^(r::Regex, i::Integer) = Regex(string("(?:", r.pattern, "){$i}"), r.compile_options, r.match_options) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/pcre.jl๑"# This file is a part of Julia. License is MIT: https://julialang.org/license ## low-level pcre2 interface ## module PCRE import ..RefValue # include($BUILDROOT/base/pcre_h.jl) include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "pcre_h.jl")) const PCRE_LIB = "libpcre2-8" function create_match_context() JIT_STACK_START_SIZE = 32768 JIT_STACK_MAX_SIZE = 1048576 jit_stack = ccall((:pcre2_jit_stack_create_8, PCRE_LIB), Ptr{Cvoid}, (Csize_t, Csize_t, Ptr{Cvoid}), JIT_STACK_START_SIZE, JIT_STACK_MAX_SIZE, C_NULL) ctx = ccall((:pcre2_match_context_create_8, PCRE_LIB), Ptr{Cvoid}, (Ptr{Cvoid},), C_NULL) ccall((:pcre2_jit_stack_assign_8, PCRE_LIB), Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), ctx, C_NULL, jit_stack) return ctx end THREAD_MATCH_CONTEXTS::Vector{Ptr{Cvoid}} = [C_NULL] PCRE_COMPILE_LOCK = nothing _tid() = Int(ccall(:jl_threadid, Int16, ())) + 1 _mth() = Int(Core.Intrinsics.atomic_pointerref(cglobal(:jl_n_threads, Cint), :acquire)) function get_local_match_context() tid = _tid() ctxs = THREAD_MATCH_CONTEXTS if length(ctxs) < tid # slow path to allocate it l = PCRE_COMPILE_LOCK::Threads.SpinLock lock(l) try ctxs = THREAD_MATCH_CONTEXTS if length(ctxs) < tid global THREAD_MATCH_CONTEXTS = ctxs = copyto!(fill(C_NULL, length(ctxs) + _mth()), ctxs) end finally unlock(l) end end ctx = @inbounds ctxs[tid] if ctx == C_NULL # slow path to allocate it ctx = create_match_context() THREAD_MATCH_CONTEXTS[tid] = ctx end return ctx end # supported options for different use cases # arguments to pcre2_compile const COMPILE_MASK = ALT_BSUX | ALT_CIRCUMFLEX | ALT_VERBNAMES | ANCHORED | # AUTO_CALLOUT | CASELESS | DOLLAR_ENDONLY | DOTALL | # DUPNAMES | ENDANCHORED | EXTENDED | EXTENDED_MORE | FIRSTLINE | LITERAL | MATCH_INVALID_UTF | MATCH_UNSET_BACKREF | MULTILINE | NEVER_BACKSLASH_C | NEVER_UCP | NEVER_UTF | NO_AUTO_CAPTURE | NO_AUTO_POSSESS | NO_DOTSTAR_ANCHOR | NO_START_OPTIMIZE | NO_UTF_CHECK | UCP | UNGREEDY | USE_OFFSET_LIMIT | UTF # arguments to pcre2_set_newline const COMPILE_NEWLINE_MASK = ( NEWLINE_CR, NEWLINE_LF, NEWLINE_CRLF, NEWLINE_ANY, NEWLINE_ANYCRLF, NEWLINE_NUL) # arguments to pcre2_set_compile_extra_options const COMPILE_EXTRA_MASK = EXTRA_ALLOW_SURROGATE_ESCAPES | EXTRA_ALT_BSUX | EXTRA_BAD_ESCAPE_IS_LITERAL | EXTRA_ESCAPED_CR_IS_LF | EXTRA_MATCH_LINE | EXTRA_MATCH_WORD # arguments to match const EXECUTE_MASK = # ANCHORED | # COPY_MATCHED_SUBJECT | # ENDANCHORED | NOTBOL | NOTEMPTY | NOTEMPTY_ATSTART | NOTEOL | # NO_JIT | NO_START_OPTIMIZE | NO_UTF_CHECK | PARTIAL_HARD | PARTIAL_SOFT const UNSET = ~Csize_t(0) # Indicates that an output vector element is unset function info(regex::Ptr{Cvoid}, what::Integer, ::Type{T}) where T buf = RefValue{T}() ret = ccall((:pcre2_pattern_info_8, PCRE_LIB), Cint, (Ptr{Cvoid}, UInt32, Ptr{Cvoid}), regex, what, buf) if ret != 0 error(ret == ERROR_NULL ? "PCRE error: NULL regex object" : ret == ERROR_BADMAGIC ? "PCRE error: invalid regex object" : ret == ERROR_BADOPTION ? "PCRE error: invalid option flags" : "PCRE error: unknown error ($ret)") end return buf[] end function ovec_length(match_data) n = ccall((:pcre2_get_ovector_count_8, PCRE_LIB), UInt32, (Ptr{Cvoid},), match_data) return 2Int(n) end function ovec_ptr(match_data) ptr = ccall((:pcre2_get_ovector_pointer_8, PCRE_LIB), Ptr{Csize_t}, (Ptr{Cvoid},), match_data) return ptr end function compile(pattern::AbstractString, options::Integer) if !(pattern isa Union{String,SubString{String}}) pattern = String(pattern) end errno = RefValue{Cint}(0) erroff = RefValue{Csize_t}(0) re_ptr = ccall((:pcre2_compile_8, PCRE_LIB), Ptr{Cvoid}, (Ptr{UInt8}, Csize_t, UInt32, Ref{Cint}, Ref{Csize_t}, Ptr{Cvoid}), pattern, ncodeunits(pattern), options, errno, erroff, C_NULL) if re_ptr == C_NULL error("PCRE compilation error: $(err_message(errno[])) at offset $(erroff[])") end return re_ptr end function jit_compile(regex::Ptr{Cvoid}) errno = ccall((:pcre2_jit_compile_8, PCRE_LIB), Cint, (Ptr{Cvoid}, UInt32), regex, JIT_COMPLETE) errno == 0 && return true errno == ERROR_JIT_BADOPTION && return false error("PCRE JIT error: $(err_message(errno))") end free_match_data(match_data) = ccall((:pcre2_match_data_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), match_data) free_re(re) = ccall((:pcre2_code_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), re) free_jit_stack(stack) = ccall((:pcre2_jit_stack_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), stack) free_match_context(context) = ccall((:pcre2_match_context_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), context) function err_message(errno::Integer) buffer = Vector{UInt8}(undef, 1024) ret = ccall((:pcre2_get_error_message_8, PCRE_LIB), Cint, (Cint, Ptr{UInt8}, Csize_t), errno, buffer, length(buffer)) ret == ERROR_BADDATA && error("PCRE error: invalid errno ($errno)") # TODO: seems like there should be a better way to get this string return GC.@preserve buffer unsafe_string(pointer(buffer)) end function exec(re, subject, offset, options, match_data) if !(subject isa Union{String,SubString{String}}) subject = String(subject) end rc = ccall((:pcre2_match_8, PCRE_LIB), Cint, (Ptr{Cvoid}, Ptr{UInt8}, Csize_t, Csize_t, UInt32, Ptr{Cvoid}, Ptr{Cvoid}), re, subject, ncodeunits(subject), offset, options, match_data, get_local_match_context()) # rc == -1 means no match, -2 means partial match. rc < -2 && error("PCRE.exec error: $(err_message(rc))") return rc >= 0 end function exec_r(re, subject, offset, options) match_data = create_match_data(re) ans = exec(re, subject, offset, options, match_data) free_match_data(match_data) return ans end function exec_r_data(re, subject, offset, options) match_data = create_match_data(re) ans = exec(re, subject, offset, options, match_data) return ans, match_data end function create_match_data(re) p = ccall((:pcre2_match_data_create_from_pattern_8, PCRE_LIB), Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}), re, C_NULL) p == C_NULL && error("PCRE error: could not allocate memory") return p end function substring_number_from_name(re, name) n = ccall((:pcre2_substring_number_from_name_8, PCRE_LIB), Cint, (Ptr{Cvoid}, Cstring), re, name) return Int(n) end function substring_length_bynumber(match_data, number) s = RefValue{Csize_t}() rc = ccall((:pcre2_substring_length_bynumber_8, PCRE_LIB), Cint, (Ptr{Cvoid}, Cint, Ref{Csize_t}), match_data, number, s) if rc < 0 rc == ERROR_UNSET && return 0 error("PCRE error: $(err_message(rc))") end return Int(s[]) end function substring_copy_bynumber(match_data, number, buf, buf_size) s = RefValue{Csize_t}(buf_size) rc = ccall((:pcre2_substring_copy_bynumber_8, PCRE_LIB), Cint, (Ptr{Cvoid}, UInt32, Ptr{UInt8}, Ref{Csize_t}), match_data, number, buf, s) rc < 0 && error("PCRE error: $(err_message(rc))") return Int(s[]) end function capture_names(re) name_count = info(re, INFO_NAMECOUNT, UInt32) name_entry_size = info(re, INFO_NAMEENTRYSIZE, UInt32) nametable_ptr = info(re, INFO_NAMETABLE, Ptr{UInt8}) names = Dict{Int,String}() for i = 1:name_count offset = (i-1)*name_entry_size + 1 # The capture group index corresponding to name 'i' is stored as a # big-endian 16-bit value. high_byte = UInt16(unsafe_load(nametable_ptr, offset)) low_byte = UInt16(unsafe_load(nametable_ptr, offset+1)) idx = (high_byte << 8) | low_byte # The capture group name is a null-terminated string located directly # after the index. names[idx] = unsafe_string(nametable_ptr+offset+1) end return names end end # module O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./pcre_h.jl/const ALLOW_EMPTY_CLASS = UInt32(0x00000001) const ALT_BSUX = UInt32(0x00000002) const ALT_CIRCUMFLEX = UInt32(0x00200000) const ALT_VERBNAMES = UInt32(0x00400000) const ANCHORED = UInt32(0x80000000) const AUTO_CALLOUT = UInt32(0x00000004) const BSR_ANYCRLF = UInt32(2) const BSR_UNICODE = UInt32(1) const CALLOUT_BACKTRACK = UInt32(0x00000002) const CALLOUT_STARTMATCH = UInt32(0x00000001) const CASELESS = UInt32(0x00000008) const CODE_UNIT_WIDTH = UInt32(8) const CONFIG_BSR = UInt32(0) const CONFIG_COMPILED_WIDTHS = UInt32(14) const CONFIG_DEPTHLIMIT = UInt32(7) const CONFIG_HEAPLIMIT = UInt32(12) const CONFIG_JIT = UInt32(1) const CONFIG_JITTARGET = UInt32(2) const CONFIG_LINKSIZE = UInt32(3) const CONFIG_MATCHLIMIT = UInt32(4) const CONFIG_NEVER_BACKSLASH_C = UInt32(13) const CONFIG_NEWLINE = UInt32(5) const CONFIG_PARENSLIMIT = UInt32(6) const CONFIG_RECURSIONLIMIT = UInt32(7) const CONFIG_STACKRECURSE = UInt32(8) const CONFIG_TABLES_LENGTH = UInt32(15) const CONFIG_UNICODE = UInt32(9) const CONFIG_UNICODE_VERSION = UInt32(10) const CONFIG_VERSION = UInt32(11) const CONVERT_GLOB = UInt32(0x00000010) const CONVERT_GLOB_NO_STARSTAR = UInt32(0x00000050) const CONVERT_GLOB_NO_WILD_SEPARATOR = UInt32(0x00000030) const CONVERT_NO_UTF_CHECK = UInt32(0x00000002) const CONVERT_POSIX_BASIC = UInt32(0x00000004) const CONVERT_POSIX_EXTENDED = UInt32(0x00000008) const CONVERT_UTF = UInt32(0x00000001) const COPY_MATCHED_SUBJECT = UInt32(0x00004000) const DFA_RESTART = UInt32(0x00000040) const DFA_SHORTEST = UInt32(0x00000080) const DOLLAR_ENDONLY = UInt32(0x00000010) const DOTALL = UInt32(0x00000020) const DUPNAMES = UInt32(0x00000040) const ENDANCHORED = UInt32(0x20000000) const ERROR_ALPHA_ASSERTION_UNKNOWN = Cint(195) const ERROR_BACKSLASH_C_CALLER_DISABLED = Cint(183) const ERROR_BACKSLASH_C_LIBRARY_DISABLED = Cint(185) const ERROR_BACKSLASH_C_SYNTAX = Cint(168) const ERROR_BACKSLASH_G_SYNTAX = Cint(157) const ERROR_BACKSLASH_K_IN_LOOKAROUND = Cint(199) const ERROR_BACKSLASH_K_SYNTAX = Cint(169) const ERROR_BACKSLASH_N_IN_CLASS = Cint(171) const ERROR_BACKSLASH_O_MISSING_BRACE = Cint(155) const ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG = Cint(177) const ERROR_BADDATA = Cint((-29)) const ERROR_BADMAGIC = Cint((-31)) const ERROR_BADMODE = Cint((-32)) const ERROR_BADOFFSET = Cint((-33)) const ERROR_BADOFFSETLIMIT = Cint((-56)) const ERROR_BADOPTION = Cint((-34)) const ERROR_BADREPESCAPE = Cint((-57)) const ERROR_BADREPLACEMENT = Cint((-35)) const ERROR_BADSERIALIZEDDATA = Cint((-62)) const ERROR_BADSUBSPATTERN = Cint((-60)) const ERROR_BADSUBSTITUTION = Cint((-59)) const ERROR_BADUTFOFFSET = Cint((-36)) const ERROR_BAD_LITERAL_OPTIONS = Cint(192) const ERROR_BAD_OPTIONS = Cint(117) const ERROR_BAD_RELATIVE_REFERENCE = Cint(129) const ERROR_BAD_SUBPATTERN_REFERENCE = Cint(115) const ERROR_CALLOUT = Cint((-37)) const ERROR_CALLOUT_BAD_STRING_DELIMITER = Cint(182) const ERROR_CALLOUT_NO_STRING_DELIMITER = Cint(181) const ERROR_CALLOUT_NUMBER_TOO_BIG = Cint(138) const ERROR_CALLOUT_STRING_TOO_LONG = Cint(172) const ERROR_CLASS_INVALID_RANGE = Cint(150) const ERROR_CLASS_RANGE_ORDER = Cint(108) const ERROR_CODE_POINT_TOO_BIG = Cint(134) const ERROR_CONDITION_ASSERTION_EXPECTED = Cint(128) const ERROR_CONDITION_ATOMIC_ASSERTION_EXPECTED = Cint(198) const ERROR_CONVERT_SYNTAX = Cint((-64)) const ERROR_DEFINE_TOO_MANY_BRANCHES = Cint(154) const ERROR_DEPTHLIMIT = Cint((-53)) const ERROR_DFA_BADRESTART = Cint((-38)) const ERROR_DFA_RECURSE = Cint((-39)) const ERROR_DFA_UCOND = Cint((-40)) const ERROR_DFA_UFUNC = Cint((-41)) const ERROR_DFA_UINVALID_UTF = Cint((-66)) const ERROR_DFA_UITEM = Cint((-42)) const ERROR_DFA_WSSIZE = Cint((-43)) const ERROR_DUPLICATE_SUBPATTERN_NAME = Cint(143) const ERROR_END_BACKSLASH = Cint(101) const ERROR_END_BACKSLASH_C = Cint(102) const ERROR_ESCAPE_INVALID_IN_CLASS = Cint(107) const ERROR_ESCAPE_INVALID_IN_VERB = Cint(140) const ERROR_HEAPLIMIT = Cint((-63)) const ERROR_HEAP_FAILED = Cint(121) const ERROR_INTERNAL = Cint((-44)) const ERROR_INTERNAL_BAD_CODE = Cint(189) const ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS = Cint(180) const ERROR_INTERNAL_BAD_CODE_IN_SKIP = Cint(190) const ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS = Cint(170) const ERROR_INTERNAL_CODE_OVERFLOW = Cint(123) const ERROR_INTERNAL_DUPMATCH = Cint((-65)) const ERROR_INTERNAL_MISSING_SUBPATTERN = Cint(153) const ERROR_INTERNAL_OVERRAN_WORKSPACE = Cint(152) const ERROR_INTERNAL_PARSED_OVERFLOW = Cint(163) const ERROR_INTERNAL_STUDY_ERROR = Cint(131) const ERROR_INTERNAL_UNEXPECTED_REPEAT = Cint(110) const ERROR_INTERNAL_UNKNOWN_NEWLINE = Cint(156) const ERROR_INVALID_AFTER_PARENS_QUERY = Cint(111) const ERROR_INVALID_HEXADECIMAL = Cint(167) const ERROR_INVALID_HYPHEN_IN_OPTIONS = Cint(194) const ERROR_INVALID_OCTAL = Cint(164) const ERROR_INVALID_SUBPATTERN_NAME = Cint(144) const ERROR_JIT_BADOPTION = Cint((-45)) const ERROR_JIT_STACKLIMIT = Cint((-46)) const ERROR_LOOKBEHIND_INVALID_BACKSLASH_C = Cint(136) const ERROR_LOOKBEHIND_NOT_FIXED_LENGTH = Cint(125) const ERROR_LOOKBEHIND_TOO_COMPLICATED = Cint(135) const ERROR_LOOKBEHIND_TOO_LONG = Cint(187) const ERROR_MALFORMED_UNICODE_PROPERTY = Cint(146) const ERROR_MARK_MISSING_ARGUMENT = Cint(166) const ERROR_MATCHLIMIT = Cint((-47)) const ERROR_MISSING_CALLOUT_CLOSING = Cint(139) const ERROR_MISSING_CLOSING_PARENTHESIS = Cint(114) const ERROR_MISSING_COMMENT_CLOSING = Cint(118) const ERROR_MISSING_CONDITION_CLOSING = Cint(124) const ERROR_MISSING_NAME_TERMINATOR = Cint(142) const ERROR_MISSING_OCTAL_OR_HEX_DIGITS = Cint(178) const ERROR_MISSING_SQUARE_BRACKET = Cint(106) const ERROR_MIXEDTABLES = Cint((-30)) const ERROR_NOMATCH = Cint((-1)) const ERROR_NOMEMORY = Cint((-48)) const ERROR_NOSUBSTRING = Cint((-49)) const ERROR_NOUNIQUESUBSTRING = Cint((-50)) const ERROR_NO_SURROGATES_IN_UTF16 = Cint(191) const ERROR_NULL = Cint((-51)) const ERROR_NULL_PATTERN = Cint(116) const ERROR_OCTAL_BYTE_TOO_BIG = Cint(151) const ERROR_PARENS_QUERY_R_MISSING_CLOSING = Cint(158) const ERROR_PARENTHESES_NEST_TOO_DEEP = Cint(119) const ERROR_PARENTHESES_STACK_CHECK = Cint(133) const ERROR_PARTIAL = Cint((-2)) const ERROR_PATTERN_STRING_TOO_LONG = Cint(188) const ERROR_PATTERN_TOO_COMPLICATED = Cint(186) const ERROR_PATTERN_TOO_LARGE = Cint(120) const ERROR_POSIX_CLASS_NOT_IN_CLASS = Cint(112) const ERROR_POSIX_NO_SUPPORT_COLLATING = Cint(113) const ERROR_QUANTIFIER_INVALID = Cint(109) const ERROR_QUANTIFIER_OUT_OF_ORDER = Cint(104) const ERROR_QUANTIFIER_TOO_BIG = Cint(105) const ERROR_QUERY_BARJX_NEST_TOO_DEEP = Cint(184) const ERROR_RECURSELOOP = Cint((-52)) const ERROR_RECURSIONLIMIT = Cint((-53)) const ERROR_REPMISSINGBRACE = Cint((-58)) const ERROR_SCRIPT_RUN_NOT_AVAILABLE = Cint(196) const ERROR_SUBPATTERN_NAMES_MISMATCH = Cint(165) const ERROR_SUBPATTERN_NAME_EXPECTED = Cint(162) const ERROR_SUBPATTERN_NAME_TOO_LONG = Cint(148) const ERROR_SUBPATTERN_NUMBER_TOO_BIG = Cint(161) const ERROR_SUPPORTED_ONLY_IN_UNICODE = Cint(193) const ERROR_TOOMANYREPLACE = Cint((-61)) const ERROR_TOO_MANY_CAPTURES = Cint(197) const ERROR_TOO_MANY_CONDITION_BRANCHES = Cint(127) const ERROR_TOO_MANY_NAMED_SUBPATTERNS = Cint(149) const ERROR_UCP_IS_DISABLED = Cint(175) const ERROR_UNAVAILABLE = Cint((-54)) const ERROR_UNICODE_DISALLOWED_CODE_POINT = Cint(173) const ERROR_UNICODE_NOT_SUPPORTED = Cint(132) const ERROR_UNICODE_PROPERTIES_UNAVAILABLE = Cint(145) const ERROR_UNKNOWN_ESCAPE = Cint(103) const ERROR_UNKNOWN_POSIX_CLASS = Cint(130) const ERROR_UNKNOWN_UNICODE_PROPERTY = Cint(147) const ERROR_UNMATCHED_CLOSING_PARENTHESIS = Cint(122) const ERROR_UNRECOGNIZED_AFTER_QUERY_P = Cint(141) const ERROR_UNSET = Cint((-55)) const ERROR_UNSUPPORTED_ESCAPE_SEQUENCE = Cint(137) const ERROR_UTF16_ERR1 = Cint((-24)) const ERROR_UTF16_ERR2 = Cint((-25)) const ERROR_UTF16_ERR3 = Cint((-26)) const ERROR_UTF32_ERR1 = Cint((-27)) const ERROR_UTF32_ERR2 = Cint((-28)) const ERROR_UTF8_ERR1 = Cint((-3)) const ERROR_UTF8_ERR10 = Cint((-12)) const ERROR_UTF8_ERR11 = Cint((-13)) const ERROR_UTF8_ERR12 = Cint((-14)) const ERROR_UTF8_ERR13 = Cint((-15)) const ERROR_UTF8_ERR14 = Cint((-16)) const ERROR_UTF8_ERR15 = Cint((-17)) const ERROR_UTF8_ERR16 = Cint((-18)) const ERROR_UTF8_ERR17 = Cint((-19)) const ERROR_UTF8_ERR18 = Cint((-20)) const ERROR_UTF8_ERR19 = Cint((-21)) const ERROR_UTF8_ERR2 = Cint((-4)) const ERROR_UTF8_ERR20 = Cint((-22)) const ERROR_UTF8_ERR21 = Cint((-23)) const ERROR_UTF8_ERR3 = Cint((-5)) const ERROR_UTF8_ERR4 = Cint((-6)) const ERROR_UTF8_ERR5 = Cint((-7)) const ERROR_UTF8_ERR6 = Cint((-8)) const ERROR_UTF8_ERR7 = Cint((-9)) const ERROR_UTF8_ERR8 = Cint((-10)) const ERROR_UTF8_ERR9 = Cint((-11)) const ERROR_UTF_IS_DISABLED = Cint(174) const ERROR_VERB_ARGUMENT_NOT_ALLOWED = Cint(159) const ERROR_VERB_NAME_TOO_LONG = Cint(176) const ERROR_VERB_UNKNOWN = Cint(160) const ERROR_VERSION_CONDITION_SYNTAX = Cint(179) const ERROR_ZERO_RELATIVE_REFERENCE = Cint(126) const EXTENDED = UInt32(0x00000080) const EXTENDED_MORE = UInt32(0x01000000) const EXTRA_ALLOW_LOOKAROUND_BSK = UInt32(0x00000040) const EXTRA_ALLOW_SURROGATE_ESCAPES = UInt32(0x00000001) const EXTRA_ALT_BSUX = UInt32(0x00000020) const EXTRA_BAD_ESCAPE_IS_LITERAL = UInt32(0x00000002) const EXTRA_ESCAPED_CR_IS_LF = UInt32(0x00000010) const EXTRA_MATCH_LINE = UInt32(0x00000008) const EXTRA_MATCH_WORD = UInt32(0x00000004) const FIRSTLINE = UInt32(0x00000100) const INFO_ALLOPTIONS = UInt32(0) const INFO_ARGOPTIONS = UInt32(1) const INFO_BACKREFMAX = UInt32(2) const INFO_BSR = UInt32(3) const INFO_CAPTURECOUNT = UInt32(4) const INFO_DEPTHLIMIT = UInt32(21) const INFO_EXTRAOPTIONS = UInt32(26) const INFO_FIRSTBITMAP = UInt32(7) const INFO_FIRSTCODETYPE = UInt32(6) const INFO_FIRSTCODEUNIT = UInt32(5) const INFO_FRAMESIZE = UInt32(24) const INFO_HASBACKSLASHC = UInt32(23) const INFO_HASCRORLF = UInt32(8) const INFO_HEAPLIMIT = UInt32(25) const INFO_JCHANGED = UInt32(9) const INFO_JITSIZE = UInt32(10) const INFO_LASTCODETYPE = UInt32(12) const INFO_LASTCODEUNIT = UInt32(11) const INFO_MATCHEMPTY = UInt32(13) const INFO_MATCHLIMIT = UInt32(14) const INFO_MAXLOOKBEHIND = UInt32(15) const INFO_MINLENGTH = UInt32(16) const INFO_NAMECOUNT = UInt32(17) const INFO_NAMEENTRYSIZE = UInt32(18) const INFO_NAMETABLE = UInt32(19) const INFO_NEWLINE = UInt32(20) const INFO_RECURSIONLIMIT = UInt32(21) const INFO_SIZE = UInt32(22) const JIT_COMPLETE = UInt32(0x00000001) const JIT_INVALID_UTF = UInt32(0x00000100) const JIT_PARTIAL_HARD = UInt32(0x00000004) const JIT_PARTIAL_SOFT = UInt32(0x00000002) const LITERAL = UInt32(0x02000000) const MAJOR = UInt32(10) const MATCH_INVALID_UTF = UInt32(0x04000000) const MATCH_UNSET_BACKREF = UInt32(0x00000200) const MINOR = UInt32(42) const MULTILINE = UInt32(0x00000400) const NEVER_BACKSLASH_C = UInt32(0x00100000) const NEVER_UCP = UInt32(0x00000800) const NEVER_UTF = UInt32(0x00001000) const NEWLINE_ANY = UInt32(4) const NEWLINE_ANYCRLF = UInt32(5) const NEWLINE_CR = UInt32(1) const NEWLINE_CRLF = UInt32(3) const NEWLINE_LF = UInt32(2) const NEWLINE_NUL = UInt32(6) const NOTBOL = UInt32(0x00000001) const NOTEMPTY = UInt32(0x00000004) const NOTEMPTY_ATSTART = UInt32(0x00000008) const NOTEOL = UInt32(0x00000002) const NO_AUTO_CAPTURE = UInt32(0x00002000) const NO_AUTO_POSSESS = UInt32(0x00004000) const NO_DOTSTAR_ANCHOR = UInt32(0x00008000) const NO_JIT = UInt32(0x00002000) const NO_START_OPTIMIZE = UInt32(0x00010000) const NO_UTF_CHECK = UInt32(0x40000000) const PARTIAL_HARD = UInt32(0x00000020) const PARTIAL_SOFT = UInt32(0x00000010) const SUBSTITUTE_EXTENDED = UInt32(0x00000200) const SUBSTITUTE_GLOBAL = UInt32(0x00000100) const SUBSTITUTE_LITERAL = UInt32(0x00008000) const SUBSTITUTE_MATCHED = UInt32(0x00010000) const SUBSTITUTE_OVERFLOW_LENGTH = UInt32(0x00001000) const SUBSTITUTE_REPLACEMENT_ONLY = UInt32(0x00020000) const SUBSTITUTE_UNKNOWN_UNSET = UInt32(0x00000800) const SUBSTITUTE_UNSET_EMPTY = UInt32(0x00000400) const UCP = UInt32(0x00020000) const UNGREEDY = UInt32(0x00040000) const USE_OFFSET_LIMIT = UInt32(0x00800000) const UTF = UInt32(0x00080000) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/parse.jl@;# This file is a part of Julia. License is MIT: https://julialang.org/license import Base.Checked: add_with_overflow, mul_with_overflow ## string to integer functions ## """ parse(type, str; base) Parse a string as a number. For `Integer` types, a base can be specified (the default is 10). For floating-point types, the string is parsed as a decimal floating-point number. `Complex` types are parsed from decimal strings of the form `"RยฑIim"` as a `Complex(R,I)` of the requested type; `"i"` or `"j"` can also be used instead of `"im"`, and `"R"` or `"Iim"` are also permitted. If the string does not contain a valid number, an error is raised. !!! compat "Julia 1.1" `parse(Bool, str)` requires at least Julia 1.1. # Examples ```jldoctest julia> parse(Int, "1234") 1234 julia> parse(Int, "1234", base = 5) 194 julia> parse(Int, "afc", base = 16) 2812 julia> parse(Float64, "1.2e-3") 0.0012 julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") 0.32 + 4.5im ``` """ parse(T::Type, str; base = Int) parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer a::Int = (base <= 36 ? 10 : 36) 2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 โ‰ค base โ‰ค 62, got $base")) d = '0' <= c <= '9' ? c-'0' : 'A' <= c <= 'Z' ? c-'A'+10 : 'a' <= c <= 'z' ? c-'a'+a : throw(ArgumentError("invalid digit: $(repr(c))")) d < base || throw(ArgumentError("invalid base $base digit $(repr(c))")) convert(T, d) end function parseint_iterate(s::AbstractString, startpos::Int, endpos::Int) (0 < startpos <= endpos) || (return Char(0), 0, 0) j = startpos c, startpos = iterate(s,startpos)::Tuple{Char, Int} c, startpos, j end function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos::Int, endpos::Int) c, i, j = parseint_iterate(s, startpos, endpos) while isspace(c) c, i, j = parseint_iterate(s,i,endpos) end (j == 0) && (return 0, 0, 0) sgn = 1 if signed if c == '-' || c == '+' (c == '-') && (sgn = -1) c, i, j = parseint_iterate(s,i,endpos) end end while isspace(c) c, i, j = parseint_iterate(s,i,endpos) end (j == 0) && (return 0, 0, 0) if base == 0 if c == '0' && i <= endpos c, i = iterate(s,i)::Tuple{Char, Int} base = c=='b' ? 2 : c=='o' ? 8 : c=='x' ? 16 : 10 if base != 10 c, i, j = parseint_iterate(s,i,endpos) end else base = 10 end end return sgn, base, j end # '0':'9' -> 0:9 # 'A':'Z' -> 10:35 # 'a':'z' -> 10:35 if base <= 36, 36:61 otherwise # input outside of that is mapped to base @inline function __convert_digit(_c::UInt32, base::UInt32) _0 = UInt32('0') _9 = UInt32('9') _A = UInt32('A') _a = UInt32('a') _Z = UInt32('Z') _z = UInt32('z') a = base <= 36 ? UInt32(10) : UInt32(36) # converting here instead of via a type assertion prevents typeassert related errors d = _0 <= _c <= _9 ? _c-_0 : _A <= _c <= _Z ? _c-_A+ UInt32(10) : _a <= _c <= _z ? _c-_a+a : base end function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) where T<:Integer sgn, base, i = parseint_preamble(T<:Signed, Int(base_), s, startpos, endpos) if sgn == 0 && base == 0 && i == 0 raise && throw(ArgumentError("input string is empty or only contains whitespace")) return nothing end if !(2 <= base <= 62) raise && throw(ArgumentError(LazyString("invalid base: base must be 2 โ‰ค base โ‰ค 62, got ", base))) return nothing end if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) return nothing end c, i = parseint_iterate(s,i,endpos) if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) return nothing end base = convert(T, base) # Special case the common cases of base being 10 or 16 to avoid expensive runtime div m::T = base == 10 ? div(typemax(T) - T(9), T(10)) : base == 16 ? div(typemax(T) - T(15), T(16)) : div(typemax(T) - base + 1, base) n::T = 0 while n <= m # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 _c = reinterpret(UInt32, c) >> 24 d::T = __convert_digit(_c, base % UInt32) # we know 2 <= base <= 62, so prevent an incorrect InexactError here if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing end n *= base n += d if i > endpos n *= sgn return n end c, i = iterate(s,i)::Tuple{Char, Int} isspace(c) && break end (T <: Signed) && (n *= sgn) while !isspace(c) # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 _c = reinterpret(UInt32, c) >> 24 d::T = __convert_digit(_c, base % UInt32) # we know 2 <= base <= 62 if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) return nothing end (T <: Signed) && (d *= sgn) n, ov_mul = mul_with_overflow(n, base) n, ov_add = add_with_overflow(n, d) if ov_mul | ov_add raise && throw(OverflowError("overflow parsing $(repr(SubString(s,startpos,endpos)))")) return nothing end (i > endpos) && return n c, i = iterate(s,i)::Tuple{Char, Int} end while i <= endpos c, i = iterate(s,i)::Tuple{Char, Int} if !isspace(c) raise && throw(ArgumentError("extra characters after whitespace in $(repr(SubString(s,startpos,endpos)))")) return nothing end end return n end function tryparse_internal(::Type{Bool}, sbuff::AbstractString, startpos::Int, endpos::Int, base::Integer, raise::Bool) if isempty(sbuff) raise && throw(ArgumentError("input string is empty")) return nothing end if isnumeric(sbuff[1]) intres = tryparse_internal(UInt8, sbuff, startpos, endpos, base, false) (intres == 1) && return true (intres == 0) && return false raise && throw(ArgumentError("invalid Bool representation: $(repr(sbuff))")) end orig_start = startpos orig_end = endpos # Ignore leading and trailing whitespace while startpos <= endpos && isspace(sbuff[startpos]) startpos = nextind(sbuff, startpos) end while endpos >= startpos && isspace(sbuff[endpos]) endpos = prevind(sbuff, endpos) end len = endpos - startpos + 1 if sbuff isa Union{String, SubString{String}} p = pointer(sbuff) + startpos - 1 truestr = "true" falsestr = "false" GC.@preserve sbuff truestr falsestr begin (len == 4) && (0 == memcmp(p, unsafe_convert(Ptr{UInt8}, truestr), 4)) && (return true) (len == 5) && (0 == memcmp(p, unsafe_convert(Ptr{UInt8}, falsestr), 5)) && (return false) end else (len == 4) && (SubString(sbuff, startpos:startpos+3) == "true") && (return true) (len == 5) && (SubString(sbuff, startpos:startpos+4) == "false") && (return false) end if raise substr = SubString(sbuff, orig_start, orig_end) # show input string in the error to avoid confusion if all(isspace, substr) throw(ArgumentError("input string only contains whitespace")) else throw(ArgumentError("invalid Bool representation: $(repr(substr))")) end end return nothing end @inline function check_valid_base(base) if 2 <= base <= 62 return base end throw(ArgumentError("invalid base: base must be 2 โ‰ค base โ‰ค 62, got $base")) end """ tryparse(type, str; base) Like [`parse`](@ref), but returns either a value of the requested type, or [`nothing`](@ref) if the string does not contain a valid number. """ function tryparse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = nothing) where {T<:Integer} # Zero base means, "figure it out" tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), false) end function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = nothing) where {T<:Integer} convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), true)) end tryparse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}") ## string to float functions ## function tryparse(::Type{Float64}, s::String) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) hasvalue ? val : nothing end function tryparse(::Type{Float64}, s::SubString{String}) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) hasvalue ? val : nothing end function tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) hasvalue ? val : nothing end function tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) hasvalue ? val : nothing end function tryparse(::Type{Float32}, s::String) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) hasvalue ? val : nothing end function tryparse(::Type{Float32}, s::SubString{String}) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) hasvalue ? val : nothing end function tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) hasvalue ? val : nothing end function tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) hasvalue ? val : nothing end tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) tryparse(::Type{Float16}, s::AbstractString) = convert(Union{Float16, Nothing}, tryparse(Float32, s)) tryparse_internal(::Type{Float16}, s::AbstractString, startpos::Int, endpos::Int) = convert(Union{Float16, Nothing}, tryparse_internal(Float32, s, startpos, endpos)) ## string to complex functions ## function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String}}, i::Int, e::Int, raise::Bool) where {T<:Real} # skip initial whitespace while i โ‰ค e && isspace(s[i]) i = nextind(s, i) end if i > e raise && throw(ArgumentError("input string is empty or only contains whitespace")) return nothing end # find index of ยฑ separating real/imaginary parts (if any) iโ‚Š = something(findnext(in(('+','-')), s, i), 0) if iโ‚Š == i # leading ยฑ sign iโ‚Š = something(findnext(in(('+','-')), s, iโ‚Š+1), 0) end if iโ‚Š != 0 && s[prevind(s, iโ‚Š)] in ('e','E') # exponent sign iโ‚Š = something(findnext(in(('+','-')), s, iโ‚Š+1), 0) end # find trailing im/i/j iแตข = something(findprev(in(('m','i','j')), s, e), 0) if iแตข > 0 && s[iแตข] == 'm' # im iแตข = prevind(s, iแตข) if s[iแตข] != 'i' raise && throw(ArgumentError("expected trailing \"im\", found only \"m\"")) return nothing end end if iโ‚Š == 0 # purely real or imaginary value if iแตข > i && !(iแตข == i+1 && s[i] in ('+','-')) # purely imaginary (not "ยฑinf") x = tryparse_internal(T, s, i, prevind(s, iแตข), raise) x === nothing && return nothing return Complex{T}(zero(x),x) else # purely real x = tryparse_internal(T, s, i, e, raise) x === nothing && return nothing return Complex{T}(x) end end if iแตข < iโ‚Š raise && throw(ArgumentError("missing imaginary unit")) return nothing # no imaginary part end # parse real part re = tryparse_internal(T, s, i, prevind(s, iโ‚Š), raise) re === nothing && return nothing # parse imaginary part im = tryparse_internal(T, s, iโ‚Š+1, prevind(s, iแตข), raise) im === nothing && return nothing return Complex{T}(re, s[iโ‚Š]=='-' ? -im : im) end # the ยฑ1 indexing above for ascii chars is specific to String, so convert: tryparse_internal(T::Type{Complex{S}}, s::AbstractString, i::Int, e::Int, raise::Bool) where S<:Real = tryparse_internal(T, String(s), i, e, raise) # fallback methods for tryparse_internal tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) where T<:Real = startpos == firstindex(s) && endpos == lastindex(s) ? tryparse(T, s) : tryparse(T, SubString(s, startpos, endpos)) function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Real result = tryparse_internal(T, s, startpos, endpos) if raise && result === nothing _parse_failure(T, s, startpos, endpos) end return result end function tryparse_internal(::Type{T}, s::AbstractString, raise::Bool; kwargs...) where T<:Real result = tryparse(T, s; kwargs...) if raise && result === nothing _parse_failure(T, s) end return result end @noinline _parse_failure(T, s::AbstractString, startpos = firstindex(s), endpos = lastindex(s)) = throw(ArgumentError("cannot parse $(repr(s[startpos:endpos])) as $T")) tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Integer = tryparse_internal(T, s, startpos, endpos, 10, raise) parse(::Type{T}, s::AbstractString; kwargs...) where T<:Real = convert(T, tryparse_internal(T, s, true; kwargs...)) parse(::Type{T}, s::AbstractString) where T<:Complex = convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), true)) tryparse(T::Type{Complex{S}}, s::AbstractString) where S<:Real = tryparse_internal(T, s, firstindex(s), lastindex(s), false) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/shell.jlC# This file is a part of Julia. License is MIT: https://julialang.org/license ## shell-like command parsing ## const shell_special = "#{}()[]<>|&*?~;" # strips the end but respects the space when the string ends with "\\ " function rstrip_shell(s::AbstractString) c_old = nothing for (i, c) in Iterators.reverse(pairs(s)) i::Int; c::AbstractChar ((c == '\\') && c_old == ' ') && return SubString(s, 1, i+1) isspace(c) || return SubString(s, 1, i) c_old = c end SubString(s, 1, 0) end function shell_parse(str::AbstractString, interpolate::Bool=true; special::AbstractString="", filename="none") last_arg = firstindex(str) # N.B.: This is used by REPLCompletions s = SubString(str, last_arg) s = rstrip_shell(lstrip(s)) isempty(s) && return interpolate ? (Expr(:tuple,:()), last_arg) : ([], last_arg) in_single_quotes = false in_double_quotes = false args = [] arg = [] i = firstindex(s) st = Iterators.Stateful(pairs(s)) update_last_arg = false # true after spaces or interpolate function push_nonempty!(list, x) if !isa(x,AbstractString) || !isempty(x) push!(list, x) end return nothing end function consume_upto!(list, s, i, j) push_nonempty!(list, s[i:prevind(s, j)::Int]) something(peek(st), lastindex(s)::Int+1 => '\0').first::Int end function append_2to1!(list, innerlist) if isempty(innerlist); push!(innerlist, ""); end push!(list, copy(innerlist)) empty!(innerlist) end C = eltype(str) P = Pair{Int,C} for (j, c) in st j, c = j::Int, c::C if !in_single_quotes && !in_double_quotes && isspace(c) update_last_arg = true i = consume_upto!(arg, s, i, j) append_2to1!(args, arg) while !isempty(st) # We've made sure above that we don't end in whitespace, # so updating `i` here is ok (i, c) = peek(st)::P isspace(c) || break popfirst!(st) end elseif interpolate && !in_single_quotes && c == '$' i = consume_upto!(arg, s, i, j) isempty(st) && error("\$ right before end of command") stpos, c = popfirst!(st)::P isspace(c) && error("space not allowed right after \$") if startswith(SubString(s, stpos), "var\"") # Disallow var"#" syntax in cmd interpolations. # TODO: Allow only identifiers after the $ for consistency with # string interpolation syntax (see #3150) ex, j = :var, stpos+3 else # use parseatom instead of parse to respect filename (#28188) ex, j = Meta.parseatom(s, stpos, filename=filename) end last_arg = stpos + s.offset update_last_arg = true push!(arg, ex) s = SubString(s, j) Iterators.reset!(st, pairs(s)) i = firstindex(s) else if update_last_arg last_arg = i + s.offset update_last_arg = false end if !in_double_quotes && c == '\'' in_single_quotes = !in_single_quotes i = consume_upto!(arg, s, i, j) elseif !in_single_quotes && c == '"' in_double_quotes = !in_double_quotes i = consume_upto!(arg, s, i, j) elseif !in_single_quotes && c == '\\' if !isempty(st) && (peek(st)::P)[2] in ('\n', '\r') i = consume_upto!(arg, s, i, j) + 1 if popfirst!(st)[2] == '\r' && (peek(st)::P)[2] == '\n' i += 1 popfirst!(st) end while !isempty(st) && (peek(st)::P)[2] in (' ', '\t') i = nextind(str, i) _ = popfirst!(st) end elseif in_double_quotes isempty(st) && error("unterminated double quote") k, cโ€ฒ = peek(st)::P if cโ€ฒ == '"' || cโ€ฒ == '$' || cโ€ฒ == '\\' i = consume_upto!(arg, s, i, j) _ = popfirst!(st) end else isempty(st) && error("dangling backslash") i = consume_upto!(arg, s, i, j) _ = popfirst!(st) end elseif !in_single_quotes && !in_double_quotes && c in special error("parsing command `$str`: special characters \"$special\" must be quoted in commands") end end end if in_single_quotes; error("unterminated single quote"); end if in_double_quotes; error("unterminated double quote"); end push_nonempty!(arg, s[i:end]) append_2to1!(args, arg) interpolate || return args, last_arg # construct an expression ex = Expr(:tuple) for arg in args push!(ex.args, Expr(:tuple, arg...)) end return ex, last_arg end function shell_split(s::AbstractString) parsed = shell_parse(s, false)[1] args = String[] for arg in parsed push!(args, string(arg...)) end args end function print_shell_word(io::IO, word::AbstractString, special::AbstractString = "") has_single = false has_special = false for c in word if isspace(c) || c=='\\' || c=='\'' || c=='"' || c=='$' || c in special has_special = true if c == '\'' has_single = true end end end if isempty(word) print(io, "''") elseif !has_special print(io, word) elseif !has_single print(io, '\'', word, '\'') else print(io, '"') for c in word if c == '"' || c == '$' print(io, '\\') end print(io, c) end print(io, '"') end nothing end function print_shell_escaped(io::IO, cmd::AbstractString, args::AbstractString...; special::AbstractString="") print_shell_word(io, cmd, special) for arg in args print(io, ' ') print_shell_word(io, arg, special) end end print_shell_escaped(io::IO; special::String="") = nothing """ shell_escape(args::Union{Cmd,AbstractString...}; special::AbstractString="") The unexported `shell_escape` function is the inverse of the unexported `shell_split` function: it takes a string or command object and escapes any special characters in such a way that calling `shell_split` on it would give back the array of words in the original command. The `special` keyword argument controls what characters in addition to whitespace, backslashes, quotes and dollar signs are considered to be special (default: none). # Examples ```jldoctest julia> Base.shell_escape("cat", "/foo/bar baz", "&&", "echo", "done") "cat '/foo/bar baz' && echo done" julia> Base.shell_escape("echo", "this", "&&", "that") "echo this && that" ``` """ shell_escape(args::AbstractString...; special::AbstractString="") = sprint((io, args...) -> print_shell_escaped(io, args..., special=special), args...) function print_shell_escaped_posixly(io::IO, args::AbstractString...) first = true for arg in args first || print(io, ' ') # avoid printing quotes around simple enough strings # that any (reasonable) shell will definitely never consider them to be special have_single::Bool = false have_double::Bool = false function isword(c::AbstractChar) if '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z' # word characters elseif c == '_' || c == '/' || c == '+' || c == '-' || c == '.' # other common characters elseif c == '\'' have_single = true elseif c == '"' have_double && return false # switch to single quoting have_double = true elseif !first && c == '=' # equals is special if it is first (e.g. `env=val ./cmd`) else # anything else return false end return true end if isempty(arg) print(io, "''") elseif all(isword, arg) have_single && (arg = replace(arg, '\'' => "\\'")) have_double && (arg = replace(arg, '"' => "\\\"")) print(io, arg) else print(io, '\'', replace(arg, '\'' => "'\\''"), '\'') end first = false end end """ shell_escape_posixly(args::Union{Cmd,AbstractString...}) The unexported `shell_escape_posixly` function takes a string or command object and escapes any special characters in such a way that it is safe to pass it as an argument to a posix shell. # Examples ```jldoctest julia> Base.shell_escape_posixly("cat", "/foo/bar baz", "&&", "echo", "done") "cat '/foo/bar baz' '&&' echo done" julia> Base.shell_escape_posixly("echo", "this", "&&", "that") "echo this '&&' that" ``` """ shell_escape_posixly(args::AbstractString...) = sprint(print_shell_escaped_posixly, args...) """ shell_escape_csh(args::Union{Cmd,AbstractString...}) shell_escape_csh(io::IO, args::Union{Cmd,AbstractString...}) This function quotes any metacharacters in the string arguments such that the string returned can be inserted into a command-line for interpretation by the Unix C shell (csh, tcsh), where each string argument will form one word. In contrast to a POSIX shell, csh does not support the use of the backslash as a general escape character in double-quoted strings. Therefore, this function wraps strings that might contain metacharacters in single quotes, except for parts that contain single quotes, which it wraps in double quotes instead. It switches between these types of quotes as needed. Linefeed characters are escaped with a backslash. This function should also work for a POSIX shell, except if the input string contains a linefeed (`"\\n"`) character. See also: [`shell_escape_posixly`](@ref) """ function shell_escape_csh(io::IO, args::AbstractString...) first = true for arg in args first || write(io, ' ') first = false i = 1 while true for (r,e) = (r"^[A-Za-z0-9/\._-]+\z"sa => "", r"^[^']*\z"sa => "'", r"^[^\$\`\"]*\z"sa => "\"", r"^[^']+"sa => "'", r"^[^\$\`\"]+"sa => "\"") if ((m = match(r, SubString(arg, i))) !== nothing) write(io, e) write(io, replace(m.match, '\n' => "\\\n")) write(io, e) i += ncodeunits(m.match) break end end i <= lastindex(arg) || break end end end shell_escape_csh(args::AbstractString...) = sprint(shell_escape_csh, args...; sizehint = sum(sizeof.(args)) + length(args) * 3) """ shell_escape_wincmd(s::AbstractString) shell_escape_wincmd(io::IO, s::AbstractString) The unexported `shell_escape_wincmd` function escapes Windows `cmd.exe` shell meta characters. It escapes `()!^<>&|` by placing a `^` in front. An `@` is only escaped at the start of the string. Pairs of `"` characters and the strings they enclose are passed through unescaped. Any remaining `"` is escaped with `^` to ensure that the number of unescaped `"` characters in the result remains even. Since `cmd.exe` substitutes variable references (like `%USER%`) _before_ processing the escape characters `^` and `"`, this function makes no attempt to escape the percent sign (`%`), the presence of `%` in the input may cause severe breakage, depending on where the result is used. Input strings with ASCII control characters that cannot be escaped (NUL, CR, LF) will cause an `ArgumentError` exception. The result is safe to pass as an argument to a command call being processed by `CMD.exe /S /C " ... "` (with surrounding double-quote pair) and will be received verbatim by the target application if the input does not contain `%` (else this function will fail with an ArgumentError). The presence of `%` in the input string may result in command injection vulnerabilities and may invalidate any claim of suitability of the output of this function for use as an argument to cmd (due to the ordering described above), so use caution when assembling a string from various sources. This function may be useful in concert with the `windows_verbatim` flag to [`Cmd`](@ref) when constructing process pipelines. ```julia wincmd(c::String) = run(Cmd(Cmd(["cmd.exe", "/s /c \\" \$c \\""]); windows_verbatim=true)) wincmd_echo(s::String) = wincmd("echo " * Base.shell_escape_wincmd(s)) wincmd_echo("hello \$(ENV["USERNAME"]) & the \\"whole\\" world! (=^I^=)") ``` But take note that if the input string `s` contains a `%`, the argument list and echo'ed text may get corrupted, resulting in arbitrary command execution. The argument can alternatively be passed as an environment variable, which avoids the problem with `%` and the need for the `windows_verbatim` flag: ```julia cmdargs = Base.shell_escape_wincmd("Passing args with %cmdargs% works 100%!") run(setenv(`cmd /C echo %cmdargs%`, "cmdargs" => cmdargs)) ``` !!! warning The argument parsing done by CMD when calling batch files (either inside `.bat` files or as arguments to them) is not fully compatible with the output of this function. In particular, the processing of `%` is different. !!! important Due to a peculiar behavior of the CMD parser/interpreter, each command after a literal `|` character (indicating a command pipeline) must have `shell_escape_wincmd` applied twice since it will be parsed twice by CMD. This implies ENV variables would also be expanded twice! For example: ```julia to_print = "All for 1 & 1 for all!" to_print_esc = Base.shell_escape_wincmd(Base.shell_escape_wincmd(to_print)) run(Cmd(Cmd(["cmd", "/S /C \\" break | echo \$(to_print_esc) \\""]), windows_verbatim=true)) ``` With an I/O stream parameter `io`, the result will be written there, rather than returned as a string. See also [`escape_microsoft_c_args`](@ref), [`shell_escape_posixly`](@ref). # Example ```jldoctest julia> Base.shell_escape_wincmd("a^\\"^o\\"^u\\"") "a^^\\"^o\\"^^u^\\"" ``` """ function shell_escape_wincmd(io::IO, s::AbstractString) # https://stackoverflow.com/a/4095133/1990689 occursin(r"[\r\n\0]"sa, s) && throw(ArgumentError("control character unsupported by CMD.EXE")) i = 1 len = ncodeunits(s) if len > 0 && s[1] == '@' write(io, '^') end while i <= len c = s[i] if c == '"' && (j = findnext('"', s, nextind(s,i))) !== nothing write(io, SubString(s,i,j)) i = j else if c in ('"', '(', ')', '!', '^', '<', '>', '&', '|') write(io, '^', c) else write(io, c) end end i = nextind(s,i) end end shell_escape_wincmd(s::AbstractString) = sprint(shell_escape_wincmd, s; sizehint = 2*sizeof(s)) """ escape_microsoft_c_args(args::Union{Cmd,AbstractString...}) escape_microsoft_c_args(io::IO, args::Union{Cmd,AbstractString...}) Convert a collection of string arguments into a string that can be passed to many Windows command-line applications. Microsoft Windows passes the entire command line as a single string to the application (unlike POSIX systems, where the shell splits the command line into a list of arguments). Many Windows API applications (including julia.exe), use the conventions of the [Microsoft C/C++ runtime](https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments) to split that command line into a list of strings. This function implements an inverse for a parser compatible with these rules. It joins command-line arguments to be passed to a Windows C/C++/Julia application into a command line, escaping or quoting the meta characters space, TAB, double quote and backslash where needed. See also [`shell_escape_wincmd`](@ref), [`escape_raw_string`](@ref). """ function escape_microsoft_c_args(io::IO, args::AbstractString...) # http://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES first = true for arg in args if first first = false else write(io, ' ') # separator end if isempty(arg) || occursin(r"[ \t\"]"sa, arg) # Julia raw strings happen to use the same escaping convention # as the argv[] parser in Microsoft's C runtime library. write(io, '"') escape_raw_string(io, arg) write(io, '"') else write(io, arg) end end end escape_microsoft_c_args(args::AbstractString...) = sprint(escape_microsoft_c_args, args...; sizehint = (sum(sizeof.(args)) + 3*length(args))) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/show.jl ล# This file is a part of Julia. License is MIT: https://julialang.org/license using Core.Compiler: has_typevar function show(io::IO, ::MIME"text/plain", u::UndefInitializer) show(io, u) get(io, :compact, false)::Bool && return print(io, ": array initializer with undefined values") end # first a few multiline show functions for types defined before the MIME type: show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges function show(io::IO, ::MIME"text/plain", r::LinRange) isempty(r) && return show(io, r) # show for LinRange, e.g. # range(1, stop=3, length=7) # 7-element LinRange{Float64}: # 1.0,1.33333,1.66667,2.0,2.33333,2.66667,3.0 summary(io, r) println(io, ":") print_range(io, r) end function _isself(@nospecialize(ft)) name = ft.name.mt.name mod = parentmodule(ft) # NOTE: not necessarily the same as ft.name.mt.module return isdefined(mod, name) && ft == typeof(getfield(mod, name)) end function show(io::IO, ::MIME"text/plain", f::Function) get(io, :compact, false)::Bool && return show(io, f) ft = typeof(f) name = ft.name.mt.name if isa(f, Core.IntrinsicFunction) print(io, f) id = Core.Intrinsics.bitcast(Int32, f) print(io, " (intrinsic function #$id)") elseif isa(f, Core.Builtin) print(io, name, " (built-in function)") else n = length(methods(f)) m = n==1 ? "method" : "methods" sname = string(name) ns = (_isself(ft) || '#' in sname) ? sname : string("(::", ft, ")") what = startswith(ns, '@') ? "macro" : "generic function" print(io, ns, " (", what, " with $n $m)") end end show(io::IO, ::MIME"text/plain", c::ComposedFunction) = show(io, c) show(io::IO, ::MIME"text/plain", c::Returns) = show(io, c) show(io::IO, ::MIME"text/plain", s::Splat) = show(io, s) const ansi_regex = r"(?s)(?:\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]))|." # Pseudo-character representing an ANSI delimiter struct ANSIDelimiter del::SubString{String} end ncodeunits(c::ANSIDelimiter) = ncodeunits(c.del) textwidth(::ANSIDelimiter) = 0 # An iterator similar to `pairs(::String)` but whose values are Char or ANSIDelimiter struct ANSIIterator captures::RegexMatchIterator end ANSIIterator(s::AbstractString) = ANSIIterator(eachmatch(ansi_regex, s)) IteratorSize(::Type{ANSIIterator}) = SizeUnknown() eltype(::Type{ANSIIterator}) = Pair{Int, Union{Char,ANSIDelimiter}} function iterate(I::ANSIIterator, (i, m_st)=(1, iterate(I.captures))) m_st === nothing && return nothing m, (j, new_m_st) = m_st c = lastindex(m.match) == 1 ? only(m.match) : ANSIDelimiter(m.match) return (i => c, (j, iterate(I.captures, (j, new_m_st)))) end textwidth(I::ANSIIterator) = mapreduce(textwidthโˆ˜last, +, I; init=0) function _truncate_at_width_or_chars(ignore_ANSI::Bool, str, width, rpad=false, chars="\r\n", truncmark="โ€ฆ") truncwidth = textwidth(truncmark) (width <= 0 || width < truncwidth) && return "" wid = truncidx = lastidx = 0 # if str needs to be truncated, truncidx is the index of truncation. stop = false # once set, only ANSI delimiters will be kept as new characters. needANSIend = false # set if the last ANSI delimiter before truncidx is not "\033[0m". I = ignore_ANSI ? ANSIIterator(str) : pairs(str) for (i, c) in I if c isa ANSIDelimiter truncidx == 0 && (needANSIend = c != "\033[0m") lastidx = i + ncodeunits(c) - 1 else stop && break wid += textwidth(c) truncidx == 0 && wid > (width - truncwidth) && (truncidx = lastidx) lastidx = i c in chars && break stop = wid >= width end end lastidx == 0 && return rpad ? ' '^width : "" str[lastidx] in chars && (lastidx = prevind(str, lastidx)) ANSIend = needANSIend ? "\033[0m" : "" pad = rpad ? repeat(' ', max(0, width-wid)) : "" truncidx == 0 && (truncidx = lastidx) if lastidx < lastindex(str) return string(SubString(str, 1, truncidx), ANSIend, truncmark, pad) else return string(str, ANSIend, pad) end end function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) isempty(iter) && get(io, :compact, false)::Bool && return show(io, iter) summary(io, iter) isempty(iter) && return print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":") limit = get(io, :limit, false)::Bool if limit sz = displaysize(io) rows, cols = sz[1] - 3, sz[2] rows < 2 && (print(io, " โ€ฆ"); return) cols < 4 && (cols = 4) cols -= 2 # For prefix " " rows -= 1 # For summary else rows = cols = typemax(Int) end for (i, v) in enumerate(iter) print(io, "\n ") i == rows < length(iter) && (print(io, "โ‹ฎ"); break) if limit str = sprint(show, v, context=io, sizehint=0) str = _truncate_at_width_or_chars(get(io, :color, false)::Bool, str, cols) print(io, str) else show(io, v) end end end function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} isempty(t) && return show(io, t) # show more descriptively, with one line per key/value pair recur_io = IOContext(io, :SHOWN_SET => t) limit = get(io, :limit, false)::Bool if !haskey(io, :compact) recur_io = IOContext(recur_io, :compact => true) end recur_io_k = IOContext(recur_io, :typeinfo=>keytype(t)) recur_io_v = IOContext(recur_io, :typeinfo=>valtype(t)) summary(io, t) isempty(t) && return print(io, ":") show_circular(io, t) && return if limit sz = displaysize(io) rows, cols = sz[1] - 3, sz[2] rows < 2 && (print(io, " โ€ฆ"); return) cols < 12 && (cols = 12) # Minimum widths of 2 for key, 4 for value cols -= 6 # Subtract the widths of prefix " " separator " => " rows -= 1 # Subtract the summary # determine max key width to align the output, caching the strings hascolor = get(recur_io, :color, false) ks = Vector{String}(undef, min(rows, length(t))) vs = Vector{String}(undef, min(rows, length(t))) keywidth = 0 valwidth = 0 for (i, (k, v)) in enumerate(t) i > rows && break ks[i] = sprint(show, k, context=recur_io_k, sizehint=0) vs[i] = sprint(show, v, context=recur_io_v, sizehint=0) keywidth = clamp(hascolor ? textwidth(ANSIIterator(ks[i])) : textwidth(ks[i]), keywidth, cols) valwidth = clamp(hascolor ? textwidth(ANSIIterator(vs[i])) : textwidth(vs[i]), valwidth, cols) end if keywidth > max(div(cols, 2), cols - valwidth) keywidth = max(cld(cols, 3), cols - valwidth) end else rows = cols = typemax(Int) end for (i, (k, v)) in enumerate(t) print(io, "\n ") if i == rows < length(t) print(io, rpad("โ‹ฎ", keywidth), " => โ‹ฎ") break end if limit key = _truncate_at_width_or_chars(hascolor, ks[i], keywidth, true) else key = sprint(show, k, context=recur_io_k, sizehint=0) end print(recur_io, key) print(io, " => ") if limit val = _truncate_at_width_or_chars(hascolor, vs[i], cols - keywidth) print(io, val) else show(recur_io_v, v) end end end function summary(io::IO, t::AbstractSet) n = length(t) showarg(io, t, true) print(io, " with ", n, (n==1 ? " element" : " elements")) end function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T isempty(t) && return show(io, t) # show more descriptively, with one line per value recur_io = IOContext(io, :SHOWN_SET => t) limit = get(io, :limit, false)::Bool summary(io, t) isempty(t) && return print(io, ":") show_circular(io, t) && return if limit sz = displaysize(io) rows, cols = sz[1] - 3, sz[2] rows < 2 && (print(io, " โ€ฆ"); return) cols -= 2 # Subtract the width of prefix " " cols < 4 && (cols = 4) # Minimum widths of 4 for value rows -= 1 # Subtract the summary else rows = cols = typemax(Int) end for (i, v) in enumerate(t) print(io, "\n ") if i == rows < length(t) print(io, rpad("โ‹ฎ", 2)) break end if limit str = sprint(show, v, context=recur_io, sizehint=0) print(io, _truncate_at_width_or_chars(get(io, :color, false)::Bool, str, cols)) else show(recur_io, v) end end end function show(io::IO, ::MIME"text/plain", opt::JLOptions) println(io, "JLOptions(") fields = fieldnames(JLOptions) nfields = length(fields) for (i, f) in enumerate(fields) v = getfield(opt, i) if isa(v, Ptr{UInt8}) v = (v != C_NULL) ? unsafe_string(v) : "" elseif isa(v, Ptr{Ptr{UInt8}}) v = unsafe_load_commands(v) end println(io, " ", f, " = ", repr(v), i < nfields ? "," : "") end print(io, ")") end function show(io::IO, ::MIME"text/plain", t::Task) show(io, t) if istaskfailed(t) println(io) show_task_exception(io, t, indent = false) end end print(io::IO, s::Symbol) = (write(io,s); nothing) """ IOContext `IOContext` provides a mechanism for passing output configuration settings among [`show`](@ref) methods. In short, it is an immutable dictionary that is a subclass of `IO`. It supports standard dictionary operations such as [`getindex`](@ref), and can also be used as an I/O stream. """ struct IOContext{IO_t <: IO} <: AbstractPipe io::IO_t dict::ImmutableDict{Symbol, Any} function IOContext{IO_t}(io::IO_t, dict::ImmutableDict{Symbol, Any}) where IO_t<:IO @assert !(IO_t <: IOContext) "Cannot create `IOContext` from another `IOContext`." return new(io, dict) end end # (Note that TTY and TTYTerminal io types have a :color property.) unwrapcontext(io::IO) = io, get(io,:color,false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}() unwrapcontext(io::IOContext) = io.io, io.dict function IOContext(io::IO, dict::ImmutableDict) io0 = unwrapcontext(io)[1] IOContext{typeof(io0)}(io0, dict) end convert(::Type{IOContext}, io::IO) = IOContext(unwrapcontext(io)...)::IOContext IOContext(io::IO) = convert(IOContext, io) function IOContext(io::IO, KV::Pair) io0, d = unwrapcontext(io) IOContext(io0, ImmutableDict{Symbol,Any}(d, KV[1], KV[2])) end """ IOContext(io::IO, context::IOContext) Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. """ IOContext(io::IO, context::IO) = IOContext(unwrapcontext(io)[1], unwrapcontext(context)[2]) """ IOContext(io::IO, KV::Pair...) Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pairs to the properties of that stream (note that `io` can itself be an `IOContext`). - use `(key => value) in io` to see if this particular combination is in the properties set - use `get(io, key, default)` to retrieve the most recent value for a particular key The following properties are in common use: - `:compact`: Boolean specifying that values should be printed more compactly, e.g. that numbers should be printed with fewer digits. This is set when printing array elements. `:compact` output should not contain line breaks. - `:limit`: Boolean specifying that containers should be truncated, e.g. showing `โ€ฆ` in place of most elements. - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text output. This can be used to override the display size for called functions, but to get the size of the screen use the `displaysize` function. - `:typeinfo`: a `Type` characterizing the information already printed concerning the type of the object about to be displayed. This is mainly useful when displaying a collection of objects of the same type, so that redundant type information can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo` property will be set to `Float16`). - `:color`: Boolean specifying whether ANSI color/escape codes are supported/expected. By default, this is determined by whether `io` is a compatible terminal and by any `--color` command-line flag when `julia` was launched. # Examples ```jldoctest julia> io = IOBuffer(); julia> printstyled(IOContext(io, :color => true), "string", color=:red) julia> String(take!(io)) "\\e[31mstring\\e[39m" julia> printstyled(io, "string", color=:red) julia> String(take!(io)) "string" ``` ```jldoctest julia> print(IOContext(stdout, :compact => false), 1.12341234) 1.12341234 julia> print(IOContext(stdout, :compact => true), 1.12341234) 1.12341 ``` ```jldoctest julia> function f(io::IO) if get(io, :short, false) print(io, "short") else print(io, "loooooong") end end f (generic function with 1 method) julia> f(stdout) loooooong julia> f(IOContext(stdout, :short => true)) short ``` """ IOContext(io::IO, KV::Pair, KVs::Pair...) = IOContext(IOContext(io, KV), KVs...) show(io::IO, ctx::IOContext) = (print(io, "IOContext("); show(io, ctx.io); print(io, ")")) pipe_reader(io::IOContext) = io.io pipe_writer(io::IOContext) = io.io lock(io::IOContext) = lock(io.io) unlock(io::IOContext) = unlock(io.io) in(key_value::Pair, io::IOContext) = in(key_value, io.dict, ===) in(key_value::Pair, io::IO) = false haskey(io::IOContext, key) = haskey(io.dict, key) haskey(io::IO, key) = false getindex(io::IOContext, key) = getindex(io.dict, key) getindex(io::IO, key) = throw(KeyError(key)) get(io::IOContext, key, default) = get(io.dict, key, default) get(io::IO, key, default) = default keys(io::IOContext) = keys(io.dict) keys(io::IO) = keys(ImmutableDict{Symbol,Any}()) displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize]::Tuple{Int,Int} : displaysize(io.io) show_circular(io::IO, @nospecialize(x)) = false function show_circular(io::IOContext, @nospecialize(x)) d = 1 for (k, v) in io.dict if k === :SHOWN_SET if v === x print(io, "#= circular reference @-$d =#") return true end d += 1 end end return false end """ show([io::IO = stdout], x) Write a text representation of a value `x` to the output stream `io`. New types `T` should overload `show(io::IO, x::T)`. The representation used by `show` generally includes Julia-specific formatting and type information, and should be parseable Julia code when possible. [`repr`](@ref) returns the output of `show` as a string. For a more verbose human-readable text output for objects of type `T`, define `show(io::IO, ::MIME"text/plain", ::T)` in addition. Checking the `:compact` [`IOContext`](@ref) key (often checked as `get(io, :compact, false)::Bool`) of `io` in such methods is recommended, since some containers show their elements by calling this method with `:compact => true`. See also [`print`](@ref), which writes un-decorated representations. # Examples ```jldoctest julia> show("Hello World!") "Hello World!" julia> print("Hello World!") Hello World! ``` """ show(io::IO, @nospecialize(x)) = show_default(io, x) show(x) = show(stdout, x) # avoid inferring show_default on the type of `x` show_default(io::IO, @nospecialize(x)) = _show_default(io, inferencebarrier(x)) function _show_default(io::IO, @nospecialize(x)) t = typeof(x) show(io, inferencebarrier(t)::DataType) print(io, '(') nf = nfields(x) nb = sizeof(x)::Int if nf != 0 || nb == 0 if !show_circular(io, x) recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x), Pair{Symbol,Any}(:typeinfo, Any)) for i in 1:nf f = fieldname(t, i) if !isdefined(x, f) print(io, undef_ref_str) else show(recur_io, getfield(x, i)) end if i < nf print(io, ", ") end end end else print(io, "0x") r = Ref{Any}(x) GC.@preserve r begin p = unsafe_convert(Ptr{Cvoid}, r) for i in (nb - 1):-1:0 print(io, string(unsafe_load(convert(Ptr{UInt8}, p + i)), base = 16, pad = 2)) end end end print(io,')') end function active_module() isassigned(REPL_MODULE_REF) || return Main REPL = REPL_MODULE_REF[] return invokelatest(REPL.active_module)::Module end # Check if a particular symbol is exported from a standard library module function is_exported_from_stdlib(name::Symbol, mod::Module) !isdefined(mod, name) && return false orig = getfield(mod, name) while !(mod === Base || mod === Core) activemod = active_module() parent = parentmodule(mod) if mod === activemod || mod === parent || parent === activemod return false end mod = parent end return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig end function show_function(io::IO, f::Function, compact::Bool, fallback::Function) ft = typeof(f) mt = ft.name.mt if mt === Symbol.name.mt # uses shared method table fallback(io, f) elseif compact print(io, mt.name) elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && getfield(mt.module, mt.name) === f mod = active_module() if is_exported_from_stdlib(mt.name, mt.module) || mt.module === mod show_sym(io, mt.name) else print(io, mt.module, ".") show_sym(io, mt.name) end else fallback(io, f) end end show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)::Bool, show_default) print(io::IO, f::Function) = show_function(io, f, true, show) function show(io::IO, f::Core.IntrinsicFunction) if !(get(io, :compact, false)::Bool) print(io, "Core.Intrinsics.") end print(io, nameof(f)) end print(io::IO, f::Core.IntrinsicFunction) = print(io, nameof(f)) show(io::IO, ::Core.TypeofBottom) = print(io, "Union{}") show(io::IO, ::MIME"text/plain", ::Core.TypeofBottom) = print(io, "Union{}") function print_without_params(@nospecialize(x)) b = unwrap_unionall(x) return isa(b, DataType) && b.name.wrapper === x end function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x)) for (key, val) in io.dict if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val) return true end end return false end io_has_tvar_name(io::IO, name::Symbol, @nospecialize(x)) = false modulesof!(s::Set{Module}, x::TypeVar) = modulesof!(s, x.ub) function modulesof!(s::Set{Module}, x::Type) x = unwrap_unionall(x) if x isa DataType push!(s, parentmodule(x)) elseif x isa Union modulesof!(s, x.a) modulesof!(s, x.b) end s end # given an IO context for printing a type, reconstruct the proper type that # we're attempting to represent. # Union{T} where T is a degenerate case and is equal to T.ub, but we don't want # to print them that way, so filter those out from our aliases completely. function makeproper(io::IO, @nospecialize(x::Type)) if io isa IOContext for (key, val) in io.dict if key === :unionall_env && val isa TypeVar x = UnionAll(val, x) end end end has_free_typevars(x) && return Any return x end function make_typealias(@nospecialize(x::Type)) Any === x && return nothing x <: Tuple && return nothing mods = modulesof!(Set{Module}(), x) Core in mods && push!(mods, Base) aliases = Tuple{GlobalRef,SimpleVector}[] xenv = UnionAll[] for p in uniontypes(unwrap_unionall(x)) p isa UnionAll && push!(xenv, p) end x isa UnionAll && push!(xenv, x) for mod in mods for name in unsorted_names(mod) if isdefined(mod, name) && !isdeprecated(mod, name) && isconst(mod, name) alias = getfield(mod, name) if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && x <: alias if alias isa UnionAll (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector # ti === Union{} && continue # impossible, since we already checked that x <: alias env = env::SimpleVector # TODO: In some cases (such as the following), the `env` is over-approximated. # We'd like to disable `fix_inferred_var_bound` since we'll already do that fix-up here. # (or detect and reverse the computation of it here). # T = Array{Array{T,1}, 1} where T # (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), T, Vector) # env[1].ub.var == T.var applied = try # this can fail if `x` contains a covariant # union, and the non-matching branch of the # union has additional restrictions on the # bounds of the environment that are not met by # the instantiation found above alias{env...} catch ex ex isa TypeError || rethrow() continue end for p in xenv applied = rewrap_unionall(applied, p) end has_free_typevars(applied) && continue applied === x || continue # it couldn't figure out the parameter matching elseif alias === x env = Core.svec() else continue # not a complete match end push!(aliases, (GlobalRef(mod, name), env)) end end end end if length(aliases) == 1 # TODO: select the type with the "best" (shortest?) environment return aliases[1] end end isgensym(s::Symbol) = '#' in string(s) function show_can_elide(p::TypeVar, wheres::Vector, elide::Int, env::SimpleVector, skip::Int) elide == 0 && return false wheres[elide] === p || return false for i = (elide + 1):length(wheres) v = wheres[i]::TypeVar has_typevar(v.lb, p) && return false has_typevar(v.ub, p) && return false end for i = 1:length(env) i == skip && continue has_typevar(env[i], p) && return false end return true end function show_typeparams(io::IO, env::SimpleVector, orig::SimpleVector, wheres::Vector) n = length(env) elide = length(wheres) function egal_var(p::TypeVar, @nospecialize o) return o isa TypeVar && ccall(:jl_types_egal, Cint, (Any, Any), p.ub, o.ub) != 0 && ccall(:jl_types_egal, Cint, (Any, Any), p.lb, o.lb) != 0 end for i = n:-1:1 p = env[i] if p isa TypeVar if i == n && egal_var(p, orig[i]) && show_can_elide(p, wheres, elide, env, i) n -= 1 elide -= 1 elseif p.lb === Union{} && isgensym(p.name) && show_can_elide(p, wheres, elide, env, i) elide -= 1 elseif p.ub === Any && isgensym(p.name) && show_can_elide(p, wheres, elide, env, i) elide -= 1 end end end if n > 0 print(io, "{") for i = 1:n p = env[i] if p isa TypeVar if p.lb === Union{} && something(findfirst(@nospecialize(w) -> w === p, wheres), 0) > elide print(io, "<:") show(io, p.ub) elseif p.ub === Any && something(findfirst(@nospecialize(w) -> w === p, wheres), 0) > elide print(io, ">:") show(io, p.lb) else show(io, p) end else show(io, p) end i < n && print(io, ", ") end print(io, "}") end resize!(wheres, elide) nothing end function show_typealias(io::IO, name::GlobalRef, x::Type, env::SimpleVector, wheres::Vector) if !(get(io, :compact, false)::Bool) # Print module prefix unless alias is visible from module passed to # IOContext. If :module is not set, default to Main (or current active module). # nothing can be used to force printing prefix. from = get(io, :module, active_module()) if (from === nothing || !isvisible(name.name, name.mod, from)) show(io, name.mod) print(io, ".") end end print(io, name.name) isempty(env) && return io = IOContext(io) for p in wheres io = IOContext(io, :unionall_env => p) end orig = getfield(name.mod, name.name) vars = TypeVar[] while orig isa UnionAll push!(vars, orig.var) orig = orig.body end show_typeparams(io, env, Core.svec(vars...), wheres) nothing end function make_wheres(io::IO, env::SimpleVector, @nospecialize(x::Type)) seen = IdSet() wheres = TypeVar[] # record things printed by the context if io isa IOContext for (key, val) in io.dict if key === :unionall_env && val isa TypeVar && has_typevar(x, val) push!(seen, val) end end end # record things in x to print outermost while x isa UnionAll if !(x.var in seen) push!(seen, x.var) push!(wheres, x.var) end x = x.body end # record remaining things in env to print innermost for i = length(env):-1:1 p = env[i] if p isa TypeVar && !(p in seen) push!(seen, p) pushfirst!(wheres, p) end end return wheres end function show_wheres(io::IO, wheres::Vector{TypeVar}) isempty(wheres) && return io = IOContext(io) n = length(wheres) for i = 1:n p = wheres[i] print(io, n == 1 ? " where " : i == 1 ? " where {" : ", ") show(io, p) io = IOContext(io, :unionall_env => p) end n > 1 && print(io, "}") nothing end function show_typealias(io::IO, @nospecialize(x::Type)) properx = makeproper(io, x) alias = make_typealias(properx) alias === nothing && return false wheres = make_wheres(io, alias[2], x) show_typealias(io, alias[1], x, alias[2], wheres) show_wheres(io, wheres) return true end function make_typealiases(@nospecialize(x::Type)) aliases = SimpleVector[] Any === x && return aliases, Union{} x <: Tuple && return aliases, Union{} mods = modulesof!(Set{Module}(), x) Core in mods && push!(mods, Base) vars = Dict{Symbol,TypeVar}() xenv = UnionAll[] each = Any[] for p in uniontypes(unwrap_unionall(x)) p isa UnionAll && push!(xenv, p) push!(each, rewrap_unionall(p, x)) end x isa UnionAll && push!(xenv, x) for mod in mods for name in unsorted_names(mod) if isdefined(mod, name) && !isdeprecated(mod, name) && isconst(mod, name) alias = getfield(mod, name) if alias isa Type && !has_free_typevars(alias) && !print_without_params(alias) && !(alias <: Tuple) (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), x, alias)::SimpleVector ti === Union{} && continue # make sure this alias wasn't from an unrelated part of the Union mod2 = modulesof!(Set{Module}(), alias) mod in mod2 || (mod === Base && Core in mods) || continue env = env::SimpleVector applied = alias if !isempty(env) applied = try # this can fail if `x` contains a covariant # union, and the non-matching branch of the # union has additional restrictions on the # bounds of the environment that are not met by # the instantiation found above alias{env...} catch ex ex isa TypeError || rethrow() continue end end ul = unionlen(applied) for p in xenv applied = rewrap_unionall(applied, p) end has_free_typevars(applied) && continue applied <: x || continue # parameter matching didn't make a subtype print_without_params(x) && (env = Core.svec()) for typ in each # check that the alias also fully subsumes at least component of the input if typ <: applied push!(aliases, Core.svec(GlobalRef(mod, name), env, applied, (ul, -length(env)))) break end end end end end end if isempty(aliases) return aliases, Union{} end sort!(aliases, by = x -> x[4]::Tuple{Int,Int}, rev = true) # heuristic sort by "best" environment let applied = Union{} applied1 = Union{} keep = SimpleVector[] prev = (0, 0) for alias in aliases alias4 = alias[4]::Tuple{Int,Int} if alias4[1] < 2 if !(alias[3] <: applied) applied1 = Union{applied1, alias[3]} push!(keep, alias) end elseif alias4 == prev || !(alias[3] <: applied) applied = applied1 = Union{applied1, alias[3]} push!(keep, alias) prev = alias4 end end return keep, applied1 end end function show_unionaliases(io::IO, x::Union) properx = makeproper(io, x) aliases, applied = make_typealiases(properx) isempty(aliases) && return false first = true tvar = false for typ in uniontypes(x) if isa(typ, TypeVar) tvar = true # sort bare TypeVars to the end continue elseif rewrap_unionall(typ, properx) <: applied continue end print(io, first ? "Union{" : ", ") first = false show(io, typ) end if first && !tvar && length(aliases) == 1 alias = aliases[1] env = alias[2]::SimpleVector wheres = make_wheres(io, env, x) show_typealias(io, alias[1], x, env, wheres) show_wheres(io, wheres) else for alias in aliases print(io, first ? "Union{" : ", ") first = false env = alias[2]::SimpleVector wheres = make_wheres(io, env, x) show_typealias(io, alias[1], x, env, wheres) show_wheres(io, wheres) end if tvar for typ in uniontypes(x) if isa(typ, TypeVar) print(io, ", ") show(io, typ) end end end print(io, "}") end return true end function show(io::IO, ::MIME"text/plain", @nospecialize(x::Type)) if !print_without_params(x) properx = makeproper(io, x) if make_typealias(properx) !== nothing || (unwrap_unionall(x) isa Union && x <: make_typealiases(properx)[2]) show(IOContext(io, :compact => true), x) if !(get(io, :compact, false)::Bool) printstyled(io, " (alias for "; color = :light_black) printstyled(IOContext(io, :compact => false), x, color = :light_black) printstyled(io, ")"; color = :light_black) end return end end show(io, x) # give a helpful hint for function types if x isa DataType && x !== UnionAll && !(get(io, :compact, false)::Bool) tn = x.name::Core.TypeName globname = isdefined(tn, :mt) ? tn.mt.name : nothing if is_global_function(tn, globname) print(io, " (singleton type of function ") show_sym(io, globname) print(io, ", subtype of Function)") end end end show(io::IO, @nospecialize(x::Type)) = _show_type(io, inferencebarrier(x)) function _show_type(io::IO, @nospecialize(x::Type)) if print_without_params(x) show_type_name(io, (unwrap_unionall(x)::DataType).name) return elseif get(io, :compact, true)::Bool && show_typealias(io, x) return elseif x isa DataType show_datatype(io, x) return elseif x isa Union if get(io, :compact, true)::Bool && show_unionaliases(io, x) return end print(io, "Union") show_delim_array(io, uniontypes(x), '{', ',', '}', false) return end x = x::UnionAll wheres = TypeVar[] let io = IOContext(io) while x isa UnionAll var = x.var if var.name === :_ || io_has_tvar_name(io, var.name, x) counter = 1 while true newname = Symbol(var.name, counter) if !io_has_tvar_name(io, newname, x) var = TypeVar(newname, var.lb, var.ub) x = x{var} break end counter += 1 end else x = x.body end push!(wheres, var) io = IOContext(io, :unionall_env => var) end if x isa DataType show_datatype(io, x, wheres) else show(io, x) end end show_wheres(io, wheres) end # Check whether 'sym' (defined in module 'parent') is visible from module 'from' # If an object with this name exists in 'from', we need to check that it's the same binding # and that it's not deprecated. function isvisible(sym::Symbol, parent::Module, from::Module) owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), parent, sym) from_owner = ccall(:jl_binding_owner, Ptr{Cvoid}, (Any, Any), from, sym) return owner !== C_NULL && from_owner === owner && !isdeprecated(parent, sym) && isdefined(from, sym) # if we're going to return true, force binding resolution end function is_global_function(tn::Core.TypeName, globname::Union{Symbol,Nothing}) if globname !== nothing globname_str = string(globname::Symbol) if ('#' โˆ‰ globname_str && '@' โˆ‰ globname_str && isdefined(tn, :module) && isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && isconcretetype(tn.wrapper) && isa(getfield(tn.module, globname), tn.wrapper)) return true end end return false end function show_type_name(io::IO, tn::Core.TypeName) if tn === UnionAll.name # by coincidence, `typeof(Type)` is a valid representation of the UnionAll type. # intercept this case and print `UnionAll` instead. return print(io, "UnionAll") end globname = isdefined(tn, :mt) ? tn.mt.name : nothing globfunc = is_global_function(tn, globname) sym = (globfunc ? globname : tn.name)::Symbol globfunc && print(io, "typeof(") quo = false if !(get(io, :compact, false)::Bool) # Print module prefix unless type is visible from module passed to # IOContext If :module is not set, default to Main (or current active module). # nothing can be used to force printing prefix from = get(io, :module, active_module()) if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from)) show(io, tn.module) print(io, ".") if globfunc && !is_id_start_char(first(string(sym))) print(io, ':') if sym in quoted_syms print(io, '(') quo = true end end end end show_sym(io, sym) quo && print(io, ")") globfunc && print(io, ")") nothing end function maybe_kws_nt(x::DataType) x.name === typename(Pairs) || return nothing length(x.parameters) == 4 || return nothing x.parameters[1] === Symbol || return nothing p4 = x.parameters[4] if (isa(p4, DataType) && p4.name === typename(NamedTuple) && length(p4.parameters) == 2) syms, types = p4.parameters types isa DataType || return nothing x.parameters[2] === eltype(p4) || return nothing isa(syms, Tuple) || return nothing x.parameters[3] === typeof(syms) || return nothing return p4 end return nothing end function show_datatype(io::IO, x::DataType, wheres::Vector{TypeVar}=TypeVar[]) parameters = x.parameters::SimpleVector istuple = x.name === Tuple.name isnamedtuple = x.name === typename(NamedTuple) kwsnt = maybe_kws_nt(x) n = length(parameters) # Print tuple types with homogeneous tails longer than max_n compactly using `NTuple` or `Vararg` if istuple max_n = 3 taillen = 1 for i in (n-1):-1:1 if parameters[i] === parameters[n] taillen += 1 else break end end if n == taillen > max_n print(io, "NTuple{", n, ", ") show(io, parameters[1]) print(io, "}") else print(io, "Tuple{") for i = 1:(taillen > max_n ? n-taillen : n) i > 1 && print(io, ", ") show(io, parameters[i]) end if taillen > max_n print(io, ", Vararg{") show(io, parameters[n]) print(io, ", ", taillen, "}") end print(io, "}") end return elseif isnamedtuple syms, types = parameters if syms isa Tuple && types isa DataType print(io, "@NamedTuple{") show_at_namedtuple(io, syms, types) print(io, "}") return end elseif get(io, :backtrace, false)::Bool && kwsnt !== nothing # simplify the type representation of keyword arguments # when printing signature of keyword method in the stack trace print(io, "@Kwargs{") show_at_namedtuple(io, kwsnt.parameters[1]::Tuple, kwsnt.parameters[2]::DataType) print(io, "}") return end show_type_name(io, x.name) show_typeparams(io, parameters, (unwrap_unionall(x.name.wrapper)::DataType).parameters, wheres) end function show_at_namedtuple(io::IO, syms::Tuple, types::DataType) first = true for i in 1:length(syms) if !first print(io, ", ") end print(io, syms[i]) typ = types.parameters[i] if typ !== Any print(io, "::") show(io, typ) end first = false end end function show_supertypes(io::IO, typ::DataType) print(io, typ) while typ != Any typ = supertype(typ) print(io, " <: ", typ) end end show_supertypes(typ::DataType) = show_supertypes(stdout, typ) """ @show exs... Prints one or more expressions, and their results, to `stdout`, and returns the last result. See also: [`show`](@ref), [`@info`](@ref man-logging), [`println`](@ref). # Examples ```jldoctest julia> x = @show 1+2 1 + 2 = 3 3 julia> @show x^2 x/2; x ^ 2 = 9 x / 2 = 1.5 ``` """ macro show(exs...) blk = Expr(:block) for ex in exs push!(blk.args, :(println($(sprint(show_unquoted,ex)*" = "), repr(begin local value = $(esc(ex)) end)))) end isempty(exs) || push!(blk.args, :value) return blk end function show(io::IO, tn::Core.TypeName) print(io, "typename(") show_type_name(io, tn) print(io, ")") end show(io::IO, ::Nothing) = print(io, "nothing") show(io::IO, b::Bool) = print(io, get(io, :typeinfo, Any) === Bool ? (b ? "1" : "0") : (b ? "true" : "false")) show(io::IO, n::Signed) = (write(io, string(n)); nothing) show(io::IO, n::Unsigned) = print(io, "0x", string(n, pad = sizeof(n)<<1, base = 16)) print(io::IO, n::Unsigned) = print(io, string(n)) show(io::IO, p::Ptr) = print(io, typeof(p), " @0x$(string(UInt(p), base = 16, pad = Sys.WORD_SIZE>>2))") has_tight_type(p::Pair) = typeof(p.first) == typeof(p).parameters[1] && typeof(p.second) == typeof(p).parameters[2] isdelimited(io::IO, x) = true isdelimited(io::IO, x::Function) = !isoperator(Symbol(x)) # !isdelimited means that the Pair is printed with "=>" (like in "1 => 2"), # without its explicit type (like in "Pair{Integer,Integer}(1, 2)") isdelimited(io::IO, p::Pair) = !(has_tight_type(p) || get(io, :typeinfo, Any) == typeof(p)) function gettypeinfos(io::IO, p::Pair) typeinfo = get(io, :typeinfo, Any) p isa typeinfo <: Pair ? fieldtype(typeinfo, 1) => fieldtype(typeinfo, 2) : Any => Any end function show(io::IO, p::Pair) isdelimited(io, p) && return show_pairtyped(io, p) typeinfos = gettypeinfos(io, p) for i = (1, 2) io_i = IOContext(io, :typeinfo => typeinfos[i]) isdelimited(io_i, p[i]) || print(io, "(") show(io_i, p[i]) isdelimited(io_i, p[i]) || print(io, ")") i == 1 && print(io, get(io, :compact, false)::Bool ? "=>" : " => ") end end function show_pairtyped(io::IO, p::Pair{K,V}) where {K,V} show(io, typeof(p)) show(io, (p.first, p.second)) end function show(io::IO, m::Module) if is_root_module(m) print(io, nameof(m)) else print_fullname(io, m) end end # The call to print_fullname above was originally `print(io, join(fullname(m),"."))`, # which allocates. The method below provides the same behavior without allocating. # See https://github.com/JuliaLang/julia/pull/42773 for perf information. function print_fullname(io::IO, m::Module) mp = parentmodule(m) if m === Main || m === Base || m === Core || mp === m show_sym(io, nameof(m)) else print_fullname(io, mp) print(io, '.') show_sym(io, nameof(m)) end end sourceinfo_slotnames(src::CodeInfo) = sourceinfo_slotnames(src.slotnames) function sourceinfo_slotnames(slotnames::Vector{Symbol}) names = Dict{String,Int}() printnames = Vector{String}(undef, length(slotnames)) for i in eachindex(slotnames) if slotnames[i] === :var"#unused#" printnames[i] = "_" continue end name = string(slotnames[i]) idx = get!(names, name, i) if idx != i || isempty(name) printname = "$name@_$i" idx > 0 && (printnames[idx] = "$name@_$idx") names[name] = 0 else printname = name end printnames[i] = printname end return printnames end show(io::IO, l::Core.MethodInstance) = show_mi(io, l) function show_mi(io::IO, l::Core.MethodInstance, from_stackframe::Bool=false) def = l.def if isa(def, Method) if isdefined(def, :generator) && l === def.generator print(io, "MethodInstance generator for ") show(io, def) else print(io, "MethodInstance for ") show_tuple_as_call(io, def.name, l.specTypes; qualified=true) end else print(io, "Toplevel MethodInstance thunk") # `thunk` is not very much information to go on. If this # MethodInstance is part of a stacktrace, it gets location info # added by other means. But if it isn't, then we should try # to print a little more identifying information. if !from_stackframe linetable = l.uninferred.linetable line = isempty(linetable) ? "unknown" : (lt = linetable[1]::Union{LineNumberNode,Core.LineInfoNode}; string(lt.file, ':', lt.line)) print(io, " from ", def, " starting at ", line) end end end # These sometimes show up as Const-values in InferenceFrameInfo signatures show(io::IO, r::Core.Compiler.UnitRange) = show(io, r.start : r.stop) show(io::IO, mime::MIME{Symbol("text/plain")}, r::Core.Compiler.UnitRange) = show(io, mime, r.start : r.stop) function show(io::IO, mi_info::Core.Compiler.Timings.InferenceFrameInfo) mi = mi_info.mi def = mi.def if isa(def, Method) if isdefined(def, :generator) && mi === def.generator print(io, "InferenceFrameInfo generator for ") show(io, def) else print(io, "InferenceFrameInfo for ") argnames = [isa(a, Core.Const) ? (isa(a.val, Type) ? "" : a.val) : "" for a in mi_info.slottypes[1:mi_info.nargs]] show_tuple_as_call(io, def.name, mi.specTypes; argnames, qualified=true) end else linetable = mi.uninferred.linetable line = isempty(linetable) ? "" : (lt = linetable[1]; string(lt.file, ':', lt.line)) print(io, "Toplevel InferenceFrameInfo thunk from ", def, " starting at ", line) end end function show(io::IO, tinf::Core.Compiler.Timings.Timing) print(io, "Core.Compiler.Timings.Timing(", tinf.mi_info, ") with ", length(tinf.children), " children") end function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, delim_one, i1=first(LinearIndices(itr)), l=last(LinearIndices(itr))) print(io, op) if !show_circular(io, itr) recur_io = IOContext(io, :SHOWN_SET => itr) first = true i = i1 if l >= i1 while true if !isassigned(itr, i) print(io, undef_ref_str) else x = itr[i] show(recur_io, x) end i += 1 if i > l delim_one && first && print(io, delim) break end first = false print(io, delim) print(io, ' ') end end end print(io, cl) end function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int)) print(io, op) if !show_circular(io, itr) recur_io = IOContext(io, :SHOWN_SET => itr) y = iterate(itr) first = true i0 = i1-1 while i1 > 1 && y !== nothing y = iterate(itr, y[2]) i1 -= 1 end if y !== nothing typeinfo = get(io, :typeinfo, Any) while true x = y[1] y = iterate(itr, y[2]) show(IOContext(recur_io, :typeinfo => itr isa typeinfo <: Tuple ? fieldtype(typeinfo, i1+i0) : typeinfo), x) i1 += 1 if y === nothing || i1 > n delim_one && first && print(io, delim) break end first = false print(io, delim) print(io, ' ') end end end print(io, cl) end show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true) show(io::IO, v::SimpleVector) = show_delim_array(io, v, "svec(", ',', ')', false) show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0) ## Abstract Syntax Tree (AST) printing ## # Summary: # print(io, ex) defers to show_unquoted(io, ex) # show(io, ex) defers to show_unquoted(io, QuoteNode(ex)) # show_unquoted(io, ex) does the heavy lifting # # AST printing should follow two rules: # 1. Meta.parse(string(ex)) == ex # 2. eval(Meta.parse(repr(ex))) == ex # # Rule 1 means that printing an expression should generate Julia code which # could be reparsed to obtain the original expression. This code should be # unambiguous and as readable as possible. # # Rule 2 means that showing an expression should generate a quoted version of # printโ€™s output. Parsing and then evaling this output should return the # original expression. # # This is consistent with many other show methods, i.e.: # show(Set([1,2,3])) # ==> "Set{Int64}([2,3,1])" # eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set # While this isnโ€™t true of ALL show methods, it is of all ASTs. using Core.Compiler: TypedSlot, UnoptSlot const ExprNode = Union{Expr, QuoteNode, UnoptSlot, LineNumberNode, SSAValue, GotoNode, GotoIfNot, GlobalRef, PhiNode, PhiCNode, UpsilonNode, ReturnNode} # Operators have precedence levels from 1-N, and show_unquoted defaults to a # precedence level of 0 (the fourth argument). The top-level print and show # methods use a precedence of -1 to specially allow space-separated macro syntax. # IOContext(io, :unquote_fallback => false) tells show_unquoted to treat any # Expr whose head is :$ as if it is inside a quote, preventing fallback to the # "unhandled" case: this is used by print/string to be lawful to Rule 1 above. # On the contrary, show/repr have to follow Rule 2, requiring any Expr whose # head is :$ and which is not inside a quote to fallback to the "unhandled" case: # this is behavior is triggered by IOContext(io, :unquote_fallback => true) print( io::IO, ex::ExprNode) = (show_unquoted(IOContext(io, :unquote_fallback => false), ex, 0, -1); nothing) show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(IOContext(io, :unquote_fallback => true), ex, 0, -1, 0) show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0) show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0) show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) show_unquoted(io::IO, ex, indent::Int, prec::Int, ::Int) = show_unquoted(io, ex, indent, prec) ## AST printing constants ## const indent_width = 4 const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(===),:(=>)]) const uni_syms = Set{Symbol}([:(::), :(<:), :(>:)]) const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(ยฌ), :(~), :(<:), :(>:), :(โˆš), :(โˆ›), :(โˆœ), :(โˆ“), :(ยฑ)]) const expr_infix_wide = Set{Symbol}([ :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(รท=), :(%=), :(>>>=), :(>>=), :(<<=), :(.=), :(.+=), :(.-=), :(.*=), :(./=), :(.\=), :(.^=), :(.&=), :(.|=), :(.รท=), :(.%=), :(.>>>=), :(.>>=), :(.<<=), :(&&), :(||), :(<:), :($=), :(โŠป=), :(>:), :(-->), :(:=), :(โ‰”), :(โฉด), :(โ‰•)]) const expr_infix = Set{Symbol}([:(:), :(->), :(::)]) const expr_infix_any = union(expr_infix, expr_infix_wide) const expr_calls = Dict(:call => ('(',')'), :calldecl => ('(',')'), :ref => ('[',']'), :curly => ('{','}'), :(.) => ('(',')')) const expr_parens = Dict(:tuple=>('(',')'), :vcat=>('[',']'), :hcat =>('[',']'), :row =>('[',']'), :vect=>('[',']'), :ncat =>('[',']'), :nrow =>('[',']'), :braces=>('{','}'), :bracescat=>('{','}')) ## AST decoding helpers ## is_id_start_char(c::AbstractChar) = ccall(:jl_id_start_char, Cint, (UInt32,), c) != 0 is_id_char(c::AbstractChar) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0 """ isidentifier(s) -> Bool Return whether the symbol or string `s` contains characters that are parsed as a valid ordinary identifier (not a binary/unary operator) in Julia code; see also [`Base.isoperator`](@ref). Internally Julia allows any sequence of characters in a `Symbol` (except `\\0`s), and macros automatically use variable names containing `#` in order to avoid naming collision with the surrounding code. In order for the parser to recognize a variable, it uses a limited set of characters (greatly extended by Unicode). `isidentifier()` makes it possible to query the parser directly whether a symbol contains valid characters. # Examples ```jldoctest julia> Meta.isidentifier(:x), Meta.isidentifier("1x") (true, false) ``` """ function isidentifier(s::AbstractString) x = Iterators.peel(s) isnothing(x) && return false (s == "true" || s == "false") && return false c, rest = x is_id_start_char(c) || return false return all(is_id_char, rest) end isidentifier(s::Symbol) = isidentifier(string(s)) is_op_suffix_char(c::AbstractChar) = ccall(:jl_op_suffix_char, Cint, (UInt32,), c) != 0 _isoperator(s) = ccall(:jl_is_operator, Cint, (Cstring,), s) != 0 """ isoperator(s::Symbol) Return `true` if the symbol can be used as an operator, `false` otherwise. # Examples ```jldoctest julia> Meta.isoperator(:+), Meta.isoperator(:f) (true, false) ``` """ isoperator(s::Union{Symbol,AbstractString}) = _isoperator(s) || ispostfixoperator(s) """ isunaryoperator(s::Symbol) Return `true` if the symbol can be used as a unary (prefix) operator, `false` otherwise. # Examples ```jldoctest julia> Meta.isunaryoperator(:-), Meta.isunaryoperator(:โˆš), Meta.isunaryoperator(:f) (true, true, false) ``` """ isunaryoperator(s::Symbol) = ccall(:jl_is_unary_operator, Cint, (Cstring,), s) != 0 is_unary_and_binary_operator(s::Symbol) = ccall(:jl_is_unary_and_binary_operator, Cint, (Cstring,), s) != 0 is_syntactic_operator(s::Symbol) = ccall(:jl_is_syntactic_operator, Cint, (Cstring,), s) != 0 """ isbinaryoperator(s::Symbol) Return `true` if the symbol can be used as a binary (infix) operator, `false` otherwise. # Examples ```jldoctest julia> Meta.isbinaryoperator(:-), Meta.isbinaryoperator(:โˆš), Meta.isbinaryoperator(:f) (true, false, false) ``` """ function isbinaryoperator(s::Symbol) return _isoperator(s) && (!isunaryoperator(s) || is_unary_and_binary_operator(s)) && s !== Symbol("'") end """ ispostfixoperator(s::Union{Symbol,AbstractString}) Return `true` if the symbol can be used as a postfix operator, `false` otherwise. # Examples ```jldoctest julia> Meta.ispostfixoperator(Symbol("'")), Meta.ispostfixoperator(Symbol("'แต€")), Meta.ispostfixoperator(:-) (true, true, false) ``` """ function ispostfixoperator(s::Union{Symbol,AbstractString}) s = String(s)::String return startswith(s, '\'') && all(is_op_suffix_char, SubString(s, 2)) end """ operator_precedence(s::Symbol) Return an integer representing the precedence of operator `s`, relative to other operators. Higher-numbered operators take precedence over lower-numbered operators. Return `0` if `s` is not a valid operator. # Examples ```jldoctest julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.) (11, 12, 17) julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=)) # (Note the necessary parens on `:(=)`) (0, 1, 1) ``` """ operator_precedence(s::Symbol) = Int(ccall(:jl_operator_precedence, Cint, (Cstring,), s)) operator_precedence(x::Any) = 0 # fallback for generic expression nodes const prec_assignment = operator_precedence(:(=)) const prec_pair = operator_precedence(:(=>)) const prec_control_flow = operator_precedence(:(&&)) const prec_arrow = operator_precedence(:(-->)) const prec_comparison = operator_precedence(:(>)) const prec_power = operator_precedence(:(^)) const prec_decl = operator_precedence(:(::)) """ operator_associativity(s::Symbol) Return a symbol representing the associativity of operator `s`. Left- and right-associative operators return `:left` and `:right`, respectively. Return `:none` if `s` is non-associative or an invalid operator. # Examples ```jldoctest julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^) (:left, :none, :right) julia> Base.operator_associativity(:โŠ—), Base.operator_associativity(:sin), Base.operator_associativity(:โ†’) (:left, :none, :right) ``` """ function operator_associativity(s::Symbol) if operator_precedence(s) in (prec_arrow, prec_assignment, prec_control_flow, prec_pair, prec_power) || (isunaryoperator(s) && !is_unary_and_binary_operator(s)) || s === :<| || s === :|| return :right elseif operator_precedence(s) in (0, prec_comparison) || s in (:+, :++, :*) return :none end return :left end is_quoted(ex) = false is_quoted(ex::QuoteNode) = true is_quoted(ex::Expr) = is_expr(ex, :quote, 1) || is_expr(ex, :inert, 1) unquoted(ex::QuoteNode) = ex.value unquoted(ex::Expr) = ex.args[1] ## AST printing helpers ## function printstyled end function with_output_color end emphasize(io, str::AbstractString, col = Base.error_color()) = get(io, :color, false)::Bool ? printstyled(io, str; color=col, bold=true) : print(io, uppercase(str)) show_linenumber(io::IO, line) = printstyled(io, "#= line ", line, " =#", color=:light_black) show_linenumber(io::IO, line, file) = printstyled(io, "#= ", file, ":", line, " =#", color=:light_black) show_linenumber(io::IO, line, file::Nothing) = show_linenumber(io, line) # show a block, e g if/for/etc function show_block(io::IO, head, args::Vector, body, indent::Int, quote_level::Int) print(io, head) if !isempty(args) print(io, ' ') if head === :elseif show_list(io, args, " ", indent, 0, quote_level) else show_list(io, args, ", ", indent, 0, quote_level) end end ind = head === :module || head === :baremodule ? indent : indent + indent_width exs = (is_expr(body, :block) || is_expr(body, :quote)) ? body.args : Any[body] for ex in exs print(io, '\n', " "^ind) show_unquoted(io, ex, ind, -1, quote_level) end print(io, '\n', " "^indent) end show_block(io::IO,head, block,i::Int, quote_level::Int) = show_block(io,head, [], block,i, quote_level) function show_block(io::IO, head, arg, block, i::Int, quote_level::Int) if is_expr(arg, :block) || is_expr(arg, :quote) show_block(io, head, arg.args, block, i, quote_level) else show_block(io, head, Any[arg], block, i, quote_level) end end # show an indented list function show_list(io::IO, items, sep, indent::Int, prec::Int=0, quote_level::Int=0, enclose_operators::Bool=false, kw::Bool=false) n = length(items) n == 0 && return indent += indent_width first = true for item in items !first && print(io, sep) parens = !is_quoted(item) && (first && prec >= prec_power && ((item isa Expr && item.head === :call && (callee = item.args[1]; isa(callee, Symbol) && callee in uni_ops)) || (item isa Real && item < 0))) || (enclose_operators && item isa Symbol && isoperator(item) && is_valid_identifier(item)) parens && print(io, '(') if kw && is_expr(item, :kw, 2) item = item::Expr show_unquoted(io, Expr(:(=), item.args[1], item.args[2]), indent, parens ? 0 : prec, quote_level) elseif kw && is_expr(item, :(=), 2) item = item::Expr show_unquoted_expr_fallback(io, item, indent, quote_level) else show_unquoted(io, item, indent, parens ? 0 : prec, quote_level) end parens && print(io, ')') first = false end end # show an indented list inside the parens (op, cl) function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, quote_level=0, encl_ops=false, kw::Bool=false) print(io, op) show_list(io, items, sep, indent, prec, quote_level, encl_ops, kw) print(io, cl) end function is_valid_identifier(sym) return isidentifier(sym) || ( _isoperator(sym) && !(sym in (Symbol("'"), :(::), :?)) && !is_syntactic_operator(sym) ) end # show a normal (non-operator) function call, e.g. f(x, y) or A[z] # kw: `=` expressions are parsed with head `kw` in this context function show_call(io::IO, head, func, func_args, indent, quote_level, kw::Bool) op, cl = expr_calls[head] if (isa(func, Symbol) && func !== :(:) && !(head === :. && isoperator(func))) || (isa(func, Symbol) && !is_valid_identifier(func)) || (isa(func, Expr) && (func.head === :. || func.head === :curly || func.head === :macroname)) || isa(func, GlobalRef) show_unquoted(io, func, indent, 0, quote_level) else print(io, '(') show_unquoted(io, func, indent, 0, quote_level) print(io, ')') end if head === :(.) print(io, '.') end if !isempty(func_args) && isa(func_args[1], Expr) && (func_args[1]::Expr).head === :parameters print(io, op) show_list(io, func_args[2:end], ", ", indent, 0, quote_level, false, kw) print(io, "; ") show_list(io, (func_args[1]::Expr).args, ", ", indent, 0, quote_level, false, kw) print(io, cl) else show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level, false, kw) end end # Print `sym` as it would appear as an identifier name in code # * Print valid identifiers & operators literally; also macros names if allow_macroname=true # * Escape invalid identifiers with var"" syntax function show_sym(io::IO, sym::Symbol; allow_macroname=false) if is_valid_identifier(sym) print(io, sym) elseif allow_macroname && (sym_str = string(sym); startswith(sym_str, '@')) print(io, '@') show_sym(io, Symbol(sym_str[2:end])) else print(io, "var", repr(string(sym))) end end ## AST printing ## function show_unquoted(io::IO, val::SSAValue, ::Int, ::Int) if get(io, :maxssaid, typemax(Int))::Int < val.id # invalid SSAValue, print this in red for better recognition printstyled(io, "%", val.id; color=:red) else print(io, "%", val.id) end end show_unquoted(io::IO, sym::Symbol, ::Int, ::Int) = show_sym(io, sym, allow_macroname=false) show_unquoted(io::IO, ex::LineNumberNode, ::Int, ::Int) = show_linenumber(io, ex.line, ex.file) show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto %", ex.label) show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = show_globalref(io, ex) function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false) print(io, ex.mod) print(io, '.') quoted = !isidentifier(ex.name) && !startswith(string(ex.name), "@") parens = quoted && (!isoperator(ex.name) || (ex.name in quoted_syms)) quoted && print(io, ':') parens && print(io, '(') show_sym(io, ex.name, allow_macroname=allow_macroname) parens && print(io, ')') nothing end function show_unquoted(io::IO, ex::UnoptSlot, ::Int, ::Int) typ = isa(ex, TypedSlot) ? ex.typ : Any slotid = ex.id slotnames = get(io, :SOURCE_SLOTNAMES, false) if (isa(slotnames, Vector{String}) && slotid <= length(slotnames::Vector{String})) print(io, (slotnames::Vector{String})[slotid]) else print(io, "_", slotid) end if typ !== Any && isa(ex, TypedSlot) print(io, "::", typ) end end function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) if isa(ex.value, Symbol) show_unquoted_quote_expr(io, ex.value, indent, prec, 0) else print(io, "\$(QuoteNode(") # QuoteNode does not allows for interpolation, so if ex.value is an # Expr it should be shown with quote_level equal to zero. # Calling show(io, ex.value) like this implicitly enforce that. show(io, ex.value) print(io, "))") end end function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int, quote_level::Int) if isa(value, Symbol) sym = value::Symbol if value in quoted_syms print(io, ":(", sym, ")") else if isidentifier(sym) || (_isoperator(sym) && sym !== Symbol("'")) print(io, ":", sym) else print(io, "Symbol(", repr(String(sym)), ")") end end else if isa(value,Expr) && value.head === :block value = value::Expr show_block(IOContext(io, beginsym=>false), "quote", value, indent, quote_level) print(io, "end") else print(io, ":(") show_unquoted(io, value, indent+2, -1, quote_level) # +2 for `:(` print(io, ")") end end end function show_generator(io, ex::Expr, indent, quote_level) if ex.head === :flatten fg::Expr = ex ranges = Any[] while isa(fg, Expr) && fg.head === :flatten push!(ranges, (fg.args[1]::Expr).args[2:end]) fg = (fg.args[1]::Expr).args[1]::Expr end push!(ranges, fg.args[2:end]) show_unquoted(io, fg.args[1], indent, 0, quote_level) for r in ranges print(io, " for ") show_list(io, r, ", ", indent, 0, quote_level) end else show_unquoted(io, ex.args[1], indent, 0, quote_level) print(io, " for ") show_list(io, ex.args[2:end], ", ", indent, 0, quote_level) end end function valid_import_path(@nospecialize(ex), allow_as = true) if allow_as && is_expr(ex, :as) && length((ex::Expr).args) == 2 ex = (ex::Expr).args[1] end return is_expr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args) end function show_import_path(io::IO, ex, quote_level) if !isa(ex, Expr) show_unquoted(io, ex) elseif ex.head === :(:) show_import_path(io, ex.args[1], quote_level) print(io, ": ") for i = 2:length(ex.args) if i > 2 print(io, ", ") end show_import_path(io, ex.args[i], quote_level) end elseif ex.head === :(.) for i = 1:length(ex.args) sym = ex.args[i]::Symbol if sym === :(.) print(io, '.') else if sym === :(..) # special case for https://github.com/JuliaLang/julia/issues/49168 print(io, "(..)") else show_sym(io, sym, allow_macroname=(i==length(ex.args))) end i < length(ex.args) && print(io, '.') end end else show_unquoted(io, ex, 0, 0, quote_level) end end # Wrap symbols for macro names to allow them to be printed literally function allow_macroname(ex) if (ex isa Symbol && first(string(ex)) == '@') || ex isa GlobalRef || (is_expr(ex, :(.)) && length(ex.args) == 2 && (is_expr(ex.args[2], :quote) || ex.args[2] isa QuoteNode)) return Expr(:macroname, ex) else ex end end is_core_macro(arg::GlobalRef, macro_name::AbstractString) = is_core_macro(arg, Symbol(macro_name)) is_core_macro(arg::GlobalRef, macro_name::Symbol) = arg == GlobalRef(Core, macro_name) is_core_macro(@nospecialize(arg), macro_name::AbstractString) = false is_core_macro(@nospecialize(arg), macro_name::Symbol) = false # symbol for IOContext flag signaling whether "begin" is treated # as an ordinary symbol, which is true in indexing expressions. const beginsym = gensym(:beginsym) function show_unquoted_expr_fallback(io::IO, ex::Expr, indent::Int, quote_level::Int) print(io, "\$(Expr(") show(io, ex.head) for arg in ex.args print(io, ", ") show(io, arg) end print(io, "))") end # TODO: implement interpolated strings function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::Int = 0) head, args, nargs = ex.head, ex.args, length(ex.args) unhandled = false # dot (i.e. "x.y"), but not compact broadcast exps if head === :(.) && (nargs != 2 || !is_expr(args[2], :tuple)) # standalone .op if nargs == 1 && args[1] isa Symbol && isoperator(args[1]::Symbol) print(io, "(.", args[1], ")") elseif nargs == 2 && is_quoted(args[2]) item = args[1] # field field = unquoted(args[2]) parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !is_expr(item, :(.)) parens && print(io, '(') show_unquoted(io, item, indent, 0, quote_level) parens && print(io, ')') # . print(io, '.') # item if isa(field, Symbol) parens = field in quoted_syms quoted = parens || isoperator(field) else parens = quoted = true end quoted && print(io, ':') parens && print(io, '(') show_unquoted(io, field, indent, 0, quote_level) parens && print(io, ')') else unhandled = true end # infix (i.e. "x <: y" or "x = y") elseif (head in expr_infix_any && nargs==2) func_prec = operator_precedence(head) head_ = head in expr_infix_wide ? " $head " : head if func_prec <= prec show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, quote_level, true) else show_list(io, args, head_, indent, func_prec, quote_level, true) end elseif head === :tuple print(io, "(") if nargs > 0 && is_expr(args[1], :parameters) arg1 = args[1]::Expr show_list(io, args[2:end], ", ", indent, 0, quote_level) nargs == 2 && print(io, ',') print(io, ";") if !isempty(arg1.args) print(io, " ") end show_list(io, arg1.args, ", ", indent, 0, quote_level, false, true) else show_list(io, args, ", ", indent, 0, quote_level) nargs == 1 && print(io, ',') end print(io, ")") # list-like forms, e.g. "[1, 2, 3]" elseif haskey(expr_parens, head) || # :vcat etc. head === :typed_vcat || head === :typed_hcat || head === :typed_ncat # print the type and defer to the untyped case if head === :typed_vcat || head === :typed_hcat || head === :typed_ncat show_unquoted(io, args[1], indent, prec, quote_level) if head === :typed_vcat head = :vcat elseif head === :typed_hcat head = :hcat else head = :ncat end args = args[2:end] nargs = nargs - 1 end op, cl = expr_parens[head] if head === :vcat || head === :bracescat sep = "; " elseif head === :hcat || head === :row sep = " " elseif head === :ncat || head === :nrow sep = ";"^args[1]::Int * " " args = args[2:end] nargs = nargs - 1 else sep = ", " end head !== :row && head !== :nrow && print(io, op) show_list(io, args, sep, indent, 0, quote_level) if nargs <= 1 && (head === :vcat || head === :ncat) print(io, sep[1:end-1]) end head !== :row && head !== :nrow && print(io, cl) # transpose elseif (head === Symbol("'") && nargs == 1) || ( # ' with unicode suffix is a call expression head === :call && nargs == 2 && args[1] isa Symbol && ispostfixoperator(args[1]::Symbol) && args[1]::Symbol !== Symbol("'") ) op, arg1 = head === Symbol("'") ? (head, args[1]) : (args[1], args[2]) if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1::Symbol)) show_enclosed_list(io, '(', [arg1::Union{Expr, Symbol}], ", ", ')', indent, 0) else show_unquoted(io, arg1, indent, 0, quote_level) end print(io, op) # function call elseif head === :call && nargs >= 1 func = args[1] fname = isa(func, GlobalRef) ? func.name : func func_prec = operator_precedence(fname) if func_prec > 0 || (isa(fname, Symbol) && fname in uni_ops) func = fname end func_args = args[2:end] # :kw exprs are only parsed inside parenthesized calls if any(a->is_expr(a, :kw), func_args) || (!isempty(func_args) && is_expr(func_args[1], :parameters)) show_call(io, head, func, func_args, indent, quote_level, true) # scalar multiplication (i.e. "100x") elseif (func === :* && length(func_args) == 2 && isa(func_args[1], Union{Int, Int64, Float32, Float64}) && isa(func_args[2], Symbol) && !in(string(func_args[2]::Symbol)[1], ('e', 'E', 'f', (func_args[1] == 0 && func_args[1] isa Integer ? # don't juxtapose 0 with b, o, x ('b', 'o', 'x') : ())...))) if func_prec <= prec show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level) else show_list(io, func_args, "", indent, func_prec, quote_level) end # unary operator (i.e. "!z") elseif isa(func,Symbol) && length(func_args) == 1 && func in uni_ops show_unquoted(io, func, indent, 0, quote_level) arg1 = func_args[1] if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1) && is_valid_identifier(arg1)) show_enclosed_list(io, '(', func_args, ", ", ')', indent, func_prec) else show_unquoted(io, arg1, indent, func_prec, quote_level) end # binary operator (i.e. "x + y") elseif func_prec > 0 # is a binary operator func = func::Symbol # operator_precedence returns func_prec == 0 for non-Symbol na = length(func_args) if (na == 2 || (na > 2 && func in (:+, :++, :*)) || (na == 3 && func === :(:))) && all(a -> !isa(a, Expr) || a.head !== :..., func_args) sep = func === :(:) ? "$func" : " $func " if func_prec <= prec show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, quote_level, true) else show_list(io, func_args, sep, indent, func_prec, quote_level, true) end elseif na == 1 # 1-argument call to normally-binary operator op, cl = expr_calls[head] print(io, "(") show_unquoted(io, func, indent, 0, quote_level) print(io, ")") show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level) else show_call(io, head, func, func_args, indent, quote_level, true) end # normal function (i.e. "f(x,y)") else show_call(io, head, func, func_args, indent, quote_level, true) end # new expr elseif head === :new || head === :splatnew show_enclosed_list(io, "%$head(", args, ", ", ")", indent, 0, quote_level) # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)") elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.) funcargslike = head === :(.) ? (args[2]::Expr).args : args[2:end] show_call(head === :ref ? IOContext(io, beginsym=>true) : io, head, args[1], funcargslike, indent, quote_level, head !== :curly) # comprehensions elseif head === :typed_comprehension && nargs == 2 show_unquoted(io, args[1], indent, 0, quote_level) print(io, '[') show_generator(io, args[2], indent, quote_level) print(io, ']') elseif head === :comprehension && nargs == 1 print(io, '[') show_generator(io, args[1], indent, quote_level) print(io, ']') elseif (head === :generator && nargs >= 2) || (head === :flatten && nargs == 1) print(io, '(') show_generator(io, ex, indent, quote_level) print(io, ')') elseif head === :filter && nargs == 2 show_unquoted(io, args[2], indent, 0, quote_level) print(io, " if ") show_unquoted(io, args[1], indent, 0, quote_level) # comparison (i.e. "x < y < z") elseif head === :comparison && nargs >= 3 && (nargs&1==1) comp_prec = minimum(operator_precedence, args[2:2:end]) if comp_prec <= prec show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec, quote_level) else show_list(io, args, " ", indent, comp_prec, quote_level) end # function calls need to transform the function from :call to :calldecl # so that operators are printed correctly elseif head === :function && nargs==2 && is_expr(args[1], :call) show_block(IOContext(io, beginsym=>false), head, Expr(:calldecl, (args[1]::Expr).args...), args[2], indent, quote_level) print(io, "end") elseif (head === :function || head === :macro) && nargs == 1 print(io, head, ' ') show_unquoted(IOContext(io, beginsym=>false), args[1]) print(io, " end") elseif head === :do && nargs == 2 iob = IOContext(io, beginsym=>false) show_unquoted(iob, args[1], indent, -1, quote_level) print(io, " do ") show_list(iob, (((args[2]::Expr).args[1])::Expr).args, ", ", 0, 0, quote_level) for stmt in (((args[2]::Expr).args[2])::Expr).args print(io, '\n', " "^(indent + indent_width)) show_unquoted(iob, stmt, indent + indent_width, -1, quote_level) end print(io, '\n', " "^indent) print(io, "end") # block with argument elseif head in (:for,:while,:function,:macro,:if,:elseif,:let) && nargs==2 if head === :function && is_expr(args[1], :...) # fix printing of "function (x...) x end" block_args = Expr(:tuple, args[1]) else block_args = args[1] end if is_expr(args[2], :block) show_block(IOContext(io, beginsym=>false), head, block_args, args[2], indent, quote_level) else show_block(IOContext(io, beginsym=>false), head, block_args, Expr(:block, args[2]), indent, quote_level) end print(io, "end") elseif (head === :if || head === :elseif) && nargs == 3 iob = IOContext(io, beginsym=>false) show_block(iob, head, args[1], args[2], indent, quote_level) arg3 = args[3] if isa(arg3, Expr) && arg3.head === :elseif show_unquoted(iob, arg3::Expr, indent, prec, quote_level) else show_block(iob, "else", arg3, indent, quote_level) print(io, "end") end elseif head === :module && nargs==3 && isa(args[1],Bool) show_block(IOContext(io, beginsym=>false), args[1] ? :module : :baremodule, args[2], args[3], indent, quote_level) print(io, "end") # type declaration elseif head === :struct && nargs==3 show_block(IOContext(io, beginsym=>false), args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent, quote_level) print(io, "end") elseif head === :primitive && nargs == 2 print(io, "primitive type ") show_list(io, args, ' ', indent, 0, quote_level) print(io, " end") elseif head === :abstract && nargs == 1 print(io, "abstract type ") show_list(IOContext(io, beginsym=>false), args, ' ', indent, 0, quote_level) print(io, " end") # empty return (i.e. "function f() return end") elseif head === :return && nargs == 1 && args[1] === nothing print(io, head) # type annotation (i.e. "::Int") elseif head in uni_syms && nargs == 1 print(io, head) show_unquoted(io, args[1], indent, 0, quote_level) # var-arg declaration or expansion # (i.e. "function f(L...) end" or "f(B...)") elseif head === :(...) && nargs == 1 dotsprec = operator_precedence(:(:)) - 1 parens = dotsprec <= prec parens && print(io, "(") show_unquoted(io, args[1], indent, dotsprec, quote_level) print(io, "...") parens && print(io, ")") elseif (nargs == 0 && head in (:break, :continue)) print(io, head) elseif (nargs == 1 && head in (:return, :const)) || head in (:local, :global) print(io, head, ' ') show_list(io, args, ", ", indent, 0, quote_level) elseif head === :export print(io, head, ' ') show_list(io, mapany(allow_macroname, args), ", ", indent) elseif head === :macrocall && nargs >= 2 # handle some special syntaxes # `a b c` if is_core_macro(args[1], :var"@cmd") print(io, "`", args[3], "`") # 11111111111111111111, 0xfffffffffffffffff, 1111...many digits... elseif is_core_macro(args[1], :var"@int128_str") || is_core_macro(args[1], :var"@uint128_str") || is_core_macro(args[1], :var"@big_str") print(io, args[3]) # x"y" and x"y"z elseif isa(args[1], Symbol) && nargs >= 3 && isa(args[3], String) && startswith(string(args[1]::Symbol), "@") && endswith(string(args[1]::Symbol), "_str") s = string(args[1]::Symbol) print(io, s[2:prevind(s,end,4)], "\"") escape_raw_string(io, args[3]) print(io, "\"") if nargs == 4 print(io, args[4]) end # general case else # first show the line number argument as a comment if isa(args[2], LineNumberNode) || is_expr(args[2], :line) print(io, args[2], ' ') end # Use the functional syntax unless specifically designated with # prec=-1 and hide the line number argument from the argument list mname = allow_macroname(args[1]) if prec >= 0 show_call(io, :call, mname, args[3:end], indent, quote_level, false) else show_args = Vector{Any}(undef, nargs - 1) show_args[1] = mname show_args[2:end] = args[3:end] show_list(io, show_args, ' ', indent, 0, quote_level) end end elseif head === :macroname && nargs == 1 arg1 = args[1] if arg1 isa Symbol show_sym(io, arg1, allow_macroname=true) elseif arg1 isa GlobalRef show_globalref(io, arg1, allow_macroname=true) elseif is_expr(arg1, :(.)) && length((arg1::Expr).args) == 2 arg1 = arg1::Expr m = arg1.args[1] if m isa Symbol || m isa GlobalRef || is_expr(m, :(.), 2) show_unquoted(io, m) else print(io, "(") show_unquoted(io, m) print(io, ")") end print(io, '.') if is_expr(arg1.args[2], :quote) mname = (arg1.args[2]::Expr).args[1] else mname = (arg1.args[2]::QuoteNode).value end if mname isa Symbol show_sym(io, mname, allow_macroname=true) else show_unquoted(io, mname) end else show_unquoted(io, arg1) end elseif head === :line && 1 <= nargs <= 2 show_linenumber(io, args...) elseif head === :try && 3 <= nargs <= 5 iob = IOContext(io, beginsym=>false) show_block(iob, "try", args[1], indent, quote_level) if is_expr(args[3], :block) show_block(iob, "catch", args[2] === false ? Any[] : args[2], args[3]::Expr, indent, quote_level) end if nargs >= 5 && is_expr(args[5], :block) show_block(iob, "else", Any[], args[5]::Expr, indent, quote_level) end if nargs >= 4 && is_expr(args[4], :block) show_block(iob, "finally", Any[], args[4]::Expr, indent, quote_level) end print(io, "end") elseif head === :block # print as (...; ...; ...;) inside indexing expression if get(io, beginsym, false) print(io, '(') ind = indent + indent_width for i = 1:length(ex.args) if i > 1 # if there was only a comment before the first semicolon, the expression would get parsed as a NamedTuple if !(i == 2 && ex.args[1] isa LineNumberNode) print(io, ';') end print(io, "\n", ' '^ind) end show_unquoted(io, ex.args[i], ind, -1, quote_level) end if length(ex.args) < 2 print(io, isempty(ex.args) ? ";;)" : ";)") else print(io, ')') end else show_block(io, "begin", ex, indent, quote_level) print(io, "end") end elseif head === :quote && nargs == 1 && isa(args[1], Symbol) show_unquoted_quote_expr(IOContext(io, beginsym=>false), args[1]::Symbol, indent, 0, quote_level+1) elseif head === :quote && !(get(io, :unquote_fallback, true)::Bool) if nargs == 1 && is_expr(args[1], :block) show_block(IOContext(io, beginsym=>false), "quote", Expr(:quote, (args[1]::Expr).args...), indent, quote_level+1) print(io, "end") elseif nargs == 1 print(io, ":(") show_unquoted(IOContext(io, beginsym=>false), args[1], indent+2, 0, quote_level+1) print(io, ")") else show_block(IOContext(io, beginsym=>false), "quote", ex, indent, quote_level+1) print(io, "end") end elseif head === :gotoifnot && nargs == 2 && isa(args[2], Int) print(io, "unless ") show_unquoted(io, args[1], indent, 0, quote_level) print(io, " goto %") print(io, args[2]::Int) elseif head === :string && nargs == 1 && isa(args[1], AbstractString) show(io, args[1]) elseif head === :null print(io, "nothing") elseif head === :string print(io, '"') for x in args if !isa(x,AbstractString) print(io, "\$(") if isa(x,Symbol) && !(x in quoted_syms) show_sym(io, x) else show_unquoted(io, x, 0, 0, quote_level) end print(io, ")") else escape_string(io, String(x)::String, "\"\$") end end print(io, '"') elseif (head === :& || head === :$) && nargs == 1 if head === :$ quote_level -= 1 end if head === :$ && get(io, :unquote_fallback, true) unhandled = true else print(io, head) a1 = args[1] parens = (isa(a1,Expr) && !in(a1.head, (:tuple, :$, :vect, :braces))) || (isa(a1,Symbol) && isoperator(a1)) parens && print(io, "(") show_unquoted(io, a1, 0, 0, quote_level) parens && print(io, ")") end # `where` syntax elseif head === :where && nargs > 1 parens = 1 <= prec parens && print(io, "(") show_unquoted(io, args[1], indent, operator_precedence(:(::)), quote_level) print(io, " where ") if nargs == 2 show_unquoted(io, args[2], indent, 1, quote_level) else print(io, "{") show_list(io, args[2:end], ", ", indent, 0, quote_level) print(io, "}") end parens && print(io, ")") elseif (head === :import || head === :using) && ((nargs == 1 && (valid_import_path(args[1]) || (is_expr(args[1], :(:)) && length((args[1]::Expr).args) > 1 && all(valid_import_path, (args[1]::Expr).args)))) || all(valid_import_path, args)) print(io, head) print(io, ' ') first = true for a in args if !first print(io, ", ") end first = false show_import_path(io, a, quote_level) end elseif head === :as && nargs == 2 && valid_import_path(args[1], false) show_import_path(io, args[1], quote_level) print(io, " as ") show_unquoted(io, args[2], indent, 0, quote_level) elseif head === :meta && nargs >= 2 && args[1] === :push_loc print(io, "# meta: location ", join(args[2:end], " ")) elseif head === :meta && nargs == 1 && args[1] === :pop_loc print(io, "# meta: pop location") elseif head === :meta && nargs == 2 && args[1] === :pop_loc print(io, "# meta: pop locations ($(args[2]::Int))") # print anything else as "Expr(head, args...)" else unhandled = true end if unhandled show_unquoted_expr_fallback(io, ex, indent, quote_level) end nothing end demangle_function_name(name::Symbol) = Symbol(demangle_function_name(string(name))) function demangle_function_name(name::AbstractString) demangle = split(name, '#') # kw sorters and impl methods use the name scheme `f#...` if length(demangle) >= 2 && demangle[1] != "" return demangle[1] end return name end # show the called object in a signature, given its type `ft` # `io` should contain the UnionAll env of the signature function show_signature_function(io::IO, @nospecialize(ft), demangle=false, fargname="", html=false, qualified=false) uw = unwrap_unionall(ft) if ft <: Function && isa(uw, DataType) && isempty(uw.parameters) && _isself(uw) uwmod = parentmodule(uw) if qualified && !is_exported_from_stdlib(uw.name.mt.name, uwmod) && uwmod !== Main print_within_stacktrace(io, uwmod, '.', bold=true) end s = sprint(show_sym, (demangle ? demangle_function_name : identity)(uw.name.mt.name), context=io) print_within_stacktrace(io, s, bold=true) elseif isType(ft) && (f = ft.parameters[1]; !isa(f, TypeVar)) uwf = unwrap_unionall(f) parens = isa(f, UnionAll) && !(isa(uwf, DataType) && f === uwf.name.wrapper) parens && print(io, "(") print_within_stacktrace(io, f, bold=true) parens && print(io, ")") else if html print(io, "($fargname::", ft, ")") else print_within_stacktrace(io, "($fargname::", ft, ")", bold=true) end end nothing end function print_within_stacktrace(io, s...; color=:normal, bold=false) if get(io, :backtrace, false)::Bool printstyled(io, s...; color, bold) else print(io, s...) end end function show_tuple_as_call(out::IO, name::Symbol, sig::Type; demangle=false, kwargs=nothing, argnames=nothing, qualified=false, hasfirst=true) # print a method signature tuple for a lambda definition if sig === Tuple print(out, demangle ? demangle_function_name(name) : name, "(...)") return end tv = Any[] io = IOContext(IOBuffer(), out) env_io = io while isa(sig, UnionAll) push!(tv, sig.var) env_io = IOContext(env_io, :unionall_env => sig.var) sig = sig.body end n = 1 sig = (sig::DataType).parameters if hasfirst show_signature_function(env_io, sig[1], demangle, "", false, qualified) n += 1 end first = true print_within_stacktrace(io, "(", bold=true) show_argnames = argnames !== nothing && length(argnames) == length(sig) for i = n:length(sig) # fixme (iter): `eachindex` with offset? first || print(io, ", ") first = false if show_argnames print_within_stacktrace(io, argnames[i]; color=:light_black) end print(io, "::") print_type_bicolor(env_io, sig[i]; use_color = get(io, :backtrace, false)::Bool) end if kwargs !== nothing print(io, "; ") first = true for (k, t) in kwargs first || print(io, ", ") first = false print_within_stacktrace(io, k; color=:light_black) if t == pairs(NamedTuple) # omit type annotation for splat keyword argument print(io, "...") else print(io, "::") print_type_bicolor(io, t; use_color = get(io, :backtrace, false)::Bool) end end end print_within_stacktrace(io, ")", bold=true) show_method_params(io, tv) str = String(take!(unwrapcontext(io)[1])) str = type_limited_string_from_context(out, str) print(out, str) nothing end function type_limited_string_from_context(out::IO, str::String) typelimitflag = get(out, :stacktrace_types_limited, nothing) if typelimitflag isa RefValue{Bool} sz = get(out, :displaysize, displaysize(out))::Tuple{Int, Int} str_lim = type_depth_limit(str, max(sz[2], 120)) if sizeof(str_lim) < sizeof(str) typelimitflag[] = true end str = str_lim end return str end # limit nesting depth of `{ }` until string textwidth is less than `n` function type_depth_limit(str::String, n::Int; maxdepth = nothing) depth = 0 width_at = Int[] # total textwidth at each nesting depth depths = zeros(Int16, lastindex(str)) # depth at each character index levelcount = Int[] # number of nodes at each level strwid = 0 st_0, st_backslash, st_squote, st_dquote = 0,1,2,4 state::Int = st_0 stateis(s) = (state & s) != 0 quoted() = stateis(st_squote) || stateis(st_dquote) enter(s) = (state |= s) leave(s) = (state &= ~s) for (i, c) in ANSIIterator(str) if c isa ANSIDelimiter depths[i] = depth continue end if c == '\\' && quoted() enter(st_backslash) elseif c == '\'' if stateis(st_backslash) || stateis(st_dquote) elseif stateis(st_squote) leave(st_squote) else enter(st_squote) end elseif c == '"' if stateis(st_backslash) || stateis(st_squote) elseif stateis(st_dquote) leave(st_dquote) else enter(st_dquote) end end if c == '}' && !quoted() depth -= 1 end wid = textwidth(c) strwid += wid if depth > 0 width_at[depth] += wid end depths[i] = depth if c == '{' && !quoted() depth += 1 if depth > length(width_at) push!(width_at, 0) push!(levelcount, 0) end levelcount[depth] += 1 end if c != '\\' && stateis(st_backslash) leave(st_backslash) end end if maxdepth === nothing limit_at = length(width_at) + 1 while strwid > n limit_at -= 1 limit_at <= 1 && break # add levelcount[] to include space taken by `โ€ฆ` strwid = strwid - width_at[limit_at] + levelcount[limit_at] if limit_at < length(width_at) # take away the `โ€ฆ` from the previous considered level strwid -= levelcount[limit_at+1] end end else limit_at = maxdepth end output = IOBuffer() prev = 0 for (i, c) in ANSIIterator(str) di = depths[i] if di < limit_at if c isa ANSIDelimiter write(output, c.del) else write(output, c) end end if di > prev && di == limit_at write(output, "โ€ฆ") end prev = di end return String(take!(output)) end function print_type_bicolor(io, type; kwargs...) str = sprint(show, type, context=io) print_type_bicolor(io, str; kwargs...) end function print_type_bicolor(io, str::String; color=:normal, inner_color=:light_black, use_color::Bool=true) i = findfirst('{', str) if !use_color # fix #41928 print(io, str) elseif i === nothing printstyled(io, str; color=color) else printstyled(io, str[1:prevind(str,i)]; color=color) if endswith(str, "...") printstyled(io, str[i:prevind(str,end,3)]; color=inner_color) printstyled(io, "..."; color=color) else printstyled(io, str[i:end]; color=inner_color) end end end resolvebinding(@nospecialize(ex)) = ex resolvebinding(ex::QuoteNode) = ex.value resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex)) function resolvebinding(ex::Expr) if ex.head === :. && isa(ex.args[2], Symbol) parent = resolvebinding(ex.args[1]) if isa(parent, Module) return resolvebinding(GlobalRef(parent, ex.args[2])) end end return nothing end function resolvebinding(ex::GlobalRef) isdefined(ex.mod, ex.name) || return nothing isconst(ex.mod, ex.name) || return nothing m = getfield(ex.mod, ex.name) isa(m, Module) || return nothing return m end function ismodulecall(ex::Expr) return ex.head === :call && (ex.args[1] === GlobalRef(Base,:getfield) || ex.args[1] === GlobalRef(Core,:getfield)) && isa(resolvebinding(ex.args[2]), Module) end function show(io::IO, tv::TypeVar) # If we are in the `unionall_env`, the type-variable is bound # and the type constraints are already printed. # We don't need to print it again. # Otherwise, the lower bound should be printed if it is not `Bottom` # and the upper bound should be printed if it is not `Any`. in_env = (:unionall_env => tv) in io function show_bound(io::IO, @nospecialize(b)) parens = isa(b,UnionAll) && !print_without_params(b) parens && print(io, "(") show(io, b) parens && print(io, ")") end lb, ub = tv.lb, tv.ub if !in_env && lb !== Bottom if ub === Any show_unquoted(io, tv.name) print(io, ">:") show_bound(io, lb) else show_bound(io, lb) print(io, "<:") show_unquoted(io, tv.name) end else show_unquoted(io, tv.name) end if !in_env && ub !== Any print(io, "<:") show_bound(io, ub) end nothing end function show(io::IO, vm::Core.TypeofVararg) print(io, "Vararg") if isdefined(vm, :T) print(io, "{") show(io, vm.T) if isdefined(vm, :N) print(io, ", ") show(io, vm.N) end print(io, "}") end end module IRShow const Compiler = Core.Compiler using Core.IR import ..Base import .Compiler: IRCode, TypedSlot, CFG, scan_ssa_use!, isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact, Effects, ALWAYS_TRUE, ALWAYS_FALSE Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind) Base.size(r::Compiler.StmtRange) = Compiler.size(r) Base.first(r::Compiler.StmtRange) = Compiler.first(r) Base.last(r::Compiler.StmtRange) = Compiler.last(r) Base.length(is::Compiler.InstructionStream) = Compiler.length(is) Base.iterate(is::Compiler.InstructionStream, st::Int=1) = (st <= Compiler.length(is)) ? (is[st], st + 1) : nothing Base.getindex(is::Compiler.InstructionStream, idx::Int) = Compiler.getindex(is, idx) Base.getindex(node::Compiler.Instruction, fld::Symbol) = Compiler.getindex(node, fld) include("compiler/ssair/show.jl") const __debuginfo = Dict{Symbol, Any}( # :full => src -> Base.IRShow.statementidx_lineinfo_printer(src), # and add variable slot information :source => src -> Base.IRShow.statementidx_lineinfo_printer(src), # :oneliner => src -> Base.IRShow.statementidx_lineinfo_printer(Base.IRShow.PartialLineInfoPrinter, src), :none => src -> Base.IRShow.lineinfo_disabled, ) const default_debuginfo = Ref{Symbol}(:none) debuginfo(sym) = sym === :default ? default_debuginfo[] : sym end function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) # Fix slot names and types in function body print(io, "CodeInfo(") lambda_io::IOContext = io if src.slotnames !== nothing lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src)) end if isempty(src.linetable) || src.linetable[1] isa LineInfoNode println(io) # TODO: static parameter values? # only accepts :source or :none, we can't have a fallback for default since # that would break code_typed(, debuginfo=:source) iff IRShow.default_debuginfo[] = :none IRShow.show_ir(lambda_io, src, IRShow.IRShowConfig(IRShow.__debuginfo[debuginfo](src))) else # this is a CodeInfo that has not been used as a method yet, so its locations are still LineNumberNodes body = Expr(:block) body.args = src.code show(lambda_io, body) end print(io, ")") end function show(io::IO, inferred::Core.Compiler.InferenceResult) mi = inferred.linfo tt = mi.specTypes.parameters[2:end] tts = join(["::$(t)" for t in tt], ", ") rettype = inferred.result if isa(rettype, Core.Compiler.InferenceState) rettype = rettype.bestguess end if isa(mi.def, Method) print(io, mi.def.name, "(", tts, " => ", rettype, ")") else print(io, "Toplevel MethodInstance thunk from ", mi.def, " => ", rettype) end end function show(io::IO, ::Core.Compiler.NativeInterpreter) print(io, "Core.Compiler.NativeInterpreter(...)") end function dump(io::IOContext, x::SimpleVector, n::Int, indent) if isempty(x) print(io, "empty SimpleVector") return end print(io, "SimpleVector") if n > 0 for i = 1:length(x) println(io) print(io, indent, " ", i, ": ") if isassigned(x,i) dump(io, x[i], n - 1, string(indent, " ")) else print(io, undef_ref_str) end end end nothing end function dump(io::IOContext, @nospecialize(x), n::Int, indent) if x === Union{} show(io, x) return end T = typeof(x) if isa(x, Function) print(io, x, " (function of type ", T, ")") else print(io, T) end nf = nfields(x) if nf > 0 if n > 0 && !show_circular(io, x) recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x)) for field in 1:nf println(io) fname = string(fieldname(T, field)) print(io, indent, " ", fname, ": ") if isdefined(x,field) dump(recur_io, getfield(x, field), n - 1, string(indent, " ")) else print(io, undef_ref_str) end end end elseif !isa(x, Function) print(io, " ") show(io, x) end nothing end dump(io::IOContext, x::Module, n::Int, indent) = print(io, "Module ", x) dump(io::IOContext, x::String, n::Int, indent) = (print(io, "String "); show(io, x)) dump(io::IOContext, x::Symbol, n::Int, indent) = print(io, typeof(x), " ", x) dump(io::IOContext, x::Union, n::Int, indent) = print(io, x) dump(io::IOContext, x::Ptr, n::Int, indent) = print(io, x) function dump_elts(io::IOContext, x::Array, n::Int, indent, i0, i1) for i in i0:i1 print(io, indent, " ", i, ": ") if !isassigned(x,i) print(io, undef_ref_str) else dump(io, x[i], n - 1, string(indent, " ")) end i < i1 && println(io) end end function dump(io::IOContext, x::Array, n::Int, indent) print(io, "Array{", eltype(x), "}(", size(x), ")") if eltype(x) <: Number print(io, " ") show(io, x) else if n > 0 && !isempty(x) && !show_circular(io, x) println(io) recur_io = IOContext(io, :SHOWN_SET => x) lx = length(x) if get(io, :limit, false)::Bool dump_elts(recur_io, x, n, indent, 1, (lx <= 10 ? lx : 5)) if lx > 10 println(io) println(io, indent, " ...") dump_elts(recur_io, x, n, indent, lx - 4, lx) end else dump_elts(recur_io, x, n, indent, 1, lx) end end end nothing end # Types function dump(io::IOContext, x::DataType, n::Int, indent) print(io, x) if x !== Any print(io, " <: ", supertype(x)) end if n > 0 && !(x <: Tuple) && !isabstracttype(x) tvar_io::IOContext = io for tparam in x.parameters # approximately recapture the list of tvar parameterization # that may be used by the internal fields if isa(tparam, TypeVar) tvar_io = IOContext(tvar_io, :unionall_env => tparam) end end if x.name === _NAMEDTUPLE_NAME && !(x.parameters[1] isa Tuple) # named tuple type with unknown field names return end fields = fieldnames(x) fieldtypes = datatype_fieldtypes(x) for idx in 1:length(fields) println(io) print(io, indent, " ", fields[idx], "::") print(tvar_io, fieldtypes[idx]) end end nothing end const DUMP_DEFAULT_MAXDEPTH = 8 function dump(io::IO, @nospecialize(x); maxdepth=DUMP_DEFAULT_MAXDEPTH) dump(IOContext(io), x, maxdepth, "") println(io) end """ dump(x; maxdepth=$DUMP_DEFAULT_MAXDEPTH) Show every part of the representation of a value. The depth of the output is truncated at `maxdepth`. # Examples ```jldoctest julia> struct MyStruct x y end julia> x = MyStruct(1, (2,3)); julia> dump(x) MyStruct x: Int64 1 y: Tuple{Int64, Int64} 1: Int64 2 2: Int64 3 julia> dump(x; maxdepth = 1) MyStruct x: Int64 1 y: Tuple{Int64, Int64} ``` """ function dump(arg; maxdepth=DUMP_DEFAULT_MAXDEPTH) # this is typically used interactively, so default to being in Main (or current active module) mod = get(stdout, :module, active_module()) dump(IOContext(stdout::IO, :limit => true, :module => mod), arg; maxdepth=maxdepth) end nocolor(io::IO) = IOContext(io, :color => false) alignment_from_show(io::IO, x::Any) = textwidth(sprint(show, x, context=nocolor(io), sizehint=0)) """ `alignment(io, X)` returns a tuple (left,right) showing how many characters are needed on either side of an alignment feature such as a decimal point. # Examples ```jldoctest julia> Base.alignment(stdout, 42) (2, 0) julia> Base.alignment(stdout, 4.23) (1, 3) julia> Base.alignment(stdout, 1 + 10im) (3, 5) ``` """ alignment(io::IO, x::Any) = (0, alignment_from_show(io, x)) alignment(io::IO, x::Number) = (alignment_from_show(io, x), 0) alignment(io::IO, x::Integer) = (alignment_from_show(io, x), 0) function alignment(io::IO, x::Real) s = sprint(show, x, context=nocolor(io), sizehint=0) m = match(r"^(.*?)((?:[\.eEfF].*)?)$", s) m === nothing ? (textwidth(s), 0) : (textwidth(m.captures[1]), textwidth(m.captures[2])) end function alignment(io::IO, x::Complex) s = sprint(show, x, context=nocolor(io), sizehint=0) m = match(r"^(.*[^ef][\+\-])(.*)$", s) m === nothing ? (textwidth(s), 0) : (textwidth(m.captures[1]), textwidth(m.captures[2])) end function alignment(io::IO, x::Rational) s = sprint(show, x, context=nocolor(io), sizehint=0) m = match(r"^(.*?/)(/.*)$", s) m === nothing ? (textwidth(s), 0) : (textwidth(m.captures[1]), textwidth(m.captures[2])) end function alignment(io::IO, x::Pair) fullwidth = alignment_from_show(io, x) if !isdelimited(io, x) # i.e. use "=>" for display ctx = IOContext(io, :typeinfo => gettypeinfos(io, x)[1]) left = alignment_from_show(ctx, x.first) left += 2 * !isdelimited(ctx, x.first) # for parens around p.first left += !(get(io, :compact, false)::Bool) # spaces are added around "=>" (left+1, fullwidth-left-1) # +1 for the "=" part of "=>" else (0, fullwidth) # as for x::Any end end const undef_ref_str = "#undef" show(io::IO, ::UndefInitializer) = print(io, "UndefInitializer()") """ summary(io::IO, x) str = summary(x) Print to a stream `io`, or return a string `str`, giving a brief description of a value. By default returns `string(typeof(x))`, e.g. [`Int64`](@ref). For arrays, returns a string of size and type info, e.g. `10-element Array{Int64,1}`. # Examples ```jldoctest julia> summary(1) "Int64" julia> summary(zeros(2)) "2-element Vector{Float64}" ``` """ summary(io::IO, x) = print(io, typeof(x)) function summary(x) io = IOBuffer() summary(io, x) String(take!(io)) end ## `summary` for AbstractArrays # sizes such as 0-dimensional, 4-dimensional, 2x3 dims2string(d) = isempty(d) ? "0-dimensional" : length(d) == 1 ? "$(d[1])-element" : join(map(string,d), 'ร—') inds2string(inds) = join(map(_indsstring,inds), 'ร—') _indsstring(i) = string(i) _indsstring(i::Union{IdentityUnitRange, Slice}) = string(i.indices) # anything array-like gets summarized e.g. 10-element Array{Int64,1} summary(io::IO, a::AbstractArray) = array_summary(io, a, axes(a)) function array_summary(io::IO, a, inds::Tuple{Vararg{OneTo}}) print(io, dims2string(length.(inds)), " ") showarg(io, a, true) end function array_summary(io::IO, a, inds) print(io, dims2string(length.(inds)), " ") showarg(io, a, true) print(io, " with indices ", inds2string(inds)) end ## `summary` for Function summary(io::IO, f::Function) = show(io, MIME"text/plain"(), f) """ showarg(io::IO, x, toplevel) Show `x` as if it were an argument to a function. This function is used by [`summary`](@ref) to display type information in terms of sequences of function calls on objects. `toplevel` is `true` if this is the direct call from `summary` and `false` for nested (recursive) calls. The fallback definition is to print `x` as "::\\\$(typeof(x))", representing argument `x` in terms of its type. (The double-colon is omitted if `toplevel=true`.) However, you can specialize this function for specific types to customize printing. # Example A SubArray created as `view(a, :, 3, 2:5)`, where `a` is a 3-dimensional Float64 array, has type SubArray{Float64, 2, Array{Float64, 3}, Tuple{Colon, Int64, UnitRange{Int64}}, false} The default `show` printing would display this full type. However, the summary for SubArrays actually prints as 2ร—4 view(::Array{Float64, 3}, :, 3, 2:5) with eltype Float64 because of a definition similar to function Base.showarg(io::IO, v::SubArray, toplevel) print(io, "view(") showarg(io, parent(v), false) print(io, ", ", join(v.indices, ", ")) print(io, ')') toplevel && print(io, " with eltype ", eltype(v)) end Note that we're calling `showarg` recursively for the parent array type, indicating that any recursed calls are not at the top level. Printing the parent as `::Array{Float64,3}` is the fallback (non-toplevel) behavior, because no specialized method for `Array` has been defined. """ function showarg(io::IO, T::Type, toplevel) toplevel || print(io, "::") print(io, "Type{", T, "}") end function showarg(io::IO, @nospecialize(x), toplevel) toplevel || print(io, "::") print(io, typeof(x)) end # This method resolves an ambiguity for packages that specialize on eltype function showarg(io::IO, a::Array{Union{}}, toplevel) toplevel || print(io, "::") print(io, typeof(a)) end # Container specializations function showarg(io::IO, v::SubArray, toplevel) print(io, "view(") showarg(io, parent(v), false) showindices(io, v.indices...) print(io, ')') toplevel && print(io, " with eltype ", eltype(v)) return nothing end showindices(io, ::Slice, inds...) = (print(io, ", :"); showindices(io, inds...)) showindices(io, ind1, inds...) = (print(io, ", ", ind1); showindices(io, inds...)) showindices(io) = nothing function showarg(io::IO, r::ReshapedArray, toplevel) print(io, "reshape(") showarg(io, parent(r), false) print(io, ", ", join(r.dims, ", ")) print(io, ')') toplevel && print(io, " with eltype ", eltype(r)) return nothing end function showarg(io::IO, r::NonReshapedReinterpretArray{T}, toplevel) where {T} print(io, "reinterpret(", T, ", ") showarg(io, parent(r), false) print(io, ')') end function showarg(io::IO, r::ReshapedReinterpretArray{T}, toplevel) where {T} print(io, "reinterpret(reshape, ", T, ", ") showarg(io, parent(r), false) print(io, ')') toplevel && print(io, " with eltype ", eltype(r)) return nothing end # printing iterators from Base.Iterators function show(io::IO, e::Iterators.Enumerate) print(io, "enumerate(") show(io, e.itr) print(io, ')') end show(io::IO, z::Iterators.Zip) = show_delim_array(io, z.is, "zip(", ',', ')', false) # pretty printing for Iterators.Pairs function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T<:AbstractArray print(io, "pairs(IndexLinear(), ::", T, ")") end function Base.showarg(io::IO, r::Iterators.Pairs{Symbol, <:Any, <:Any, T}, toplevel) where {T <: NamedTuple} print(io, "pairs(::NamedTuple)") end function Base.showarg(io::IO, r::Iterators.Pairs{<:Any, <:Any, I, D}, toplevel) where {D, I} print(io, "Iterators.Pairs(::", D, ", ::", I, ")") end # printing BitArrays # (following functions not exported - mainly intended for debug) function print_bit_chunk(io::IO, c::UInt64, l::Integer = 64) for s = 0:l-1 d = (c >>> s) & 1 print(io, "01"[d + 1]) if (s + 1) & 7 == 0 print(io, " ") end end end print_bit_chunk(c::UInt64, l::Integer) = print_bit_chunk(stdout, c, l) print_bit_chunk(c::UInt64) = print_bit_chunk(stdout, c) function bitshow(io::IO, B::BitArray) isempty(B) && return Bc = B.chunks for i = 1:length(Bc)-1 print_bit_chunk(io, Bc[i]) print(io, ": ") end l = _mod64(length(B)-1) + 1 print_bit_chunk(io, Bc[end], l) end bitshow(B::BitArray) = bitshow(stdout, B) bitstring(B::BitArray) = sprint(bitshow, B) # printing OpaqueClosure function show(io::IO, oc::Core.OpaqueClosure) A, R = typeof(oc).parameters show_tuple_as_call(io, Symbol(""), A; hasfirst=false) print(io, "::", R) print(io, "->โ—Œ") end function show(io::IO, ::MIME"text/plain", oc::Core.OpaqueClosure{A, R}) where {A, R} show(io, oc) end Z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/compiler/ssair/show.jlก# This file is a part of Julia. License is MIT: https://julialang.org/license # This file is not loaded into `Core.Compiler` but rather loaded into the context of # `Base.IRShow` and thus does not participate in bootstrapping. @nospecialize if Pair != Base.Pair import Base: Base, IOContext, string, join, sprint IOContext(io::IO, KV::Pair) = IOContext(io, Base.Pair(KV[1], KV[2])) length(s::String) = Base.length(s) ^(s::String, i::Int) = Base.:^(s, i) end import Base: show_unquoted using Base: printstyled, with_output_color, prec_decl, @invoke function Base.show(io::IO, cfg::CFG) print(io, "CFG with $(length(cfg.blocks)) blocks:") for (idx, block) in enumerate(cfg.blocks) print(io, "\n bb ", idx) if block.stmts.start == block.stmts.stop print(io, " (stmt ", block.stmts.start, ")") else print(io, " (stmts ", block.stmts.start, ":", block.stmts.stop, ")") end if !isempty(block.succs) print(io, " โ†’ bb ") join(io, block.succs, ", ") end end end function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxlength_idx::Int, color::Bool, show_type::Bool) if idx in used idx_s = string(idx) pad = " "^(maxlength_idx - length(idx_s) + 1) print(io, "%", idx_s, pad, "= ") else print(io, " "^(maxlength_idx + 4)) end # TODO: `indent` is supposed to be the full width of the leader for correct alignment indent = 16 if !color && stmt isa PiNode # when the outer context is already colored (green, for pending nodes), don't use the usual coloring printer print(io, "ฯ€ (") show_unquoted(io, stmt.val, indent) print(io, ", ") print(io, stmt.typ) print(io, ")") elseif isexpr(stmt, :invoke) && length(stmt.args) >= 2 && isa(stmt.args[1], MethodInstance) stmt = stmt::Expr # TODO: why is this here, and not in Base.show_unquoted print(io, "invoke ") linfo = stmt.args[1]::Core.MethodInstance show_unquoted(io, stmt.args[2], indent) print(io, "(") # XXX: this is wrong if `sig` is not a concretetype method # more correct would be to use `fieldtype(sig, i)`, but that would obscure / discard Varargs information in show sig = linfo.specTypes == Tuple ? Core.svec() : Base.unwrap_unionall(linfo.specTypes).parameters::Core.SimpleVector print_arg(i) = sprint(; context=io) do io show_unquoted(io, stmt.args[i], indent) if (i - 1) <= length(sig) print(io, "::", sig[i - 1]) end end join(io, (print_arg(i) for i = 3:length(stmt.args)), ", ") print(io, ")") # given control flow information, we prefer to print these with the basic block #, instead of the ssa % elseif isexpr(stmt, :enter) && length((stmt::Expr).args) == 1 && (stmt::Expr).args[1] isa Int print(io, "\$(Expr(:enter, #", (stmt::Expr).args[1]::Int, "))") elseif stmt isa GotoNode print(io, "goto #", stmt.label) elseif stmt isa PhiNode show_unquoted_phinode(io, stmt, indent, "#") elseif stmt isa GotoIfNot show_unquoted_gotoifnot(io, stmt, indent, "#") elseif stmt isa TypedSlot # call `show` with the type set to Any so it will not be shown, since # we will show the type ourselves. show_unquoted(io, SlotNumber(stmt.id), indent, show_type ? prec_decl : 0) # everything else in the IR, defer to the generic AST printer else show_unquoted(io, stmt, indent, show_type ? prec_decl : 0) end nothing end show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, Core.SlotNumber(val.n), indent, prec) show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%") function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String) args = String[let e = stmt.edges[i] v = !isassigned(stmt.values, i) ? "#undef" : sprint(; context=io) do ioโ€ฒ show_unquoted(ioโ€ฒ, stmt.values[i], indent) end "$prefix$e => $v" end for i in 1:length(stmt.edges) ] print(io, "ฯ† ", '(') join(io, args, ", ") print(io, ')') end function show_unquoted(io::IO, stmt::PhiCNode, indent::Int, ::Int) print(io, "ฯ†แถœ (") first = true for v in stmt.values first ? (first = false) : print(io, ", ") show_unquoted(io, v, indent) end print(io, ")") end function show_unquoted(io::IO, stmt::PiNode, indent::Int, ::Int) print(io, "ฯ€ (") show_unquoted(io, stmt.val, indent) print(io, ", ") printstyled(io, stmt.typ, color=:cyan) print(io, ")") end function show_unquoted(io::IO, stmt::UpsilonNode, indent::Int, ::Int) print(io, "ฯ’ (") isdefined(stmt, :val) ? show_unquoted(io, stmt.val, indent) : print(io, "#undef") print(io, ")") end function show_unquoted(io::IO, stmt::ReturnNode, indent::Int, ::Int) if !isdefined(stmt, :val) print(io, "unreachable") else print(io, "return ") show_unquoted(io, stmt.val, indent) end end show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%") function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String) print(io, "goto ", prefix, stmt.dest, " if not ") show_unquoted(io, stmt.cond, indent) end function compute_inlining_depth(linetable::Vector, iline::Int32) iline == 0 && return 1 depth = -1 while iline != 0 depth += 1 lineinfo = linetable[iline]::LineInfoNode iline = lineinfo.inlined_at end return depth end function should_print_ssa_type(@nospecialize node) if isa(node, Expr) return !(node.head in (:gc_preserve_begin, :gc_preserve_end, :meta, :enter, :leave)) end return !isa(node, PiNode) && !isa(node, GotoIfNot) && !isa(node, GotoNode) && !isa(node, ReturnNode) && !isa(node, QuoteNode) end function default_expr_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...) show_type || return nothing printstyled(io, "::", type, color=(used ? :cyan : :light_black)) return nothing end function normalize_method_name(m) if m isa Method return m.name elseif m isa MethodInstance return (m.def::Method).name elseif m isa Symbol return m else return Symbol("") end end @noinline method_name(m::LineInfoNode) = normalize_method_name(m.method) # converts the linetable for line numbers # into a list in the form: # 1 outer-most-frame # 2 inlined-frame # 3 innermost-frame function compute_loc_stack(linetable::Vector, line::Int32) stack = Int[] while line != 0 entry = linetable[line]::LineInfoNode pushfirst!(stack, line) line = entry.inlined_at end return stack end """ Compute line number annotations for an IRCode This functions compute three sets of annotations for each IR line. Take the following example (taken from `@code_typed sin(1.0)`): ``` ** *** ********** 35 6 โ”€โ”€ %10 = :(Base.mul_float)(%%2, %%2)::Float64 โ”‚โ•ปโ•ท sin_kernel โ”‚ %11 = :(Base.mul_float)(%10, %10)::Float64 โ”‚โ”‚โ•ป * ``` The three annotations are indicated with `*`. The first one is the line number of the active function (printed once whenever the outer most line number changes). The second is the inlining indicator. The number of lines indicate the level of nesting, with a half-size line (โ•ท) indicating the start of a scope and a full size line (โ”‚) indicating a continuing scope. The last annotation is the most complicated one. It is a heuristic way to print the name of the entered scope. What it attempts to do is print the outermost scope that hasn't been printed before. Let's work a number of examples to see the impacts and tradeoffs involved. ``` f() = leaf_function() # Deliberately not defined to end up in the IR verbatim g() = f() h() = g() top_function() = h() ``` After inlining, we end up with: ``` 1 1 โ”€ %1 = :(Main.leaf_function)()::Any โ”‚โ•ปโ•ทโ•ท h โ””โ”€โ”€ return %1 โ”‚ ``` We see that the only function printed is the outermost function. This certainly loses some information, but the idea is that the outermost function would have the most semantic meaning (in the context of the function we're looking at). On the other hand, let's see what happens when we redefine f: ``` function f() leaf_function() leaf_function() leaf_function() end ``` We get: ``` 1 1 โ”€ :(Main.leaf_function)()::Any โ”‚โ•ปโ•ทโ•ท h โ”‚ :(Main.leaf_function)()::Any โ”‚โ”‚โ”ƒโ”‚ g โ”‚ %3 = :(Main.leaf_function)()::Any โ”‚โ”‚โ”‚โ”ƒ f โ””โ”€โ”€ return %3 โ”‚ ``` Even though we were in the `f` scope since the first statement, it tooks us two statements to catch up and print the intermediate scopes. Which scope is printed is indicated both by the indentation of the method name and by an increased thickness of the appropriate line for the scope. """ function compute_ir_line_annotations(code::IRCode) loc_annotations = String[] loc_methods = String[] loc_lineno = String[] cur_group = 1 last_lineno = 0 last_stack = Int[] last_printed_depth = 0 linetable = code.linetable lines = code.stmts.line last_line = zero(eltype(lines)) for idx in 1:length(lines) buf = IOBuffer() line = lines[idx] print(buf, "โ”‚") depth = compute_inlining_depth(linetable, line) iline = line lineno = 0 loc_method = "" if line != 0 stack = compute_loc_stack(linetable, line) lineno = linetable[stack[1]].line x = min(length(last_stack), length(stack)) if length(stack) != 0 # Compute the last depth that was in common first_mismatch = let last_stack=last_stack findfirst(i->last_stack[i] != stack[i], 1:x) end # If the first mismatch is the last stack frame, that might just # be a line number mismatch in inner most frame. Ignore those if length(last_stack) == length(stack) && first_mismatch == length(stack) last_entry, entry = linetable[last_stack[end]], linetable[stack[end]] if method_name(last_entry) === method_name(entry) && last_entry.file === entry.file first_mismatch = nothing end end last_depth = something(first_mismatch, x+1)-1 if min(depth, last_depth) > last_printed_depth printing_depth = min(depth, last_printed_depth + 1) last_printed_depth = printing_depth elseif length(stack) > length(last_stack) || first_mismatch !== nothing printing_depth = min(depth, last_depth + 1) last_printed_depth = printing_depth else printing_depth = 0 end stole_one = false if printing_depth != 0 for _ in 1:(printing_depth-1) print(buf, "โ”‚") end if printing_depth <= last_depth-1 && first_mismatch === nothing print(buf, "โ”ƒ") for _ in printing_depth+1:min(depth, last_depth) print(buf, "โ”‚") end else stole_one = true print(buf, "โ•ป") end else for _ in 1:min(depth, last_depth) print(buf, "โ”‚") end end print(buf, "โ•ท"^max(0, depth - last_depth - stole_one)) if printing_depth != 0 if length(stack) == printing_depth loc_method = line else loc_method = stack[printing_depth + 1] end loc_method = method_name(linetable[loc_method]) end loc_method = string(" "^printing_depth, loc_method) end last_stack = stack entry = linetable[line] end push!(loc_annotations, String(take!(buf))) push!(loc_lineno, (lineno != 0 && lineno != last_lineno) ? string(lineno) : "") push!(loc_methods, loc_method) last_line = line (lineno != 0) && (last_lineno = lineno) nothing end return (loc_annotations, loc_methods, loc_lineno) end Base.show(io::IO, code::Union{IRCode, IncrementalCompact}) = show_ir(io, code) lineinfo_disabled(io::IO, linestart::String, idx::Int) = "" function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false) context = LineInfoNode[] context_depth = Ref(0) indent(s::String) = s^(max(context_depth[], 1) - 1) function emit_lineinfo_update(io::IO, linestart::String, lineidx::Int32) # internal configuration options: linecolor = :yellow collapse = showtypes ? false : true indent_all = true # convert lineidx to a vector if lineidx == typemin(Int32) # sentinel value: reset internal (and external) state pops = indent("โ””") if !isempty(pops) print(io, linestart) printstyled(io, pops; color=linecolor) println(io) end empty!(context) context_depth[] = 0 elseif lineidx > 0 # just skip over lines with no debug info at all DI = LineInfoNode[] while lineidx != 0 entry = linetable[lineidx]::LineInfoNode push!(DI, entry) lineidx = entry.inlined_at end # FOR DEBUGGING, or if you just like very excessive output: # this prints out the context in full for every statement #empty!(context) #context_depth[] = 0 nframes = length(DI) nctx::Int = 0 pop_skips = 0 # compute the size of the matching prefix in the inlining information stack for i = 1:min(length(context), nframes) CtxLine = context[i] FrameLine = DI[nframes - i + 1] CtxLine === FrameLine || break nctx = i end update_line_only::Bool = false if collapse if nctx > 0 # check if we're adding more frames with the same method name, # if so, drop all existing calls to it from the top of the context # AND check if instead the context was previously printed that way # but now has removed the recursive frames let method = method_name(context[nctx]) # last matching frame if (nctx < nframes && method_name(DI[nframes - nctx]) === method) || (nctx < length(context) && method_name(context[nctx + 1]) === method) update_line_only = true while nctx > 0 && method_name(context[nctx]) === method nctx -= 1 end end end end # look at the first non-matching element to see if we are only changing the line number if !update_line_only && nctx < length(context) && nctx < nframes let CtxLine = context[nctx + 1], FrameLine = DI[nframes - nctx] if method_name(CtxLine) === method_name(FrameLine) update_line_only = true end end end elseif nctx < length(context) && nctx < nframes # look at the first non-matching element to see if we are only changing the line number let CtxLine = context[nctx + 1], FrameLine = DI[nframes - nctx] if CtxLine.file === FrameLine.file && method_name(CtxLine) === method_name(FrameLine) update_line_only = true end end end # examine what frames we're returning from if nctx < length(context) # compute the new inlining depth if collapse npops = 1 let Prev = method_name(context[nctx + 1]) for i = (nctx + 2):length(context) Next = method_name(context[i]) Prev === Next || (npops += 1) Prev = Next end end else npops = length(context) - nctx end resize!(context, nctx) update_line_only && (npops -= 1) if npops > 0 context_depth[] -= npops print(io, linestart) printstyled(io, indent("โ”‚"), "โ””"^npops; color=linecolor) println(io) end end # now print the new frames while nctx < nframes frame::LineInfoNode = DI[nframes - nctx] nctx += 1 started::Bool = false if !update_line_only && showtypes && !isa(frame.method, Symbol) && nctx != 1 print(io, linestart) Base.with_output_color(linecolor, io) do io print(io, indent("โ”‚")) print(io, "โ”Œ invoke ", frame.method) println(io) end started = true end print(io, linestart) Base.with_output_color(linecolor, io) do io print(io, indent("โ”‚")) push!(context, frame) if update_line_only update_line_only = false else context_depth[] += 1 nctx != 1 && print(io, started ? "โ”‚" : "โ”Œ") end print(io, " @ ", frame.file) if frame.line != typemax(frame.line) && frame.line != 0 print(io, ":", frame.line) end print(io, " within `", method_name(frame), "`") if collapse method = method_name(frame) while nctx < nframes frame = DI[nframes - nctx] method_name(frame) === method || break nctx += 1 push!(context, frame) print(io, " @ ", frame.file, ":", frame.line) end end end println(io) end # FOR DEBUGGING `collapse`: # this double-checks the computation of context_depth #let Prev = method_name(context[1]), # depth2 = 1 # for i = 2:nctx # Next = method_name(context[i]) # (collapse && Prev === Next) || (depth2 += 1) # Prev = Next # end # @assert context_depth[] == depth2 #end end indent_all || return "" return sprint(io -> printstyled(io, indent("โ”‚"), color=linecolor), context=io) end return emit_lineinfo_update end """ IRShowConfig - `line_info_preprinter(io::IO, indent::String, idx::Int)`` may print relevant info at the beginning of the line, and should at least print `indent`. It returns a string that will be printed after the final basic-block annotation. - `line_info_postprinter(io::IO; type, used::Bool, show_type::Bool, idx::Int)` prints relevant information like type-annotation at the end of the statement - `should_print_stmt(idx::Int) -> Bool`: whether the statement at index `idx` should be printed as part of the IR or not - `bb_color`: color used for printing the basic block brackets on the left """ struct IRShowConfig line_info_preprinter line_info_postprinter should_print_stmt bb_color::Symbol function IRShowConfig(line_info_preprinter, line_info_postprinter=default_expr_type_printer; should_print_stmt=Returns(true), bb_color::Symbol=:light_black) return new(line_info_preprinter, line_info_postprinter, should_print_stmt, bb_color) end end struct _UNDEF global const UNDEF = _UNDEF.instance end function _stmt(code::IRCode, idx::Int) stmts = code.stmts return isassigned(stmts.inst, idx) ? stmts[idx][:inst] : UNDEF end function _stmt(compact::IncrementalCompact, idx::Int) stmts = compact.result return isassigned(stmts.inst, idx) ? stmts[idx][:inst] : UNDEF end function _stmt(code::CodeInfo, idx::Int) code = code.code return isassigned(code, idx) ? code[idx] : UNDEF end function _type(code::IRCode, idx::Int) stmts = code.stmts return isassigned(stmts.type, idx) ? stmts[idx][:type] : UNDEF end function _type(compact::IncrementalCompact, idx::Int) stmts = compact.result return isassigned(stmts.type, idx) ? stmts[idx][:type] : UNDEF end function _type(code::CodeInfo, idx::Int) types = code.ssavaluetypes types isa Vector{Any} || return nothing return isassigned(types, idx) ? types[idx] : UNDEF end function statement_indices_to_labels(stmt, cfg::CFG) # convert statement index to labels, as expected by print_stmt if stmt isa Expr if stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int)) end elseif isa(stmt, GotoIfNot) stmt = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) elseif stmt isa GotoNode stmt = GotoNode(block_for_inst(cfg, stmt.label)) elseif stmt isa PhiNode e = stmt.edges stmt = PhiNode(Int32[block_for_inst(cfg, Int(e[i])) for i in 1:length(e)], stmt.values) end return stmt end # Show a single statement, code.stmts[idx]/code.code[idx], in the context of the whole IRCode/CodeInfo. # Returns the updated value of bb_idx. # pop_new_node!(idx::Int; attach_after=false) -> (node_idx, new_node_inst, new_node_type) # may return a new node at the current index `idx`, which is printed before the statement # at index `idx`. This function is repeatedly called until it returns `nothing`. # to iterate nodes that are to be inserted after the statement, set `attach_after=true`. function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, config::IRShowConfig, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false) return show_ir_stmt(io, code, idx, config.line_info_preprinter, config.line_info_postprinter, used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color) end function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, line_info_preprinter, line_info_postprinter, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false, bb_color=:light_black) stmt = _stmt(code, idx) type = _type(code, idx) max_bb_idx_size = length(string(length(cfg.blocks))) if isempty(used) maxlength_idx = 0 else maxused = maximum(used) maxlength_idx = length(string(maxused)) end if stmt === UNDEF # This is invalid, but do something useful rather # than erroring, to make debugging easier printstyled(io, "#UNDEF\n", color=:red) return bb_idx end i = 1 function print_indentation(final::Bool=true) # Compute BB guard rail if bb_idx > length(cfg.blocks) # If invariants are violated, print a special leader linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) printstyled(io, "!!! ", "โ”€"^max_bb_idx_size, color=bb_color) else bbrange = cfg.blocks[bb_idx].stmts # Print line info update linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "โ”‚ ", color=bb_color), context=io) linestart *= " "^max_bb_idx_size # idx == 0 means only indentation is printed, so we don't print linfos # multiple times if the are new nodes inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0) if i == 1 && idx == first(bbrange) bb_idx_str = string(bb_idx) bb_pad = max_bb_idx_size - length(bb_idx_str) bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "โ”€" : "โ”„" printstyled(io, bb_idx_str, " ", bb_type, "โ”€"^bb_pad, color=bb_color) elseif final && idx == last(bbrange) # print separator printstyled(io, "โ””", "โ”€"^(1 + max_bb_idx_size), color=bb_color) else printstyled(io, "โ”‚ ", " "^max_bb_idx_size, color=bb_color) end end print(io, inlining_indent, " ") end # first, print new nodes that are to be inserted before the current statement function print_new_node(node; final::Bool=true) print_indentation(final) node_idx, new_node_inst, new_node_type = node @assert new_node_inst !== UNDEF # we filtered these out earlier show_type = should_print_ssa_type(new_node_inst) let maxlength_idx=maxlength_idx, show_type=show_type with_output_color(:green, io) do ioโ€ฒ print_stmt(ioโ€ฒ, node_idx, new_node_inst, used, maxlength_idx, false, show_type) end end if new_node_type === UNDEF # try to be robust against errors printstyled(io, "::#UNDEF", color=:red) else line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx) end println(io) end while (next = pop_new_node!(idx)) !== nothing only_after || print_new_node(next; final=false) i += 1 end # peek at the nodes to be inserted after the current statement # (to determine of the statement itself is the final one) next = pop_new_node!(idx; attach_after=true) # then, print the current statement # FIXME: `only_after` is hack so that we can call this function to print uncompacted # attach-after nodes when the current node has already been compated already if !only_after print_indentation(next===nothing) if code isa CodeInfo stmt = statement_indices_to_labels(stmt, cfg) end show_type = type !== nothing && should_print_ssa_type(stmt) print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) if type !== nothing # ignore types for pre-inference code if type === UNDEF # This is an error, but can happen if passes don't update their type information printstyled(io, "::#UNDEF", color=:red) else line_info_postprinter(io; type, used = idx in used, show_type, idx) end end println(io) end i += 1 # finally, print new nodes that are to be inserted after the current statement while next !== nothing print_new_node(next) i += 1 next = pop_new_node!(idx; attach_after=true) end # increment the basic block counter if bb_idx <= length(cfg.blocks) bbrange = cfg.blocks[bb_idx].stmts if bb_idx <= length(cfg.blocks) && idx == last(bbrange) bb_idx += 1 end end return bb_idx end function _new_nodes_iter(stmts, new_nodes, new_nodes_info, new_nodes_idx) new_nodes_perm = filter(i -> isassigned(new_nodes.inst, i), 1:length(new_nodes)) sort!(new_nodes_perm, by = x -> (x = new_nodes_info[x]; (x.pos, x.attach_after))) # separate iterators for the nodes that are inserted before resp. after each statement before_iter = Ref(1) after_iter = Ref(1) return function get_new_node(idx::Int; attach_after=false) iter = attach_after ? after_iter : before_iter iter[] <= length(new_nodes_perm) || return nothing node_idx = new_nodes_perm[iter[]] # skip nodes while node_idx < new_nodes_idx || # already compacted idx > new_nodes_info[node_idx].pos || # not interested in new_nodes_info[node_idx].attach_after != attach_after iter[] += 1 iter[] > length(new_nodes_perm) && return nothing node_idx = new_nodes_perm[iter[]] end if new_nodes_info[node_idx].pos != idx || new_nodes_info[node_idx].attach_after != attach_after return nothing end iter[] += 1 new_node = new_nodes[node_idx] new_node_inst = isassigned(new_nodes.inst, node_idx) ? new_node[:inst] : UNDEF new_node_type = isassigned(new_nodes.type, node_idx) ? new_node[:type] : UNDEF node_idx += length(stmts) return node_idx, new_node_inst, new_node_type end end function new_nodes_iter(ir::IRCode, new_nodes_idx=1) stmts = ir.stmts new_nodes = ir.new_nodes.stmts new_nodes_info = ir.new_nodes.info return _new_nodes_iter(stmts, new_nodes, new_nodes_info, new_nodes_idx) end function new_nodes_iter(compact::IncrementalCompact) stmts = compact.result new_nodes = compact.new_new_nodes.stmts new_nodes_info = compact.new_new_nodes.info return _new_nodes_iter(stmts, new_nodes, new_nodes_info, 1) end # print only line numbers on the left, some of the method names and nesting depth on the right function inline_linfo_printer(code::IRCode) loc_annotations, loc_methods, loc_lineno = compute_ir_line_annotations(code) max_loc_width = maximum(length, loc_annotations) max_lineno_width = maximum(length, loc_lineno) max_method_width = maximum(length, loc_methods) function (io::IO, indent::String, idx::Int) cols = (displaysize(io)::Tuple{Int,Int})[2] if idx == 0 annotation = "" loc_method = "" lineno = "" elseif idx <= length(loc_annotations) # N.B.: The line array length not matching is invalid, # but let's be robust here annotation = loc_annotations[idx] loc_method = loc_methods[idx] lineno = loc_lineno[idx] else annotation = "!" loc_method = "" lineno = "" end # Print location information right aligned. If the line below is too long, it'll overwrite this, # but that's what we want. if get(io, :color, false)::Bool method_start_column = cols - max_method_width - max_loc_width - 2 filler = " "^(max_loc_width-length(annotation)) printstyled(io, "\e[$(method_start_column)G$(annotation)$(filler)$(loc_method)\e[1G", color = :light_black) end printstyled(io, lineno, " "^(max_lineno_width - length(lineno) + 1); color = :light_black) return "" end end _strip_color(s::String) = replace(s, r"\e\[\d+m"a => "") function statementidx_lineinfo_printer(f, code::IRCode) printer = f(code.linetable) function (io::IO, indent::String, idx::Int) printer(io, indent, idx > 0 ? code.stmts[idx][:line] : typemin(Int32)) end end function statementidx_lineinfo_printer(f, code::CodeInfo) printer = f(code.linetable) function (io::IO, indent::String, idx::Int) printer(io, indent, idx > 0 ? code.codelocs[idx] : typemin(Int32)) end end statementidx_lineinfo_printer(code) = statementidx_lineinfo_printer(DILineInfoPrinter, code) function stmts_used(io::IO, code::IRCode, warn_unset_entry=true) stmts = code.stmts used = BitSet() for stmt in stmts scan_ssa_use!(push!, used, stmt[:inst]) end new_nodes = code.new_nodes.stmts for nn in 1:length(new_nodes) if isassigned(new_nodes.inst, nn) scan_ssa_use!(push!, used, new_nodes[nn][:inst]) elseif warn_unset_entry printstyled(io, "ERROR: New node array has unset entry\n", color=:red) warn_unset_entry = false end end return used end function stmts_used(::IO, code::CodeInfo) stmts = code.code used = BitSet() for stmt in stmts scan_ssa_use!(push!, used, stmt) end return used end function default_config(code::IRCode; verbose_linetable=false) return IRShowConfig(verbose_linetable ? statementidx_lineinfo_printer(code) : inline_linfo_printer(code); bb_color=:normal) end default_config(code::CodeInfo) = IRShowConfig(statementidx_lineinfo_printer(code)) function show_ir_stmts(io::IO, ir::Union{IRCode, CodeInfo, IncrementalCompact}, inds, config::IRShowConfig, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing)) for idx in inds if config.should_print_stmt(ir, idx, used) bb_idx = show_ir_stmt(io, ir, idx, config, used, cfg, bb_idx; pop_new_node!) elseif bb_idx <= length(cfg.blocks) && idx == cfg.blocks[bb_idx].stmts.stop bb_idx += 1 end end return bb_idx end function finish_show_ir(io::IO, cfg::CFG, config::IRShowConfig) max_bb_idx_size = length(string(length(cfg.blocks))) config.line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0) return nothing end function show_ir(io::IO, ir::IRCode, config::IRShowConfig=default_config(ir); pop_new_node! = new_nodes_iter(ir)) used = stmts_used(io, ir) cfg = ir.cfg maxssaid = length(ir.stmts) + Core.Compiler.length(ir.new_nodes) let io = IOContext(io, :maxssaid=>maxssaid) show_ir_stmts(io, ir, 1:length(ir.stmts), config, used, cfg, 1; pop_new_node!) end finish_show_ir(io, cfg, config) end function show_ir(io::IO, ci::CodeInfo, config::IRShowConfig=default_config(ci); pop_new_node! = Returns(nothing)) used = stmts_used(io, ci) cfg = compute_basic_blocks(ci.code) let io = IOContext(io, :maxssaid=>length(ci.code)) show_ir_stmts(io, ci, 1:length(ci.code), config, used, cfg, 1; pop_new_node!) end finish_show_ir(io, cfg, config) end function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=default_config(compact.ir)) cfg = compact.ir.cfg # First print everything that has already been compacted # merge uses in uncompacted region into compacted uses used_compacted = BitSet(i for (i, x) in pairs(compact.used_ssas) if x != 0) used_uncompacted = stmts_used(io, compact.ir) for (i, ssa) = enumerate(compact.ssa_rename) if isa(ssa, SSAValue) && ssa.id in used_uncompacted push!(used_compacted, i) end end # while compacting, the end of the active result bb will not have been determined # (this is done post-hoc by `finish_current_bb!`), so determine it here from scratch. result_bbs = copy(compact.cfg_transform.result_bbs) if compact.active_result_bb <= length(result_bbs) # count the total number of nodes we'll add to this block input_bb_idx = block_for_inst(compact.ir.cfg, compact.idx) input_bb = compact.ir.cfg.blocks[input_bb_idx] count = 0 for input_idx in input_bb.stmts.start:input_bb.stmts.stop pop_new_node! = new_nodes_iter(compact.ir) while pop_new_node!(input_idx) !== nothing count += 1 end while pop_new_node!(input_idx; attach_after=true) !== nothing count += 1 end end still_to_be_inserted = (last(input_bb.stmts) - compact.idx) + count result_bb = result_bbs[compact.active_result_bb] result_bbs[compact.active_result_bb] = Core.Compiler.BasicBlock(result_bb, Core.Compiler.StmtRange(first(result_bb.stmts), compact.result_idx+still_to_be_inserted)) end compact_cfg = CFG(result_bbs, Int[first(result_bbs[i].stmts) for i in 2:length(result_bbs)]) pop_new_node! = new_nodes_iter(compact) maxssaid = length(compact.result) + Core.Compiler.length(compact.new_new_nodes) bb_idx = let io = IOContext(io, :maxssaid=>maxssaid) show_ir_stmts(io, compact, 1:compact.result_idx-1, config, used_compacted, compact_cfg, 1; pop_new_node!) end # Print uncompacted nodes from the original IR # print a separator (_, width) = displaysize(io) stmts = compact.ir.stmts indent = length(string(length(stmts))) # config.line_info_preprinter(io, "", compact.idx) printstyled(io, "โ”€"^(width-indent-1), '\n', color=:red) # while compacting, the start of the active uncompacted bb will have been overwritten. # this manifests as a stmt range end that is less than the start, so correct that. inputs_bbs = copy(cfg.blocks) for (i, bb) in enumerate(inputs_bbs) if bb.stmts.stop < bb.stmts.start inputs_bbs[i] = Core.Compiler.BasicBlock(bb, Core.Compiler.StmtRange(last(bb.stmts), last(bb.stmts))) # this is not entirely correct, and will result in the bb starting again, # but is the best we can do without changing how `finish_current_bb!` works. end end uncompacted_cfg = CFG(inputs_bbs, Int[first(inputs_bbs[i].stmts) for i in 2:length(inputs_bbs)]) pop_new_node! = new_nodes_iter(compact.ir, compact.new_nodes_idx) maxssaid = length(compact.ir.stmts) + Core.Compiler.length(compact.ir.new_nodes) let io = IOContext(io, :maxssaid=>maxssaid) # first show any new nodes to be attached after the last compacted statement if compact.idx > 1 show_ir_stmt(io, compact.ir, compact.idx-1, config, used_uncompacted, uncompacted_cfg, bb_idx; pop_new_node!, only_after=true) end # then show the actual uncompacted IR show_ir_stmts(io, compact.ir, compact.idx:length(stmts), config, used_uncompacted, uncompacted_cfg, bb_idx; pop_new_node!) end finish_show_ir(io, uncompacted_cfg, config) end function effectbits_letter(effects::Effects, name::Symbol, suffix::Char) ft = fieldtype(Effects, name) if ft === UInt8 prefix = getfield(effects, name) === ALWAYS_TRUE ? '+' : getfield(effects, name) === ALWAYS_FALSE ? '!' : '?' elseif ft === Bool prefix = getfield(effects, name) ? '+' : '!' else error("unsupported effectbits type given") end return string(prefix, suffix) end function effectbits_color(effects::Effects, name::Symbol) ft = fieldtype(Effects, name) if ft === UInt8 color = getfield(effects, name) === ALWAYS_TRUE ? :green : getfield(effects, name) === ALWAYS_FALSE ? :red : :yellow elseif ft === Bool color = getfield(effects, name) ? :green : :red else error("unsupported effectbits type given") end return color end function Base.show(io::IO, e::Effects) print(io, "(") printstyled(io, effectbits_letter(e, :consistent, 'c'); color=effectbits_color(e, :consistent)) print(io, ',') printstyled(io, effectbits_letter(e, :effect_free, 'e'); color=effectbits_color(e, :effect_free)) print(io, ',') printstyled(io, effectbits_letter(e, :nothrow, 'n'); color=effectbits_color(e, :nothrow)) print(io, ',') printstyled(io, effectbits_letter(e, :terminates, 't'); color=effectbits_color(e, :terminates)) print(io, ',') printstyled(io, effectbits_letter(e, :notaskstate, 's'); color=effectbits_color(e, :notaskstate)) print(io, ',') printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly)) print(io, ',') printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds)) print(io, ')') e.nonoverlayed || printstyled(io, 'โ€ฒ'; color=:red) end @specialize P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/arrayshow.jlB^# This file is a part of Julia. License is MIT: https://julialang.org/license # methods related to array printing # Printing a value requires to take into account the :typeinfo property # from the IO context; this property encodes (as a type) the type information # that is supposed to have already been displayed concerning this value, # so that redundancy can be avoided. For example, when printing an array of # `Float16` values, the header "Float16" will be printed, and the values # can simply be printed with the decimal representations: # show(Float16(1)) -> "Float16(1.0)" # show([Float16(1)]) -> "Float16[1.0]" (instead of "Float16[Float16(1.0)]") # Similarly: # show([[Float16(1)]]) -> "Array{Float16}[[1.0]]" (instead of "Array{Float16}[Float16[1.0]]") # # The array printing methods here can be grouped into two categories (and are annotated as such): # 1) "typeinfo aware" : these are "API boundaries" functions, which will read the typeinfo # property from the context, and pass down to their value an updated property # according to its eltype; at each layer of nesting, only one "typeinfo aware" # function must be called; # 2) "typeinfo agnostic": these are helper functions used by the first category; hence # they don't manipulate the typeinfo property, and let the printing routines # for their elements read directly the property set by their callers # # Non-annotated functions are even lower level (e.g. print_matrix_row), so they fall # by default into category 2. # # The basic organization of this file is # 1) printing with `display` # 2) printing with `show` # 3) Logic for displaying type information ## printing with `display` """ Unexported convenience function used in body of `replace_in_print_matrix` methods. By default returns a string of the same width as original with a centered cdot, used in printing of structural zeros of structured matrices. Accept keyword args `c` for alternate single character marker. """ function replace_with_centered_mark(s::AbstractString;c::AbstractChar = 'โ‹…') N = textwidth(ANSIIterator(s)) return join(setindex!([" " for i=1:N],string(c),ceil(Int,N/2))) end const undef_ref_alignment = (3,3) """ `alignment(io, X, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the alignment for specified parts of array `X`, returning the (left,right) info. It will look in X's `rows`, `cols` (both lists of indices) and figure out what's needed to be fully aligned, for example looking all the way down a column and finding out the maximum size of each element. Parameter `sep::Integer` is number of spaces to put between elements. `cols_if_complete` and `cols_otherwise` indicate screen width to use. Alignment is reported as a vector of (left,right) tuples, one for each column going across the screen. """ function alignment(io::IO, @nospecialize(X::AbstractVecOrMat), rows::AbstractVector{T}, cols::AbstractVector{V}, cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer, #= `size(X) may not infer, set this in caller =# ncols::Integer=size(X, 2)) where {T,V} a = Tuple{T, V}[] for j in cols # need to go down each column one at a time l = r = 0 for i in rows # plumb down and see what largest element sizes are if isassigned(X,i,j) aij = alignment(io, X[i,j])::Tuple{Int,Int} else aij = undef_ref_alignment end l = max(l, aij[1]) # left characters r = max(r, aij[2]) # right characters end push!(a, (l, r)) # one tuple per column of X, pruned to screen width if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete pop!(a) # remove this latest tuple if we're already beyond screen width break end end if 1 < length(a) < ncols while sum(map(sum,a)) + sep*length(a) >= cols_otherwise pop!(a) end end return a end """ `print_matrix_row(io, X, A, i, cols, sep)` produces the aligned output for a single matrix row X[i, cols] where the desired list of columns is given. The corresponding alignment A is used, and the separation between elements is specified as string sep. `print_matrix_row` will also respect compact output for elements. """ function print_matrix_row(io::IO, @nospecialize(X::AbstractVecOrMat), A::Vector, i::Integer, cols::AbstractVector, sep::AbstractString, #= `axes(X)` may not infer, set this in caller =# idxlast::Integer=last(axes(X, 2))) for (k, j) = enumerate(cols) k > length(A) && break if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices x = X[i,j] a = alignment(io, x)::Tuple{Int,Int} # First try 3-arg show sx = sprint(show, "text/plain", x, context=io, sizehint=0) # If the output contains line breaks, try 2-arg show instead. if occursin('\n', sx) sx = sprint(show, x, context=io, sizehint=0) end else a = undef_ref_alignment sx = undef_ref_str end l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed r = j == idxlast ? "" : repeat(" ", A[k][2]-a[2]) prettysx = replace_in_print_matrix(X,i,j,sx) print(io, l, prettysx, r) if k < length(A); print(io, sep); end end end """ `print_matrix_vdots` is used to show a series of vertical ellipsis instead of a bunch of rows for long matrices. Not only is the string vdots shown but it also repeated every M elements if desired. """ function print_matrix_vdots(io::IO, vdots::AbstractString, A::Vector, sep::AbstractString, M::Integer, m::Integer, pad_right::Bool = true) for k = 1:length(A) w = A[k][1] + A[k][2] if k % M == m l = repeat(" ", max(0, A[k][1]-length(vdots))) r = k == length(A) && !pad_right ? "" : repeat(" ", max(0, w-length(vdots)-length(l))) print(io, l, vdots, r) else (k != length(A) || pad_right) && print(io, repeat(" ", w)) end if k < length(A); print(io, sep); end end end # typeinfo agnostic """ print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) Prints a matrix with limited output size. If `io` sets `:limit` to true, then only the corners of the matrix are printed, separated with vertical, horizontal, and diagonal ellipses as appropriate. Optional arguments are string pre (printed before the matrix, e.g. an opening bracket) which will cause a corresponding same-size indent on following rows, and string post (printed at the end of the last row of the matrix). Also options to use different ellipsis characters hdots, vdots, ddots. These are repeated every hmod or vmod elements. """ function print_matrix(io::IO, X::AbstractVecOrMat, pre::AbstractString = " ", # pre-matrix string sep::AbstractString = " ", # separator between elements post::AbstractString = "", # post-matrix string hdots::AbstractString = " \u2026 ", vdots::AbstractString = "\u22ee", ddots::AbstractString = " \u22f1 ", hmod::Integer = 5, vmod::Integer = 5) _print_matrix(io, inferencebarrier(X), pre, sep, post, hdots, vdots, ddots, hmod, vmod, unitrange(axes(X,1)), unitrange(axes(X,2))) end function _print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, hdots, vdots, ddots, hmod, vmod, rowsA, colsA) hmod, vmod = Int(hmod)::Int, Int(vmod)::Int ncols, idxlast = length(colsA), last(colsA) if !(get(io, :limit, false)::Bool) screenheight = screenwidth = typemax(Int) else sz = displaysize(io)::Tuple{Int,Int} screenheight, screenwidth = sz[1] - 4, sz[2] end screenwidth -= length(pre)::Int + length(post)::Int presp = repeat(" ", length(pre)::Int) # indent each row to match pre string postsp = "" @assert textwidth(hdots) == textwidth(ddots) sepsize = length(sep)::Int m, n = length(rowsA), length(colsA) # To figure out alignments, only need to look at as many rows as could # fit down screen. If screen has at least as many rows as A, look at A. # If not, then we only need to look at the first and last chunks of A, # each half a screen height in size. halfheight = div(screenheight,2) if m > screenheight rowsA = [rowsA[(0:halfheight-1) .+ firstindex(rowsA)]; rowsA[(end-div(screenheight-1,2)+1):end]] else rowsA = [rowsA;] end # Similarly for columns, only necessary to get alignments for as many # columns as could conceivably fit across the screen maxpossiblecols = div(screenwidth, 1+sepsize) if n > maxpossiblecols colsA = [colsA[(0:maxpossiblecols-1) .+ firstindex(colsA)]; colsA[(end-maxpossiblecols+1):end]] else colsA = [colsA;] end A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize, ncols) # Nine-slicing is accomplished using print_matrix_row repeatedly if m <= screenheight # rows fit vertically on screen if n <= length(A) # rows and cols fit so just print whole matrix in one piece for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,A,i,colsA,sep,idxlast) print(io, i == last(rowsA) ? post : postsp) if i != last(rowsA); println(io); end end else # rows fit down screen but cols don't, so need horizontal ellipsis c = div(screenwidth-length(hdots)::Int+1,2)+1 # what goes to right of ellipsis Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize, ncols)) # alignments for right c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)::Int Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize, ncols) # alignments for left of ellipsis for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep,idxlast) print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots)::Int)) print_matrix_row(io, X, Ralign, i, (n - length(Ralign)) .+ colsA, sep, idxlast) print(io, i == last(rowsA) ? post : postsp) if i != last(rowsA); println(io); end end end else # rows don't fit so will need vertical ellipsis if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,A,i,colsA,sep,idxlast) print(io, i == last(rowsA) ? post : postsp) if i != rowsA[end] || i == rowsA[halfheight]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots, A, sep, vmod, 1, false) print(io, i == last(rowsA) ? post : postsp * '\n') end end else # neither rows nor cols fit, so use all 3 kinds of dots c = div(screenwidth-length(hdots)::Int+1,2)+1 Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize, ncols)) c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots)::Int Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize, ncols) r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half for i in rowsA print(io, i == first(rowsA) ? pre : presp) print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep,idxlast) print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots)::Int)) print_matrix_row(io, X,Ralign,i,(n-length(Ralign)).+colsA,sep,idxlast) print(io, i == last(rowsA) ? post : postsp) if i != rowsA[end] || i == rowsA[halfheight]; println(io); end if i == rowsA[halfheight] print(io, i == first(rowsA) ? pre : presp) print_matrix_vdots(io, vdots, Lalign, sep, vmod, 1, true) print(io, ddots) print_matrix_vdots(io, vdots, Ralign, sep, vmod, r, false) print(io, i == last(rowsA) ? post : postsp * '\n') end end end if isempty(rowsA) print(io, pre) print(io, vdots) length(colsA) > 1 && print(io, " ", ddots) print(io, post) end end end # typeinfo agnostic # n-dimensional arrays show_nd(io::IO, a::AbstractArray, print_matrix::Function, show_full::Bool) = _show_nd(io, inferencebarrier(a), print_matrix, show_full, map(unitrange, axes(a))) function _show_nd(io::IO, @nospecialize(a::AbstractArray), print_matrix::Function, show_full::Bool, axs::Tuple{Vararg{AbstractUnitRange}}) limit = get(io, :limit, false)::Bool if isempty(a) return end tailinds = tail(tail(axs)) nd = ndims(a)-2 show_full || print(io, "[") Is = CartesianIndices(tailinds) lastidxs = first(Is).I reached_last_d = false for I in Is idxs = I.I if limit for i = 1:nd ii = idxs[i] ind = tailinds[i] if length(ind) > 10 if ii == ind[firstindex(ind)+3] && all(d->idxs[d]==first(tailinds[d]),1:i-1) for j=i+1:nd szj = length(axs[j+2]) indj = tailinds[j] if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3 @goto skip end end print(io, ";"^(i+2)) print(io, " \u2026 ") show_full && print(io, "\n\n") @goto skip end if ind[firstindex(ind)+2] < ii <= ind[end-3] @goto skip end end end end if show_full _show_nd_label(io, a, idxs) end slice = view(a, axs[1], axs[2], idxs...) if show_full print_matrix(io, slice) print(io, idxs == map(last,tailinds) ? "" : "\n\n") else idxdiff = lastidxs .- idxs .< 0 if any(idxdiff) lastchangeindex = 2 + findlast(idxdiff) print(io, ";"^lastchangeindex) lastchangeindex == ndims(a) && (reached_last_d = true) print(io, " ") end print_matrix(io, slice) end @label skip lastidxs = idxs end if !show_full reached_last_d || print(io, ";"^(nd+2)) print(io, "]") end end function _show_nd_label(io::IO, a::AbstractArray, idxs) print(io, "[:, :, ") for i = 1:length(idxs)-1 print(io, idxs[i], ", ") end println(io, idxs[end], "] =") end # print_array: main helper functions for show(io, text/plain, array) # typeinfo agnostic # Note that this is for showing the content inside the array, and for `MIME"text/plain". # There are `show(::IO, ::A) where A<:AbstractArray` methods that don't use this # e.g. show_vector, show_zero_dim print_array(io::IO, X::AbstractArray{<:Any, 0}) = isassigned(X) ? show(io, X[]) : print(io, undef_ref_str) print_array(io::IO, X::AbstractVecOrMat) = print_matrix(io, X) print_array(io::IO, X::AbstractArray) = show_nd(io, X, print_matrix, true) # typeinfo aware # implements: show(io::IO, ::MIME"text/plain", X::AbstractArray) function show(io::IO, ::MIME"text/plain", X::AbstractArray) if isempty(X) && (get(io, :compact, false)::Bool || X isa Vector) return show(io, X) end # 0) show summary before setting :compact summary(io, X) isempty(X) && return print(io, ":") show_circular(io, X) && return # 1) compute new IOContext if !haskey(io, :compact) && length(axes(X, 2)) > 1 io = IOContext(io, :compact => true) end if get(io, :limit, false)::Bool && eltype(X) === Method # override usual show method for Vector{Method}: don't abbreviate long lists io = IOContext(io, :limit => false) end if get(io, :limit, false)::Bool && displaysize(io)[1]-4 <= 0 return print(io, " โ€ฆ") else println(io) end # 2) update typeinfo # # it must come after printing the summary, which can exploit :typeinfo itself # (e.g. views) # we assume this function is always called from top-level, i.e. that it's not nested # within another "show" method; hence we always print the summary, without # checking for current :typeinfo (this could be changed in the future) io = IOContext(io, :typeinfo => eltype(X)) # 2) show actual content recur_io = IOContext(io, :SHOWN_SET => X) print_array(recur_io, X) end ## printing with `show` ### non-Vector arrays # _show_nonempty & _show_empty: main helper functions for show(io, X) # typeinfo agnostic """ `_show_nonempty(io, X::AbstractMatrix, prefix)` prints matrix X with opening and closing square brackets, preceded by `prefix`, supposed to encode the type of the elements. """ _show_nonempty(io::IO, X::AbstractMatrix, prefix::String) = _show_nonempty(io, inferencebarrier(X), prefix, false, axes(X)) function _show_nonempty(io::IO, @nospecialize(X::AbstractMatrix), prefix::String, drop_brackets::Bool, axs::Tuple{AbstractUnitRange,AbstractUnitRange}) @assert !isempty(X) limit = get(io, :limit, false)::Bool indr, indc = axs nr, nc = length(indr), length(indc) rdots, cdots = false, false rr1, rr2 = unitrange(indr), 1:0 cr1 = unitrange(indc) cr2 = first(cr1) .+ (0:-1) if limit if nr > 4 rr1, rr2 = rr1[1:2], rr1[nr-1:nr] rdots = true end if nc > 4 cr1, cr2 = cr1[1:2], cr1[nc-1:nc] cdots = true end end drop_brackets || print(io, prefix, "[") for rr in (rr1, rr2) for i in rr for cr in (cr1, cr2) for j in cr j > first(cr) && print(io, " ") if !isassigned(X,i,j) print(io, undef_ref_str) else el = X[i,j] show(io, el) end end if last(cr) == last(indc) i < last(indr) && print(io, "; ") elseif cdots print(io, " \u2026 ") end end end last(rr) != last(indr) && rdots && print(io, "\u2026 ; ") end if !drop_brackets nc > 1 || print(io, ";;") print(io, "]") end return nothing end function _show_nonempty(io::IO, X::AbstractArray, prefix::String) print(io, prefix) show_nd(io, X, (io, slice) -> _show_nonempty(io, inferencebarrier(slice), prefix, true, axes(slice)), false) end # a specific call path is used to show vectors (show_vector) _show_nonempty(::IO, ::AbstractVector, ::String) = error("_show_nonempty(::IO, ::AbstractVector, ::String) is not implemented") _show_nonempty(io::IO, X::AbstractArray{T,0} where T, prefix::String) = print_array(io, X) # NOTE: it's not clear how this method could use the :typeinfo attribute function _show_empty(io::IO, X::Array) show(io, typeof(X)) print(io, "(undef, ", join(size(X),", "), ')') end _show_empty(io, X::AbstractArray) = summary(io, X) # typeinfo aware (necessarily) function show(io::IO, X::AbstractArray) ndims(X) == 0 && return show_zero_dim(io, X) ndims(X) == 1 && return show_vector(io, X) prefix, implicit = typeinfo_prefix(io, X) if !implicit io = IOContext(io, :typeinfo => eltype(X)) end isempty(X) ? _show_empty(io, X) : _show_nonempty(io, X, prefix) end ### 0-dimensional arrays (#31481) show_zero_dim(io::IO, X::BitArray{0}) = print(io, "BitArray(", Int(X[]), ")") function show_zero_dim(io::IO, X::AbstractArray{T, 0}) where T if isassigned(X) print(io, "fill(") show(io, X[]) else print(io, "Array{", T, ", 0}(") show(io, undef) end print(io, ")") end ### Vector arrays # typeinfo aware # NOTE: v is not constrained to be a vector, as this function can work with iterables # in general (it's used e.g. by show(::IO, ::Set)) function show_vector(io::IO, v, opn='[', cls=']') prefix, implicit = typeinfo_prefix(io, v) print(io, prefix) # directly or indirectly, the context now knows about eltype(v) if !implicit io = IOContext(io, :typeinfo => eltype(v)) end limited = get(io, :limit, false)::Bool if limited && length(v) > 20 axs1 = axes1(v) f, l = first(axs1), last(axs1) show_delim_array(io, v, opn, ",", "", false, f, f+9) print(io, " โ€ฆ ") show_delim_array(io, v, "", ",", cls, false, l-9, l) else show_delim_array(io, v, opn, ",", cls, false) end end ## Logic for displaying type information # given type `typeinfo` extracted from context, assuming a collection # is being displayed, deduce the elements type; in spirit this is # similar to `eltype` (except that we don't want a default fall-back # returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`, # because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`) typeinfo_eltype(typeinfo) = nothing # element type not precisely known typeinfo_eltype(typeinfo::Type{Union{}}, slurp...) = nothing typeinfo_eltype(typeinfo::Type{<:AbstractArray{T}}) where {T} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractDict{K,V}}) where {K,V} = eltype(typeinfo) typeinfo_eltype(typeinfo::Type{<:AbstractSet{T}}) where {T} = eltype(typeinfo) # types that can be parsed back accurately from their un-decorated representations function typeinfo_implicit(@nospecialize(T)) if T === Float64 || T === Int || T === Char || T === String || T === Symbol || issingletontype(T) return true end return isconcretetype(T) && ((T <: Array && typeinfo_implicit(eltype(T))) || ((T <: Tuple || T <: Pair) && all(typeinfo_implicit, fieldtypes(T))) || (T <: AbstractDict && typeinfo_implicit(keytype(T)) && typeinfo_implicit(valtype(T)))) end # X not constrained, can be any iterable (cf. show_vector) function typeinfo_prefix(io::IO, X) typeinfo = get(io, :typeinfo, Any)::Type if !(X isa typeinfo) typeinfo = Any end # what the context already knows about the eltype of X: eltype_ctx = typeinfo_eltype(typeinfo) eltype_X = eltype(X) if X isa AbstractDict if eltype_X == eltype_ctx sprint(show_type_name, typeof(X).name; context=io), false elseif !isempty(X) && typeinfo_implicit(keytype(X)) && typeinfo_implicit(valtype(X)) sprint(show_type_name, typeof(X).name; context=io), true else sprint(print, typeof(X); context=io), false end else # Types hard-coded here are those which are created by default for a given syntax if eltype_X == eltype_ctx "", false elseif !isempty(X) && typeinfo_implicit(eltype_X) "", true elseif print_without_params(eltype_X) sprint(show_type_name, unwrap_unionall(eltype_X).name; context=io), false # Print "Array" rather than "Array{T,N}" else sprint(print, eltype_X; context=io), false end end end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/methodshow.jl้?# This file is a part of Julia. License is MIT: https://julialang.org/license # Method and method table pretty-printing const empty_sym = Symbol("") function strip_gensym(sym) if sym === :var"#self#" || sym === :var"#unused#" return empty_sym end return Symbol(replace(String(sym), r"^(.*)#(.*#)?\d+$"sa => s"\1")) end function argtype_decl(env, n, @nospecialize(sig::DataType), i::Int, nargs, isva::Bool) # -> (argname, argtype) t = unwrapva(sig.parameters[min(i, end)]) if i == nargs && isva va = sig.parameters[end] if isvarargtype(va) && (!isdefined(va, :N) || !isa(va.N, Int)) t = va else ntotal = length(sig.parameters) isvarargtype(va) && (ntotal += va.N - 1) t = Vararg{t,ntotal-nargs+1} end end if isa(n,Expr) n = n.args[1] # handle n::T in arg list end n = strip_gensym(n) local s if n === empty_sym s = "" else s = sprint(show_sym, n) t === Any && return s, "" end if isvarargtype(t) if !isdefined(t, :N) if unwrapva(t) === Any return string(s, "..."), "" else return s, string_with_env(env, unwrapva(t)) * "..." end end return s, string_with_env(env, "Vararg{", t.T, ", ", t.N, "}") end return s, string_with_env(env, t) end function method_argnames(m::Method) argnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), m.slot_syms) isempty(argnames) && return argnames return argnames[1:m.nargs] end function arg_decl_parts(m::Method, html=false) tv = Any[] sig = m.sig while isa(sig, UnionAll) push!(tv, sig.var) sig = sig.body end file, line = updated_methodloc(m) argnames = method_argnames(m) if length(argnames) >= m.nargs show_env = ImmutableDict{Symbol, Any}() for t in tv show_env = ImmutableDict(show_env, :unionall_env => t) end decls = Tuple{String,String}[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva) for i = 1:m.nargs] decls[1] = ("", sprint(show_signature_function, unwrapva(sig.parameters[1]), false, decls[1][1], html, context = show_env)) else decls = Tuple{String,String}[("", "") for i = 1:length(sig.parameters::SimpleVector)] end return tv, decls, file, line end # NOTE: second argument is deprecated and is no longer used function kwarg_decl(m::Method, kwtype = nothing) if m.sig !== Tuple # OpaqueClosure or Builtin kwtype = typeof(Core.kwcall) sig = rewrap_unionall(Tuple{kwtype, NamedTuple, (unwrap_unionall(m.sig)::DataType).parameters...}, m.sig) kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter()) if kwli !== nothing kwli = kwli::Method slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), kwli.slot_syms) kws = filter(x -> !(x === empty_sym || '#' in string(x)), slotnames[(kwli.nargs + 1):end]) # ensure the kwarg... is always printed last. The order of the arguments are not # necessarily the same as defined in the function i = findfirst(x -> endswith(string(x)::String, "..."), kws) if i !== nothing push!(kws, kws[i]) deleteat!(kws, i) end isempty(kws) && push!(kws, :var"...") return kws end end return Symbol[] end function show_method_params(io::IO, tv) if !isempty(tv) print(io, " where ") if length(tv) == 1 show(io, tv[1]) else print(io, "{") for i = 1:length(tv) if i > 1 print(io, ", ") end x = tv[i] show(io, x) io = IOContext(io, :unionall_env => x) end print(io, "}") end end end # In case the line numbers in the source code have changed since the code was compiled, # allow packages to set a callback function that corrects them. # (Used by Revise and perhaps other packages.) # Any function `f` stored here must be consistent with the signature # f(m::Method)::Tuple{Union{Symbol,String}, Union{Int32,Int64}} const methodloc_callback = Ref{Union{Function, Nothing}}(nothing) function fixup_stdlib_path(path::String) # The file defining Base.Sys gets included after this file is included so make sure # this function is valid even in this intermediary state if isdefined(@__MODULE__, :Sys) BUILD_STDLIB_PATH = Sys.BUILD_STDLIB_PATH::String STDLIB = Sys.STDLIB::String if BUILD_STDLIB_PATH != STDLIB # BUILD_STDLIB_PATH gets defined in sysinfo.jl npath = normpath(path) npathโ€ฒ = replace(npath, normpath(BUILD_STDLIB_PATH) => normpath(STDLIB)) return npath == npathโ€ฒ ? path : npathโ€ฒ end end return path end # This function does the method location updating function updated_methodloc(m::Method)::Tuple{String, Int32} file, line = m.file, m.line if methodloc_callback[] !== nothing try file, line = invokelatest(methodloc_callback[], m)::Tuple{Union{Symbol,String}, Union{Int32,Int64}} catch end end file = fixup_stdlib_path(string(file)) return file, Int32(line) end functionloc(m::Core.MethodInstance) = functionloc(m.def) """ functionloc(m::Method) Return a tuple `(filename,line)` giving the location of a `Method` definition. """ function functionloc(m::Method) file, ln = updated_methodloc(m) if ln <= 0 error("could not determine location of method definition") end return (find_source_file(string(file)), ln) end """ functionloc(f::Function, types) Return a tuple `(filename,line)` giving the location of a generic `Function` definition. """ functionloc(@nospecialize(f), @nospecialize(types)) = functionloc(which(f,types)) function functionloc(@nospecialize(f)) mt = methods(f) if isempty(mt) if isa(f, Function) error("function has no definitions") else error("object is not callable") end end if length(mt) > 1 error("function has multiple methods; please specify a type signature") end return functionloc(first(mt)) end function sym_to_string(sym) if sym === :var"..." return "..." end s = String(sym) if endswith(s, "...") return string(sprint(show_sym, Symbol(s[1:end-3])), "...") else return sprint(show_sym, sym) end end # default compact view show(io::IO, m::Method; kwargs...) = show_method(IOContext(io, :compact=>true), m; kwargs...) show(io::IO, ::MIME"text/plain", m::Method; kwargs...) = show_method(io, m; kwargs...) function show_method(io::IO, m::Method; modulecolor = :light_black, digit_align_width = 1) tv, decls, file, line = arg_decl_parts(m) sig = unwrap_unionall(m.sig) if sig === Tuple # Builtin print(io, m.name, "(...)") file = "none" line = 0 else print(io, decls[1][2], "(") # arguments for (i,d) in enumerate(decls[2:end]) printstyled(io, d[1], color=:light_black) if !isempty(d[2]) print(io, "::") print_type_bicolor(io, d[2], color=:bold, inner_color=:normal) end i < length(decls)-1 && print(io, ", ") end kwargs = kwarg_decl(m) if !isempty(kwargs) print(io, "; ") for kw in kwargs skw = sym_to_string(kw) print(io, skw) if kw != last(kwargs) print(io, ", ") end end end print(io, ")") show_method_params(io, tv) end if !(get(io, :compact, false)::Bool) # single-line mode println(io) digit_align_width += 4 end # module & file, re-using function from errorshow.jl print_module_path_file(io, parentmodule(m), string(file), line; modulecolor, digit_align_width) end function show_method_list_header(io::IO, ms::MethodList, namefmt::Function) mt = ms.mt name = mt.name hasname = isdefined(mt.module, name) && typeof(getfield(mt.module, name)) <: Function n = length(ms) m = n==1 ? "method" : "methods" print(io, "# $n $m") sname = string(name) namedisplay = namefmt(sname) if hasname what = (startswith(sname, '@') ? "macro" : mt.module === Core && mt.defs isa Core.TypeMapEntry && (mt.defs.func::Method).sig === Tuple ? "builtin function" : # else "generic function") print(io, " for ", what, " ", namedisplay, " from ") col = get!(() -> popfirst!(STACKTRACE_MODULECOLORS), STACKTRACE_FIXEDCOLORS, parentmodule_before_main(ms.mt.module)) printstyled(io, ms.mt.module, color=col) elseif '#' in sname print(io, " for anonymous function ", namedisplay) elseif mt === _TYPE_NAME.mt print(io, " for type constructor") else print(io, " for callable object") end !iszero(n) && print(io, ":") end # Determine the `modulecolor` value to pass to `show_method` function _modulecolor(method::Method) mmt = get_methodtable(method) if mmt === nothing || mmt.module === parentmodule(method) return nothing end # `mmt` is only particularly relevant for external method tables. Since the primary # method table is shared, we now need to distinguish "primary" methods by trying to # check if there is a primary `DataType` to identify it with. c.f. how `jl_method_def` # would derive this same information (for the name). ft = argument_datatype((unwrap_unionall(method.sig)::DataType).parameters[1]) # `ft` should be the type associated with the first argument in the method signature. # If it's `Type`, try to unwrap it again. if isType(ft) ft = argument_datatype(ft.parameters[1]) end if ft === nothing || parentmodule(method) === parentmodule(ft) !== Core return nothing end m = parentmodule_before_main(method) return get!(() -> popfirst!(STACKTRACE_MODULECOLORS), STACKTRACE_FIXEDCOLORS, m) end function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=true) mt = ms.mt name = mt.name hasname = isdefined(mt.module, name) && typeof(getfield(mt.module, name)) <: Function if header show_method_list_header(io, ms, str -> "\""*str*"\"") end n = rest = 0 local last last_shown_line_infos = get(io, :last_shown_line_infos, nothing) last_shown_line_infos === nothing || empty!(last_shown_line_infos) digit_align_width = length(string(max > 0 ? max : length(ms))) for meth in ms if max == -1 || n < max n += 1 println(io) print(io, " ", lpad("[$n]", digit_align_width + 2), " ") show_method(io, meth; modulecolor=_modulecolor(meth)) file, line = updated_methodloc(meth) if last_shown_line_infos !== nothing push!(last_shown_line_infos, (string(file), line)) end else rest += 1 last = meth end end if rest > 0 println(io) if rest == 1 show_method(io, last) else print(io, "... $rest methods not shown") if hasname print(io, " (use methods($name) to see them all)") end end end nothing end show(io::IO, ms::MethodList) = show_method_table(io, ms) show(io::IO, ::MIME"text/plain", ms::MethodList) = show_method_table(io, ms) show(io::IO, mt::Core.MethodTable) = show_method_table(io, MethodList(mt)) function inbase(m::Module) if m == Base true else parent = parentmodule(m) parent === m ? false : inbase(parent) end end fileurl(file) = let f = find_source_file(file); f === nothing ? "" : "file://"*f; end function url(m::Method) M = parentmodule(m) (m.file === :null || m.file === :string) && return "" file = string(m.file) line = m.line line <= 0 || occursin(r"In\[[0-9]+\]"a, file) && return "" Sys.iswindows() && (file = replace(file, '\\' => '/')) libgit2_id = PkgId(UUID((0x76f85450_5226_5b5a,0x8eaa_529ad045b433)), "LibGit2") if inbase(M) if isempty(Base.GIT_VERSION_INFO.commit) # this url will only work if we're on a tagged release return "https://github.com/JuliaLang/julia/tree/v$VERSION/base/$file#L$line" else return "https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/$file#L$line" end elseif root_module_exists(libgit2_id) LibGit2 = root_module(libgit2_id) try d = dirname(file) return LibGit2.with(LibGit2.GitRepoExt(d)) do repo LibGit2.with(LibGit2.GitConfig(repo)) do cfg u = LibGit2.get(cfg, "remote.origin.url", "") u = (match(LibGit2.GITHUB_REGEX,u)::AbstractMatch).captures[1] commit = string(LibGit2.head_oid(repo)) root = LibGit2.path(repo) if startswith(file, root) || startswith(realpath(file), root) "https://github.com/$u/tree/$commit/"*file[length(root)+1:end]*"#L$line" else fileurl(file) end end end catch return fileurl(file) end else return fileurl(file) end end function show(io::IO, ::MIME"text/html", m::Method) tv, decls, file, line = arg_decl_parts(m, true) sig = unwrap_unionall(m.sig) if sig === Tuple # Builtin print(io, m.name, "(...) in ", parentmodule(m)) return end print(io, decls[1][2], "(") join( io, String[ isempty(d[2]) ? string(d[1]) : string(d[1], "::", d[2] , "") for d in decls[2:end] ], ", ", ", ", ) kwargs = kwarg_decl(m) if !isempty(kwargs) print(io, "; ") join(io, map(sym_to_string, kwargs), ", ", ", ") print(io, "") end print(io, ")") if !isempty(tv) print(io,"") show_method_params(io, tv) print(io,"") end print(io, " in ", parentmodule(m)) if line > 0 file, line = updated_methodloc(m) u = url(m) if isempty(u) print(io, " at ", file, ":", line) else print(io, """ at """, file, ":", line, "") end end end function show(io::IO, mime::MIME"text/html", ms::MethodList) mt = ms.mt show_method_list_header(io, ms, str -> ""*str*"") print(io, "
    ") for meth in ms print(io, "
  • ") show(io, mime, meth) print(io, "
  • ") end print(io, "
") end show(io::IO, mime::MIME"text/html", mt::Core.MethodTable) = show(io, mime, MethodList(mt)) # pretty-printing of AbstractVector{Method} function show(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) last_shown_line_infos = get(io, :last_shown_line_infos, nothing) last_shown_line_infos === nothing || empty!(last_shown_line_infos) first = true for (i, m) in enumerate(mt) first || println(io) first = false print(io, "[$(i)] ") show(io, m) file, line = updated_methodloc(m) if last_shown_line_infos !== nothing push!(last_shown_line_infos, (string(file), line)) end end first && summary(io, mt) nothing end function show(io::IO, mime::MIME"text/html", mt::AbstractVector{Method}) summary(io, mt) if !isempty(mt) print(io, ":
    ") for d in mt print(io, "
  • ") show(io, mime, d) end print(io, "
") end nothing end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/cartesian.jl๋-# This file is a part of Julia. License is MIT: https://julialang.org/license module Cartesian export @nloops, @nref, @ncall, @nexprs, @nextract, @nall, @nany, @ntuple, @nif ### Cartesian-specific macros """ @nloops N itersym rangeexpr bodyexpr @nloops N itersym rangeexpr preexpr bodyexpr @nloops N itersym rangeexpr preexpr postexpr bodyexpr Generate `N` nested loops, using `itersym` as the prefix for the iteration variables. `rangeexpr` may be an anonymous-function expression, or a simple symbol `var` in which case the range is `axes(var, d)` for dimension `d`. Optionally, you can provide "pre" and "post" expressions. These get executed first and last, respectively, in the body of each loop. For example: @nloops 2 i A d -> j_d = min(i_d, 5) begin s += @nref 2 A j end would generate: for i_2 = axes(A, 2) j_2 = min(i_2, 5) for i_1 = axes(A, 1) j_1 = min(i_1, 5) s += A[j_1, j_2] end end If you want just a post-expression, supply [`nothing`](@ref) for the pre-expression. Using parentheses and semicolons, you can supply multi-statement expressions. """ macro nloops(N, itersym, rangeexpr, args...) _nloops(N, itersym, rangeexpr, args...) end function _nloops(N::Int, itersym::Symbol, arraysym::Symbol, args::Expr...) @gensym d _nloops(N, itersym, :($d->Base.axes($arraysym, $d)), args...) end function _nloops(N::Int, itersym::Symbol, rangeexpr::Expr, args::Expr...) if rangeexpr.head !== :-> throw(ArgumentError("second argument must be an anonymous function expression to compute the range")) end if !(1 <= length(args) <= 3) throw(ArgumentError("number of arguments must be 1 โ‰ค length(args) โ‰ค 3, got $nargs")) end body = args[end] ex = Expr(:escape, body) for dim = 1:N itervar = inlineanonymous(itersym, dim) rng = inlineanonymous(rangeexpr, dim) preexpr = length(args) > 1 ? inlineanonymous(args[1], dim) : (:(nothing)) postexpr = length(args) > 2 ? inlineanonymous(args[2], dim) : (:(nothing)) ex = quote for $(esc(itervar)) = $(esc(rng)) $(esc(preexpr)) $ex $(esc(postexpr)) end end end ex end """ @nref N A indexexpr Generate expressions like `A[i_1, i_2, ...]`. `indexexpr` can either be an iteration-symbol prefix, or an anonymous-function expression. # Examples ```jldoctest julia> @macroexpand Base.Cartesian.@nref 3 A i :(A[i_1, i_2, i_3]) ``` """ macro nref(N::Int, A::Symbol, ex) vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:ref, A, vars...)) end """ @ncall N f sym... Generate a function call expression. `sym` represents any number of function arguments, the last of which may be an anonymous-function expression and is expanded into `N` arguments. For example, `@ncall 3 func a` generates func(a_1, a_2, a_3) while `@ncall 2 func a b i->c[i]` yields func(a, b, c[1], c[2]) """ macro ncall(N::Int, f, args...) pre = args[1:end-1] ex = args[end] vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:call, f, pre..., vars...)) end """ @nexprs N expr Generate `N` expressions. `expr` should be an anonymous-function expression. # Examples ```jldoctest julia> @macroexpand Base.Cartesian.@nexprs 4 i -> y[i] = A[i+j] quote y[1] = A[1 + j] y[2] = A[2 + j] y[3] = A[3 + j] y[4] = A[4 + j] end ``` """ macro nexprs(N::Int, ex::Expr) exs = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:block, exs...)) end """ @nextract N esym isym Generate `N` variables `esym_1`, `esym_2`, ..., `esym_N` to extract values from `isym`. `isym` can be either a `Symbol` or anonymous-function expression. `@nextract 2 x y` would generate x_1 = y[1] x_2 = y[2] while `@nextract 3 x d->y[2d-1]` yields x_1 = y[1] x_2 = y[3] x_3 = y[5] """ macro nextract(N::Int, esym::Symbol, isym::Symbol) aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), :(($isym)[$i]))) for i = 1:N ] Expr(:block, aexprs...) end macro nextract(N::Int, esym::Symbol, ex::Expr) aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), inlineanonymous(ex,i))) for i = 1:N ] Expr(:block, aexprs...) end """ @nall N expr Check whether all of the expressions generated by the anonymous-function expression `expr` evaluate to `true`. `@nall 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 && i_2 > 1 && i_3 > 1)`. This can be convenient for bounds-checking. """ macro nall(N::Int, criterion::Expr) if criterion.head !== :-> throw(ArgumentError("second argument must be an anonymous function expression yielding the criterion")) end conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] Expr(:&&, conds...) end """ @nany N expr Check whether any of the expressions generated by the anonymous-function expression `expr` evaluate to `true`. `@nany 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 || i_2 > 1 || i_3 > 1)`. """ macro nany(N::Int, criterion::Expr) if criterion.head !== :-> error("Second argument must be an anonymous function expression yielding the criterion") end conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] Expr(:||, conds...) end """ @ntuple N expr Generates an `N`-tuple. `@ntuple 2 i` would generate `(i_1, i_2)`, and `@ntuple 2 k->k+1` would generate `(2,3)`. """ macro ntuple(N::Int, ex) vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:tuple, vars...)) end """ @nif N conditionexpr expr @nif N conditionexpr expr elseexpr Generates a sequence of `if ... elseif ... else ... end` statements. For example: @nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK") would generate: if i_1 > size(A, 1) error("Dimension ", 1, " too big") elseif i_2 > size(A, 2) error("Dimension ", 2, " too big") else println("All OK") end """ macro nif(N, condition, operation...) # Handle the final "else" ex = esc(inlineanonymous(length(operation) > 1 ? operation[2] : operation[1], N)) # Make the nested if statements for i = N-1:-1:1 ex = Expr(:if, esc(inlineanonymous(condition,i)), esc(inlineanonymous(operation[1],i)), ex) end ex end ## Utilities # Simplify expressions like :(d->3:size(A,d)-3) given an explicit value for d function inlineanonymous(ex::Expr, val) if ex.head !== :-> throw(ArgumentError("not an anonymous function")) end if !isa(ex.args[1], Symbol) throw(ArgumentError("not a single-argument anonymous function")) end sym = ex.args[1]::Symbol ex = ex.args[2]::Expr exout = lreplace(ex, sym, val) exout = poplinenum(exout) exprresolve(exout) end # Given :i and 3, this generates :i_3 inlineanonymous(base::Symbol, ext) = Symbol(base,'_',ext) # Replace a symbol by a value or a "coded" symbol # E.g., for d = 3, # lreplace(:d, :d, 3) -> 3 # lreplace(:i_d, :d, 3) -> :i_3 # lreplace(:i_{d-1}, :d, 3) -> :i_2 # This follows LaTeX notation. struct LReplace{S<:AbstractString} pat_sym::Symbol pat_str::S val::Int end LReplace(sym::Symbol, val::Integer) = LReplace(sym, string(sym), val) lreplace(ex::Expr, sym::Symbol, val) = lreplace!(copy(ex), LReplace(sym, val)) function lreplace!(sym::Symbol, r::LReplace) sym == r.pat_sym && return r.val Symbol(lreplace!(string(sym), r)) end function lreplace!(str::AbstractString, r::LReplace) i = firstindex(str) pat = r.pat_str j = firstindex(pat) matching = false local istart::Int while i <= ncodeunits(str) cstr = str[i] i = nextind(str, i) if !matching if cstr != '_' || i > ncodeunits(str) continue end istart = i cstr = str[i] i = nextind(str, i) end if j <= lastindex(pat) cr = pat[j] j = nextind(pat, j) if cstr == cr matching = true else matching = false j = firstindex(pat) i = istart continue end end if matching && j > lastindex(pat) if i > lastindex(str) || str[i] == '_' # We have a match return string(str[1:prevind(str, istart)], r.val, lreplace!(str[i:end], r)) end matching = false j = firstindex(pat) i = istart end end str end function lreplace!(ex::Expr, r::LReplace) # Curly-brace notation, which acts like parentheses if ex.head === :curly && length(ex.args) == 2 && isa(ex.args[1], Symbol) && endswith(string(ex.args[1]::Symbol), "_") excurly = exprresolve(lreplace!(ex.args[2], r)) if isa(excurly, Int) return Symbol(ex.args[1]::Symbol, excurly) else ex.args[2] = excurly return ex end end for i in 1:length(ex.args) ex.args[i] = lreplace!(ex.args[i], r) end ex end lreplace!(arg, r::LReplace) = arg poplinenum(arg) = arg function poplinenum(ex::Expr) if ex.head === :block if length(ex.args) == 1 return ex.args[1] elseif length(ex.args) == 2 && isa(ex.args[1], LineNumberNode) return ex.args[2] elseif (length(ex.args) == 2 && isa(ex.args[1], Expr) && ex.args[1].head === :line) return ex.args[2] end end ex end ## Resolve expressions at parsing time ## const exprresolve_arith_dict = Dict{Symbol,Function}(:+ => +, :- => -, :* => *, :/ => /, :^ => ^, :div => div) const exprresolve_cond_dict = Dict{Symbol,Function}(:(==) => ==, :(<) => <, :(>) => >, :(<=) => <=, :(>=) => >=) function exprresolve_arith(ex::Expr) if ex.head === :call callee = ex.args[1] if isa(callee, Symbol) if haskey(exprresolve_arith_dict, callee) && all(Bool[isa(ex.args[i], Number) for i = 2:length(ex.args)]) return true, exprresolve_arith_dict[callee](ex.args[2:end]...) end end end false, 0 end exprresolve_arith(arg) = false, 0 exprresolve_conditional(b::Bool) = true, b function exprresolve_conditional(ex::Expr) if ex.head === :call callee = ex.args[1] if isa(callee, Symbol) if callee โˆˆ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) return true, exprresolve_cond_dict[callee](ex.args[2], ex.args[3]) end end end false, false end exprresolve_conditional(arg) = false, false exprresolve(arg) = arg function exprresolve(ex::Expr) for i = 1:length(ex.args) ex.args[i] = exprresolve(ex.args[i]) end # Handle simple arithmetic can_eval, result = exprresolve_arith(ex) if can_eval return result elseif ex.head === :call && (ex.args[1] === :+ || ex.args[1] === :-) && length(ex.args) == 3 && ex.args[3] == 0 # simplify x+0 and x-0 return ex.args[2] end # Resolve array references if ex.head === :ref && isa(ex.args[1], Array) for i = 2:length(ex.args) if !isa(ex.args[i], Real) return ex end end return ex.args[1][ex.args[2:end]...] end # Resolve conditionals if ex.head === :if can_eval, tf = exprresolve_conditional(ex.args[1]) if can_eval ex = tf ? ex.args[2] : ex.args[3] end end ex end end W/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/multidimensional.jld# This file is a part of Julia. License is MIT: https://julialang.org/license ### Multidimensional iterators module IteratorsMD import .Base: eltype, length, size, first, last, in, getindex, setindex!, min, max, zero, oneunit, isless, eachindex, convert, show, iterate, promote_rule import .Base: +, -, *, (:) import .Base: simd_outer_range, simd_inner_length, simd_index, setindex import .Base: to_indices, to_index, _to_indices1, _cutdim using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail, ReshapedArray, ReshapedArrayLF, OneTo, Fix1 using .Base.Iterators: Reverse, PartitionIterator using .Base: @propagate_inbounds export CartesianIndex, CartesianIndices """ CartesianIndex(i, j, k...) -> I CartesianIndex((i, j, k...)) -> I Create a multidimensional index `I`, which can be used for indexing a multidimensional array `A`. In particular, `A[I]` is equivalent to `A[i,j,k...]`. One can freely mix integer and `CartesianIndex` indices; for example, `A[Ipre, i, Ipost]` (where `Ipre` and `Ipost` are `CartesianIndex` indices and `i` is an `Int`) can be a useful expression when writing algorithms that work along a single dimension of an array of arbitrary dimensionality. A `CartesianIndex` is sometimes produced by [`eachindex`](@ref), and always when iterating with an explicit [`CartesianIndices`](@ref). An `I::CartesianIndex` is treated as a "scalar" (not a container) for `broadcast`. In order to iterate over the components of a `CartesianIndex`, convert it to a tuple with `Tuple(I)`. # Examples ```jldoctest julia> A = reshape(Vector(1:16), (2, 2, 2, 2)) 2ร—2ร—2ร—2 Array{Int64, 4}: [:, :, 1, 1] = 1 3 2 4 [:, :, 2, 1] = 5 7 6 8 [:, :, 1, 2] = 9 11 10 12 [:, :, 2, 2] = 13 15 14 16 julia> A[CartesianIndex((1, 1, 1, 1))] 1 julia> A[CartesianIndex((1, 1, 1, 2))] 9 julia> A[CartesianIndex((1, 1, 2, 1))] 5 ``` !!! compat "Julia 1.10" Using a `CartesianIndex` as a "scalar" for `broadcast` requires Julia 1.10; in previous releases, use `Ref(I)`. """ struct CartesianIndex{N} <: AbstractCartesianIndex{N} I::NTuple{N,Int} CartesianIndex{N}(index::NTuple{N,Integer}) where {N} = new(index) end CartesianIndex(index::NTuple{N,Integer}) where {N} = CartesianIndex{N}(index) CartesianIndex(index::Integer...) = CartesianIndex(index) CartesianIndex{N}(index::Vararg{Integer,N}) where {N} = CartesianIndex{N}(index) # Allow passing tuples smaller than N CartesianIndex{N}(index::Tuple) where {N} = CartesianIndex{N}(fill_to_length(index, 1, Val(N))) CartesianIndex{N}(index::Integer...) where {N} = CartesianIndex{N}(index) CartesianIndex{N}() where {N} = CartesianIndex{N}(()) # Un-nest passed CartesianIndexes CartesianIndex(index::Union{Integer, CartesianIndex}...) = CartesianIndex(flatten(index)) flatten(::Tuple{}) = () flatten(I::Tuple{Any}) = Tuple(I[1]) @inline flatten(I::Tuple) = (Tuple(I[1])..., flatten(tail(I))...) CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...) show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I)) # length length(::CartesianIndex{N}) where {N} = N length(::Type{CartesianIndex{N}}) where {N} = N # indexing getindex(index::CartesianIndex, i::Integer) = index.I[i] Base.get(A::AbstractArray, I::CartesianIndex, default) = get(A, I.I, default) eltype(::Type{T}) where {T<:CartesianIndex} = eltype(fieldtype(T, :I)) # access to index tuple Tuple(index::CartesianIndex) = index.I Base.setindex(x::CartesianIndex,i,j) = CartesianIndex(Base.setindex(Tuple(x),i,j)) # equality Base.:(==)(a::CartesianIndex{N}, b::CartesianIndex{N}) where N = a.I == b.I # zeros and ones zero(::CartesianIndex{N}) where {N} = zero(CartesianIndex{N}) zero(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(Returns(0), Val(N))) oneunit(::CartesianIndex{N}) where {N} = oneunit(CartesianIndex{N}) oneunit(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(Returns(1), Val(N))) # arithmetic, min/max @inline (-)(index::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(-, index.I)) @inline (+)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(+, index1.I, index2.I)) @inline (-)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(-, index1.I, index2.I)) @inline min(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(min, index1.I, index2.I)) @inline max(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(max, index1.I, index2.I)) @inline (*)(a::Integer, index::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(x->a*x, index.I)) @inline (*)(index::CartesianIndex, a::Integer) = *(a,index) # comparison isless(I1::CartesianIndex{N}, I2::CartesianIndex{N}) where {N} = isless(reverse(I1.I), reverse(I2.I)) # conversions convert(::Type{T}, index::CartesianIndex{1}) where {T<:Number} = convert(T, index[1]) convert(::Type{T}, index::CartesianIndex) where {T<:Tuple} = convert(T, index.I) # hashing const cartindexhash_seed = UInt == UInt64 ? 0xd60ca92f8284b8b0 : 0xf2ea7c2e function Base.hash(ci::CartesianIndex, h::UInt) h += cartindexhash_seed for i in ci.I h = hash(i, h) end return h end # nextind and prevind with CartesianIndex function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} iter = CartesianIndices(axes(a)) # might overflow I = inc(i.I, iter.indices) return I end function Base.prevind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} iter = CartesianIndices(axes(a)) # might underflow I = dec(i.I, iter.indices) return I end Base._ind2sub(t::Tuple, ind::CartesianIndex) = Tuple(ind) # Iteration over the elements of CartesianIndex cannot be supported until its length can be inferred, # see #23719 Base.iterate(::CartesianIndex) = error("iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`") # Iteration const OrdinalRangeInt = OrdinalRange{Int, Int} """ CartesianIndices(sz::Dims) -> R CartesianIndices((istart:[istep:]istop, jstart:[jstep:]jstop, ...)) -> R Define a region `R` spanning a multidimensional rectangular range of integer indices. These are most commonly encountered in the context of iteration, where `for I in R ... end` will return [`CartesianIndex`](@ref) indices `I` equivalent to the nested loops for j = jstart:jstep:jstop for i = istart:istep:istop ... end end Consequently these can be useful for writing algorithms that work in arbitrary dimensions. CartesianIndices(A::AbstractArray) -> R As a convenience, constructing a `CartesianIndices` from an array makes a range of its indices. !!! compat "Julia 1.6" The step range method `CartesianIndices((istart:istep:istop, jstart:[jstep:]jstop, ...))` requires at least Julia 1.6. # Examples ```jldoctest julia> foreach(println, CartesianIndices((2, 2, 2))) CartesianIndex(1, 1, 1) CartesianIndex(2, 1, 1) CartesianIndex(1, 2, 1) CartesianIndex(2, 2, 1) CartesianIndex(1, 1, 2) CartesianIndex(2, 1, 2) CartesianIndex(1, 2, 2) CartesianIndex(2, 2, 2) julia> CartesianIndices(fill(1, (2,3))) CartesianIndices((2, 3)) ``` ## Conversion between linear and cartesian indices Linear index to cartesian index conversion exploits the fact that a `CartesianIndices` is an `AbstractArray` and can be indexed linearly: ```jldoctest julia> cartesian = CartesianIndices((1:3, 1:2)) CartesianIndices((1:3, 1:2)) julia> cartesian[4] CartesianIndex(1, 2) julia> cartesian = CartesianIndices((1:2:5, 1:2)) CartesianIndices((1:2:5, 1:2)) julia> cartesian[2, 2] CartesianIndex(3, 2) ``` ## Broadcasting `CartesianIndices` support broadcasting arithmetic (+ and -) with a `CartesianIndex`. !!! compat "Julia 1.1" Broadcasting of CartesianIndices requires at least Julia 1.1. ```jldoctest julia> CIs = CartesianIndices((2:3, 5:6)) CartesianIndices((2:3, 5:6)) julia> CI = CartesianIndex(3, 4) CartesianIndex(3, 4) julia> CIs .+ CI CartesianIndices((5:6, 9:10)) ``` For cartesian to linear index conversion, see [`LinearIndices`](@ref). """ struct CartesianIndices{N,R<:NTuple{N,OrdinalRangeInt}} <: AbstractArray{CartesianIndex{N},N} indices::R end CartesianIndices(::Tuple{}) = CartesianIndices{0,typeof(())}(()) function CartesianIndices(inds::NTuple{N,OrdinalRange{<:Integer, <:Integer}}) where {N} indices = map(r->convert(OrdinalRangeInt, r), inds) CartesianIndices{N, typeof(indices)}(indices) end CartesianIndices(index::CartesianIndex) = CartesianIndices(index.I) CartesianIndices(inds::NTuple{N,Union{<:Integer,OrdinalRange{<:Integer}}}) where {N} = CartesianIndices(map(_convert2ind, inds)) CartesianIndices(A::AbstractArray) = CartesianIndices(axes(A)) _convert2ind(sz::Bool) = Base.OneTo(Int8(sz)) _convert2ind(sz::Integer) = Base.OneTo(sz) _convert2ind(sz::AbstractUnitRange) = first(sz):last(sz) _convert2ind(sz::OrdinalRange) = first(sz):step(sz):last(sz) function show(io::IO, iter::CartesianIndices) print(io, "CartesianIndices(") show(io, map(_xform_index, iter.indices)) print(io, ")") end _xform_index(i) = i _xform_index(i::OneTo) = i.stop show(io::IO, ::MIME"text/plain", iter::CartesianIndices) = show(io, iter) """ (:)(start::CartesianIndex, [step::CartesianIndex], stop::CartesianIndex) Construct [`CartesianIndices`](@ref) from two `CartesianIndex` and an optional step. !!! compat "Julia 1.1" This method requires at least Julia 1.1. !!! compat "Julia 1.6" The step range method start:step:stop requires at least Julia 1.6. # Examples ```jldoctest julia> I = CartesianIndex(2,1); julia> J = CartesianIndex(3,3); julia> I:J CartesianIndices((2:3, 1:3)) julia> I:CartesianIndex(1, 2):J CartesianIndices((2:1:3, 1:2:3)) ``` """ (:)(I::CartesianIndex{N}, J::CartesianIndex{N}) where N = CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J))) (:)(I::CartesianIndex{N}, S::CartesianIndex{N}, J::CartesianIndex{N}) where N = CartesianIndices(map((i,s,j) -> i:s:j, Tuple(I), Tuple(S), Tuple(J))) promote_rule(::Type{CartesianIndices{N,R1}}, ::Type{CartesianIndices{N,R2}}) where {N,R1,R2} = CartesianIndices{N,Base.indices_promote_type(R1,R2)} convert(::Type{Tuple{}}, R::CartesianIndices{0}) = () for RT in (OrdinalRange{Int, Int}, StepRange{Int, Int}, AbstractUnitRange{Int}) @eval convert(::Type{NTuple{N,$RT}}, R::CartesianIndices{N}) where {N} = map(x->convert($RT, x), R.indices) end convert(::Type{NTuple{N,AbstractUnitRange}}, R::CartesianIndices{N}) where {N} = convert(NTuple{N,AbstractUnitRange{Int}}, R) convert(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianIndices{N}) where {N} = UnitRange{Int}.(convert(NTuple{N,AbstractUnitRange}, R)) convert(::Type{NTuple{N,UnitRange}}, R::CartesianIndices{N}) where {N} = UnitRange.(convert(NTuple{N,AbstractUnitRange}, R)) convert(::Type{Tuple{Vararg{AbstractUnitRange{Int}}}}, R::CartesianIndices{N}) where {N} = convert(NTuple{N,AbstractUnitRange{Int}}, R) convert(::Type{Tuple{Vararg{AbstractUnitRange}}}, R::CartesianIndices) = convert(Tuple{Vararg{AbstractUnitRange{Int}}}, R) convert(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianIndices{N}) where {N} = convert(NTuple{N,UnitRange{Int}}, R) convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianIndices) = convert(Tuple{Vararg{UnitRange{Int}}}, R) convert(::Type{CartesianIndices{N,R}}, inds::CartesianIndices{N}) where {N,R} = CartesianIndices(convert(R, inds.indices))::CartesianIndices{N,R} # equality Base.:(==)(a::CartesianIndices{N}, b::CartesianIndices{N}) where N = all(map(==, a.indices, b.indices)) Base.:(==)(a::CartesianIndices, b::CartesianIndices) = false # AbstractArray implementation Base.axes(iter::CartesianIndices{N,R}) where {N,R} = map(Base.axes1, iter.indices) Base.has_offset_axes(iter::CartesianIndices) = Base.has_offset_axes(iter.indices...) @propagate_inbounds function isassigned(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R} for i in 1:N isassigned(iter.indices[i], I[i]) || return false end return true end # getindex for a 0D CartesianIndices is necessary for disambiguation @propagate_inbounds function Base.getindex(iter::CartesianIndices{0,R}) where {R} CartesianIndex() end @inline function Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R} # Eagerly do boundscheck before calculating each item of the CartesianIndex so that # we can pass `@inbounds` hint to inside the map and generates more efficient SIMD codes (#42115) @boundscheck checkbounds(iter, I...) index = map(iter.indices, I) do r, i @inbounds getindex(r, i) end CartesianIndex(index) end # CartesianIndices act as a multidimensional range, so cartesian indexing of CartesianIndices # with compatible dimensions may be seen as indexing into the component ranges. # This may use the special indexing behavior implemented for ranges to return another CartesianIndices @inline function Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Union{OrdinalRange{<:Integer, <:Integer}, Colon}, N}) where {N,R} @boundscheck checkbounds(iter, I...) indices = map(iter.indices, I) do r, i @inbounds getindex(r, i) end CartesianIndices(indices) end @propagate_inbounds function Base.getindex(iter::CartesianIndices{N}, C::CartesianIndices{N}) where {N} getindex(iter, C.indices...) end @inline Base.getindex(iter::CartesianIndices{0}, ::CartesianIndices{0}) = iter # If dimensions permit, we may index into a CartesianIndices directly instead of constructing a SubArray wrapper @propagate_inbounds function Base.view(c::CartesianIndices{N}, r::Vararg{Union{OrdinalRange{<:Integer, <:Integer}, Colon},N}) where {N} getindex(c, r...) end @propagate_inbounds function Base.view(c::CartesianIndices{N}, C::CartesianIndices{N}) where {N} getindex(c, C) end eachindex(::IndexCartesian, A::AbstractArray) = CartesianIndices(axes(A)) @inline function eachindex(::IndexCartesian, A::AbstractArray, B::AbstractArray...) axsA = axes(A) Base._all_match_first(axes, axsA, B...) || Base.throw_eachindex_mismatch_indices(IndexCartesian(), axes(A), axes.(B)...) CartesianIndices(axsA) end @inline function iterate(iter::CartesianIndices) iterfirst = first(iter) if !all(map(in, iterfirst.I, iter.indices)) return nothing end iterfirst, iterfirst end @inline function iterate(iter::CartesianIndices, state) valid, I = __inc(state.I, iter.indices) valid || return nothing return CartesianIndex(I...), CartesianIndex(I...) end # increment & carry @inline function inc(state, indices) _, I = __inc(state, indices) return CartesianIndex(I...) end # Unlike ordinary ranges, CartesianIndices continues the iteration in the next column when the # current column is consumed. The implementation is written recursively to achieve this. # `iterate` returns `Union{Nothing, Tuple}`, we explicitly pass a `valid` flag to eliminate # the type instability inside the core `__inc` logic, and this gives better runtime performance. __inc(::Tuple{}, ::Tuple{}) = false, () @inline function __inc(state::Tuple{Int}, indices::Tuple{OrdinalRangeInt}) rng = indices[1] I = state[1] + step(rng) valid = __is_valid_range(I, rng) && state[1] != last(rng) return valid, (I, ) end @inline function __inc(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}}) rng = indices[1] I = state[1] + step(rng) if __is_valid_range(I, rng) && state[1] != last(rng) return true, (I, tail(state)...) end valid, I = __inc(tail(state), tail(indices)) return valid, (first(rng), I...) end @inline __is_valid_range(I, rng::AbstractUnitRange) = I in rng @inline function __is_valid_range(I, rng::OrdinalRange) if step(rng) > 0 lo, hi = first(rng), last(rng) else lo, hi = last(rng), first(rng) end lo <= I <= hi end # 0-d cartesian ranges are special-cased to iterate once and only once iterate(iter::CartesianIndices{0}, done=false) = done ? nothing : (CartesianIndex(), true) size(iter::CartesianIndices) = map(length, iter.indices) length(iter::CartesianIndices) = prod(size(iter)) # make CartesianIndices a multidimensional range Base.step(iter::CartesianIndices) = CartesianIndex(map(step, iter.indices)) first(iter::CartesianIndices) = CartesianIndex(map(first, iter.indices)) last(iter::CartesianIndices) = CartesianIndex(map(last, iter.indices)) # When used as indices themselves, CartesianIndices can simply become its tuple of ranges _to_indices1(A, inds, I1::CartesianIndices) = map(Fix1(to_index, A), I1.indices) _cutdim(inds::Tuple, I1::CartesianIndices) = split(inds, Val(ndims(I1)))[2] # but preserve CartesianIndices{0} as they consume a dimension. _to_indices1(A, inds, I1::CartesianIndices{0}) = (I1,) @inline in(i::CartesianIndex, r::CartesianIndices) = false @inline in(i::CartesianIndex{N}, r::CartesianIndices{N}) where {N} = all(map(in, i.I, r.indices)) simd_outer_range(iter::CartesianIndices{0}) = iter function simd_outer_range(iter::CartesianIndices) CartesianIndices(tail(iter.indices)) end simd_inner_length(iter::CartesianIndices{0}, ::CartesianIndex) = 1 simd_inner_length(iter::CartesianIndices, I::CartesianIndex) = Base.length(iter.indices[1]) simd_index(iter::CartesianIndices{0}, ::CartesianIndex, I1::Int) = first(iter) @propagate_inbounds simd_index(iter::CartesianIndices, Ilast::CartesianIndex, I1::Int) = CartesianIndex(iter.indices[1][I1+firstindex(iter.indices[1])], Ilast) # Split out the first N elements of a tuple @inline function split(t, V::Val) ref = ntuple(Returns(true), V) # create a reference tuple of length N _split1(t, ref), _splitrest(t, ref) end @inline _split1(t, ref) = (t[1], _split1(tail(t), tail(ref))...) @inline _splitrest(t, ref) = _splitrest(tail(t), tail(ref)) # exit either when we've exhausted the input or reference tuple _split1(::Tuple{}, ::Tuple{}) = () _split1(::Tuple{}, ref) = () _split1(t, ::Tuple{}) = () _splitrest(::Tuple{}, ::Tuple{}) = () _splitrest(t, ::Tuple{}) = t _splitrest(::Tuple{}, ref) = () @inline function split(I::CartesianIndex, V::Val) i, j = split(I.I, V) CartesianIndex(i), CartesianIndex(j) end function split(R::CartesianIndices, V::Val) i, j = split(R.indices, V) CartesianIndices(i), CartesianIndices(j) end # reversed CartesianIndices iteration @inline function Base._reverse(iter::CartesianIndices, ::Colon) CartesianIndices(reverse.(iter.indices)) end Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dim::Integer) 1 <= dim <= ndims(iter) || throw(ArgumentError(Base.LazyString("invalid dimension ", dim, " in reverse"))) ndims(iter) == 1 && return Base._reverse(iter, :) indices = iter.indices return CartesianIndices(Base.setindex(indices, reverse(indices[dim]), dim)) end Base.@constprop :aggressive function Base._reverse(iter::CartesianIndices, dims::Tuple{Vararg{Integer}}) indices = iter.indices # use `sum` to force const fold dimrev = ntuple(i -> sum(==(i), dims; init = 0) == 1, Val(length(indices))) length(dims) == sum(dimrev) || throw(ArgumentError(Base.LazyString("invalid dimensions ", dims, " in reverse"))) length(dims) == length(indices) && return Base._reverse(iter, :) indicesโ€ฒ = map((i, f) -> f ? (@noinline reverse(i)) : i, indices, dimrev) return CartesianIndices(indicesโ€ฒ) end # fix ambiguity with array.jl: Base._reverse(iter::CartesianIndices{1}, dims::Tuple{Integer}) = Base._reverse(iter, first(dims)) @inline function iterate(r::Reverse{<:CartesianIndices}) iterfirst = last(r.itr) if !all(map(in, iterfirst.I, r.itr.indices)) return nothing end iterfirst, iterfirst end @inline function iterate(r::Reverse{<:CartesianIndices}, state) valid, I = __dec(state.I, r.itr.indices) valid || return nothing return CartesianIndex(I...), CartesianIndex(I...) end # decrement & carry @inline function dec(state, indices) _, I = __dec(state, indices) return CartesianIndex(I...) end # decrement post check to avoid integer overflow @inline __dec(::Tuple{}, ::Tuple{}) = false, () @inline function __dec(state::Tuple{Int}, indices::Tuple{OrdinalRangeInt}) rng = indices[1] I = state[1] - step(rng) valid = __is_valid_range(I, rng) && state[1] != first(rng) return valid, (I,) end @inline function __dec(state::Tuple{Int,Int,Vararg{Int}}, indices::Tuple{OrdinalRangeInt,OrdinalRangeInt,Vararg{OrdinalRangeInt}}) rng = indices[1] I = state[1] - step(rng) if __is_valid_range(I, rng) && state[1] != first(rng) return true, (I, tail(state)...) end valid, I = __dec(tail(state), tail(indices)) return valid, (last(rng), I...) end # 0-d cartesian ranges are special-cased to iterate once and only once iterate(iter::Reverse{<:CartesianIndices{0}}, state=false) = state ? nothing : (CartesianIndex(), true) function Base.LinearIndices(inds::CartesianIndices{N,R}) where {N,R<:NTuple{N, AbstractUnitRange}} LinearIndices{N,R}(inds.indices) end function Base.LinearIndices(inds::CartesianIndices) indices = inds.indices if all(x->step(x)==1, indices) indices = map(rng->first(rng):last(rng), indices) LinearIndices{length(indices), typeof(indices)}(indices) else # Given the fact that StepRange 1:2:4 === 1:2:3, we lost the original size information # and thus cannot calculate the correct linear indices when the steps are not 1. throw(ArgumentError("LinearIndices for $(typeof(inds)) with non-1 step size is not yet supported.")) end end # This is currently needed because converting to LinearIndices is only available when steps are # all 1 # NOTE: this is only a temporary patch and could be possibly removed when StepRange support to # LinearIndices is done function Base.collect(inds::CartesianIndices{N, R}) where {N,R<:NTuple{N, AbstractUnitRange}} Base._collect_indices(axes(inds), inds) end function Base.collect(inds::CartesianIndices) dest = Array{eltype(inds), ndims(inds)}(undef, size(inds)) i = 0 @inbounds for a in inds dest[i+=1] = a end dest end # array operations Base.intersect(a::CartesianIndices{N}, b::CartesianIndices{N}) where N = CartesianIndices(intersect.(a.indices, b.indices)) # Views of reshaped CartesianIndices are used for partitions โ€” ensure these are fast const CartesianPartition{T<:CartesianIndex, P<:CartesianIndices, R<:ReshapedArray{T,1,P}} = SubArray{T,1,R,<:Tuple{AbstractUnitRange{Int}},false} eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArrayLF} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArray} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, false} Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:ReshapedArray} = Iterators.IteratorEltype(T) eltype(::Type{PartitionIterator{T}}) where {T<:OneTo} = UnitRange{eltype(T)} eltype(::Type{PartitionIterator{T}}) where {T<:Union{UnitRange, StepRange, StepRangeLen, LinRange}} = T Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Union{OneTo, UnitRange, StepRange, StepRangeLen, LinRange}} = Iterators.IteratorEltype(T) @inline function iterate(iter::CartesianPartition) isempty(iter) && return nothing f = first(iter) return (f, (f, 1)) end @inline function iterate(iter::CartesianPartition, (state, n)) n >= length(iter) && return nothing I = IteratorsMD.inc(state.I, iter.parent.parent.indices) return I, (I, n+1) end @inline function simd_outer_range(iter::CartesianPartition) # In general, the Cartesian Partition might start and stop in the middle of the outer # dimensions โ€” thus the outer range of a CartesianPartition is itself a # CartesianPartition. mi = iter.parent.mi ci = iter.parent.parent ax, ax1 = axes(ci), Base.axes1(ci) subs = Base.ind2sub_rs(ax, mi, first(iter.indices[1])) vl, fl = Base._sub2ind(tail(ax), tail(subs)...), subs[1] vr, fr = divrem(last(iter.indices[1]) - 1, mi[end]) .+ (1, first(ax1)) oci = CartesianIndices(tail(ci.indices)) # A fake CartesianPartition to reuse the outer iterate fallback outer = @inbounds view(ReshapedArray(oci, (length(oci),), mi), vl:vr) init = @inbounds dec(oci[tail(subs)...].I, oci.indices) # real init state # Use Generator to make inner loop branchless @inline function skip_len_I(i::Int, I::CartesianIndex) l = i == 1 ? fl : first(ax1) r = i == length(outer) ? fr : last(ax1) l - first(ax1), r - l + 1, I end (skip_len_I(i, I) for (i, I) in Iterators.enumerate(Iterators.rest(outer, (init, 0)))) end @inline function simd_outer_range(iter::CartesianPartition{CartesianIndex{2}}) # But for two-dimensional Partitions the above is just a simple one-dimensional range # over the second dimension; we don't need to worry about non-rectangular staggers in # higher dimensions. mi = iter.parent.mi ci = iter.parent.parent ax, ax1 = axes(ci), Base.axes1(ci) fl, vl = Base.ind2sub_rs(ax, mi, first(iter.indices[1])) fr, vr = Base.ind2sub_rs(ax, mi, last(iter.indices[1])) outer = @inbounds CartesianIndices((ci.indices[2][vl:vr],)) # Use Generator to make inner loop branchless @inline function skip_len_I(I::CartesianIndex{1}) l = I == first(outer) ? fl : first(ax1) r = I == last(outer) ? fr : last(ax1) l - first(ax1), r - l + 1, I end (skip_len_I(I) for I in outer) end @inline simd_inner_length(iter::CartesianPartition, (_, len, _)::Tuple{Int,Int,CartesianIndex}) = len @propagate_inbounds simd_index(iter::CartesianPartition, (skip, _, I)::Tuple{Int,Int,CartesianIndex}, n::Int) = simd_index(iter.parent.parent, I, n + skip) end # IteratorsMD using .IteratorsMD ## Bounds-checking with CartesianIndex # Disallow linear indexing with CartesianIndex function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{<:CartesianIndex}}) @inline checkbounds_indices(Bool, axes(A), (i,)) end @inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, (), (I[1].I..., tail(I)...)) @inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) @inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) = checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) # Indexing into Array with mixtures of Integers and CartesianIndices is # extremely performance-sensitive. While the abstract fallbacks support this, # codegen has extra support for SIMDification that sub2ind doesn't (yet) support @propagate_inbounds getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = A[to_indices(A, (i1, I...))...] @propagate_inbounds setindex!(A::Array, v, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = (A[to_indices(A, (i1, I...))...] = v; A) # Support indexing with an array of CartesianIndex{N}s # Here we try to consume N of the indices (if there are that many available) # The first two simply handle ambiguities @inline function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I)) end @inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{0}},Vararg{Any}}) checkbounds_indices(Bool, IA, tail(I)) end @inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I)) end @inline function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N IA1, IArest = IteratorsMD.split(IA, Val(N)) checkindex(Bool, IA1, I[1]) & checkbounds_indices(Bool, IArest, tail(I)) end @inline function checkbounds_indices(::Type{Bool}, IA::Tuple{}, I::Tuple{AbstractArray{Bool,N},Vararg{Any}}) where N return checkbounds_indices(Bool, IA, (LogicalIndex(I[1]), tail(I)...)) end @inline function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{AbstractArray{Bool,N},Vararg{Any}}) where N return checkbounds_indices(Bool, IA, (LogicalIndex(I[1]), tail(I)...)) end function checkindex(::Type{Bool}, inds::Tuple, I::AbstractArray{<:CartesianIndex}) b = true for i in I b &= checkbounds_indices(Bool, inds, (i,)) end b end checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndices) = all(checkindex.(Bool, inds, I.indices)) # combined count of all indices, including CartesianIndex and # AbstractArray{CartesianIndex} # rather than returning N, it returns an NTuple{N,Bool} so the result is inferable @inline index_ndims(i1, I...) = (true, index_ndims(I...)...) @inline function index_ndims(i1::CartesianIndex, I...) (map(Returns(true), i1.I)..., index_ndims(I...)...) end @inline function index_ndims(i1::AbstractArray{CartesianIndex{N}}, I...) where N (ntuple(Returns(true), Val(N))..., index_ndims(I...)...) end index_ndims() = () # combined dimensionality of all indices # rather than returning N, it returns an NTuple{N,Bool} so the result is inferable @inline index_dimsum(i1, I...) = (index_dimsum(I...)...,) @inline index_dimsum(::Colon, I...) = (true, index_dimsum(I...)...) @inline index_dimsum(::AbstractArray{Bool}, I...) = (true, index_dimsum(I...)...) @inline function index_dimsum(::AbstractArray{<:Any,N}, I...) where N (ntuple(Returns(true), Val(N))..., index_dimsum(I...)...) end index_dimsum() = () # Recursively compute the lengths of a list of indices, without dropping scalars index_lengths() = () @inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...) @inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...) # shape of array to create for getindex() with indices I, dropping scalars # returns a Tuple{Vararg{AbstractUnitRange}} of indices index_shape() = () @inline index_shape(::Real, rest...) = index_shape(rest...) @inline index_shape(A::AbstractArray, rest...) = (axes(A)..., index_shape(rest...)...) """ LogicalIndex(mask) The `LogicalIndex` type is a special vector that simply contains all indices I where `mask[I]` is true. This specialized type does not support indexing directly as doing so would require O(n) lookup time. `AbstractArray{Bool}` are wrapped with `LogicalIndex` upon calling [`to_indices`](@ref). """ struct LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T} mask::A sum::Int LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, count(mask)) end LogicalIndex(mask::AbstractVector{Bool}) = LogicalIndex{Int, typeof(mask)}(mask) LogicalIndex(mask::AbstractArray{Bool, N}) where {N} = LogicalIndex{CartesianIndex{N}, typeof(mask)}(mask) LogicalIndex{Int}(mask::AbstractArray) = LogicalIndex{Int, typeof(mask)}(mask) size(L::LogicalIndex) = (L.sum,) length(L::LogicalIndex) = L.sum collect(L::LogicalIndex) = [i for i in L] show(io::IO, r::LogicalIndex) = print(io,collect(r)) print_array(io::IO, X::LogicalIndex) = print_array(io, collect(X)) # Iteration over LogicalIndex is very performance-critical, but it also must # support arbitrary AbstractArray{Bool}s with both Int and CartesianIndex. # Thus the iteration state contains an index iterator and its state. We also # keep track of the count of elements since we already know how many there # should be -- this way we don't need to look at future indices to check done. @inline function iterate(L::LogicalIndex{Int}) r = LinearIndices(L.mask) iterate(L, (1, r)) end @inline function iterate(L::LogicalIndex{<:CartesianIndex}) r = CartesianIndices(axes(L.mask)) iterate(L, (1, r)) end @propagate_inbounds function iterate(L::LogicalIndex, s) # We're looking for the n-th true element, using iterator r at state i n = s[1] n > length(L) && return nothing #unroll once to help inference, cf issue #29418 idx, i = iterate(tail(s)...)::Tuple{Any,Any} s = (n+1, s[2], i) L.mask[idx] && return (idx, s) while true idx, i = iterate(tail(s)...)::Tuple{Any,Any} s = (n+1, s[2], i) L.mask[idx] && return (idx, s) end end # When wrapping a BitArray, lean heavily upon its internals. @inline function iterate(L::LogicalIndex{Int,<:BitArray}) L.sum == 0 && return nothing Bc = L.mask.chunks return iterate(L, (1, 1, (), @inbounds Bc[1])) end @inline function iterate(L::LogicalIndex{<:CartesianIndex,<:BitArray}) L.sum == 0 && return nothing Bc = L.mask.chunks irest = ntuple(one, ndims(L.mask)-1) return iterate(L, (1, 1, irest, @inbounds Bc[1])) end @inline function iterate(L::LogicalIndex{<:Any,<:BitArray}, (i1, Bi, irest, c)) Bc = L.mask.chunks while c == 0 Bi >= length(Bc) && return nothing i1 += 64 @inbounds c = Bc[Bi+=1] end tz = trailing_zeros(c) c = _blsr(c) i1, irest = _overflowind(i1 + tz, irest, size(L.mask)) return eltype(L)(i1, irest...), (i1 - tz, Bi, irest, c) end @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) = eachindex(IndexLinear(), A) == eachindex(IndexLinear(), I.mask) @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = axes(A) == axes(I.mask) @inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == axes(I.mask) checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = checkbounds_indices(Bool, inds, axes(I.mask)) ensure_indexable(I::Tuple{}) = () @inline ensure_indexable(I::Tuple{Any, Vararg{Any}}) = (I[1], ensure_indexable(tail(I))...) @inline ensure_indexable(I::Tuple{LogicalIndex, Vararg{Any}}) = (collect(I[1]), ensure_indexable(tail(I))...) # In simple cases, we know that we don't need to use axes(A). Optimize those # until Julia gets smart enough to elide the call on its own: @inline to_indices(A, I::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = to_indices(A, (), I) # But some index types require more context spanning multiple indices # CartesianIndex is unfolded outside the inner to_indices for better inference _to_indices1(A, inds, I1::CartesianIndex) = map(Fix1(to_index, A), I1.I) _cutdim(inds, I1::CartesianIndex) = IteratorsMD.split(inds, Val(length(I1)))[2] # For arrays of CartesianIndex, we just skip the appropriate number of inds _cutdim(inds, I1::AbstractArray{CartesianIndex{N}}) where {N} = IteratorsMD.split(inds, Val(N))[2] # And boolean arrays behave similarly; they also skip their number of dimensions _cutdim(inds::Tuple, I1::AbstractArray{Bool}) = IteratorsMD.split(inds, Val(ndims(I1)))[2] # As an optimization, we allow trailing Array{Bool} and BitArray to be linear over trailing dimensions @inline to_indices(A, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) where {N} = (_maybe_linear_logical_index(IndexStyle(A), A, I[1]),) _maybe_linear_logical_index(::IndexStyle, A, i) = to_index(A, i) _maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i) # Colons get converted to slices by `uncolon` _to_indices1(A, inds, I1::Colon) = (uncolon(inds),) uncolon(::Tuple{}) = Slice(OneTo(1)) uncolon(inds::Tuple) = Slice(inds[1]) ### From abstractarray.jl: Internal multidimensional indexing definitions ### getindex(x::Union{Number,AbstractChar}, ::CartesianIndex{0}) = x getindex(t::Tuple, i::CartesianIndex{1}) = getindex(t, i.I[1]) # These are not defined on directly on getindex to avoid # ambiguities for AbstractArray subtypes. See the note in abstractarray.jl @inline function _getindex(l::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...) @boundscheck checkbounds(A, I...) return _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...) end # But we can speed up IndexCartesian arrays by reshaping them to the appropriate dimensionality: _maybe_reshape(::IndexLinear, A::AbstractArray, I...) = A _maybe_reshape(::IndexCartesian, A::AbstractVector, I...) = A @inline _maybe_reshape(::IndexCartesian, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...)) @inline __maybe_reshape(A::AbstractArray{T,N}, ::NTuple{N,Any}) where {T,N} = A @inline __maybe_reshape(A::AbstractArray, ::NTuple{N,Any}) where {N} = reshape(A, Val(N)) function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N # This is specifically not inlined to prevent excessive allocations in type unstable code shape = index_shape(I...) dest = similar(A, shape) map(length, axes(dest)) == map(length, shape) || throw_checksize_error(dest, shape) _unsafe_getindex!(dest, A, I...) # usually a generated function, don't allow it to impact inference result return dest end function _generate_unsafe_getindex!_body(N::Int) quote @inline D = eachindex(dest) Dy = iterate(D) @inbounds @nloops $N j d->I[d] begin # This condition is never hit, but at the moment # the optimizer is not clever enough to split the union without it Dy === nothing && return dest (idx, state) = Dy dest[idx] = @ncall $N getindex src j Dy = iterate(D, state) end return dest end end # Always index with the exactly indices provided. @generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N _generate_unsafe_getindex!_body(N) end # manually written-out specializations for 1 and 2 arguments to save compile time @eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},1}) $(_generate_unsafe_getindex!_body(1)) end @eval function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray},2}) $(_generate_unsafe_getindex!_body(2)) end @noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) ## setindex! ## function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractArray}...) @inline @boundscheck checkbounds(A, I...) _unsafe_setindex!(l, _maybe_reshape(l, A, I...), x, I...) A end function _generate_unsafe_setindex!_body(N::Int) quote xโ€ฒ = unalias(A, x) @nexprs $N d->(I_d = unalias(A, I[d])) idxlens = @ncall $N index_lengths I @ncall $N setindex_shape_check xโ€ฒ (d->idxlens[d]) Xy = iterate(xโ€ฒ) @inbounds @nloops $N i d->I_d begin # This is never reached, but serves as an assumption for # the optimizer that it does not need to emit error paths Xy === nothing && break (val, state) = Xy @ncall $N setindex! A val i Xy = iterate(xโ€ฒ, state) end A end end @generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray}, N}) where N _generate_unsafe_setindex!_body(N) end @eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},1}) $(_generate_unsafe_setindex!_body(1)) end @eval function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Vararg{Union{Real,AbstractArray},2}) $(_generate_unsafe_setindex!_body(2)) end diff(a::AbstractVector) = diff(a, dims=1) """ diff(A::AbstractVector) diff(A::AbstractArray; dims::Integer) Finite difference operator on a vector or a multidimensional array `A`. In the latter case the dimension to operate on needs to be specified with the `dims` keyword argument. !!! compat "Julia 1.1" `diff` for arrays with dimension higher than 2 requires at least Julia 1.1. # Examples ```jldoctest julia> a = [2 4; 6 16] 2ร—2 Matrix{Int64}: 2 4 6 16 julia> diff(a, dims=2) 2ร—1 Matrix{Int64}: 2 10 julia> diff(vec(a)) 3-element Vector{Int64}: 4 -2 12 ``` """ function diff(a::AbstractArray{T,N}; dims::Integer) where {T,N} require_one_based_indexing(a) 1 <= dims <= N || throw(ArgumentError("dimension $dims out of range (1:$N)")) r = axes(a) r0 = ntuple(i -> i == dims ? UnitRange(1, last(r[i]) - 1) : UnitRange(r[i]), N) r1 = ntuple(i -> i == dims ? UnitRange(2, last(r[i])) : UnitRange(r[i]), N) return view(a, r1...) .- view(a, r0...) end function diff(r::AbstractRange{T}; dims::Integer=1) where {T} dims == 1 || throw(ArgumentError("dimension $dims out of range (1:1)")) return [@inbounds r[i+1] - r[i] for i in firstindex(r):lastindex(r)-1] end ### from abstractarray.jl # In the common case where we have two views into the same parent, aliasing checks # are _much_ easier and more important to get right function mightalias(A::SubArray{T,<:Any,P}, B::SubArray{T,<:Any,P}) where {T,P} if !_parentsmatch(A.parent, B.parent) # We cannot do any better than the usual dataids check return !_isdisjoint(dataids(A), dataids(B)) end # Now we know that A.parent === B.parent. This means that the indices of A # and B are the same length and indexing into the same dimensions. We can # just walk through them and check for overlaps: O(ndims(A)). We must finally # ensure that the indices don't alias with either parent return _indicesmightoverlap(A.indices, B.indices) || !_isdisjoint(dataids(A.parent), _splatmap(dataids, B.indices)) || !_isdisjoint(dataids(B.parent), _splatmap(dataids, A.indices)) end _parentsmatch(A::AbstractArray, B::AbstractArray) = A === B # Two reshape(::Array)s of the same size aren't `===` because they have different headers _parentsmatch(A::Array, B::Array) = pointer(A) == pointer(B) && size(A) == size(B) _indicesmightoverlap(A::Tuple{}, B::Tuple{}) = true _indicesmightoverlap(A::Tuple{}, B::Tuple) = error("malformed subarray") _indicesmightoverlap(A::Tuple, B::Tuple{}) = error("malformed subarray") # For ranges, it's relatively cheap to construct the intersection @inline function _indicesmightoverlap(A::Tuple{AbstractRange, Vararg{Any}}, B::Tuple{AbstractRange, Vararg{Any}}) !isempty(intersect(A[1], B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false end # But in the common AbstractUnitRange case, there's an even faster shortcut @inline function _indicesmightoverlap(A::Tuple{AbstractUnitRange, Vararg{Any}}, B::Tuple{AbstractUnitRange, Vararg{Any}}) max(first(A[1]),first(B[1])) <= min(last(A[1]),last(B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false end # And we can check scalars against each other and scalars against arrays quite easily @inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) = A[1] == B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false @inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}}) = A[1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false @inline _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) = B[1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false # And small arrays are quick, too @inline function _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}}) if length(A[1]) == 1 return A[1][1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false elseif length(B[1]) == 1 return B[1][1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false else # But checking larger arrays requires O(m*n) and is too much work return true end end # And in general, checking the intersection is too much work _indicesmightoverlap(A::Tuple{Any, Vararg{Any}}, B::Tuple{Any, Vararg{Any}}) = true """ fill!(A, x) Fill array `A` with the value `x`. If `x` is an object reference, all elements will refer to the same object. `fill!(A, Foo())` will return `A` filled with the result of evaluating `Foo()` once. # Examples ```jldoctest julia> A = zeros(2,3) 2ร—3 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 0.0 julia> fill!(A, 2.) 2ร—3 Matrix{Float64}: 2.0 2.0 2.0 2.0 2.0 2.0 julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(undef, 3), a); a[1] = 2; A 3-element Vector{Vector{Int64}}: [2, 1, 1] [2, 1, 1] [2, 1, 1] julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(undef, 3), f()) 3-element Vector{Int64}: 1 1 1 ``` """ function fill!(A::AbstractArray{T}, x) where T xT = convert(T, x) for I in eachindex(A) @inbounds A[I] = xT end A end function copyto!(dest::AbstractArray{T1,N}, Rdest::CartesianIndices{N}, src::AbstractArray{T2,N}, Rsrc::CartesianIndices{N}) where {T1,T2,N} isempty(Rdest) && return dest if size(Rdest) != size(Rsrc) throw(ArgumentError("source and destination must have same size (got $(size(Rsrc)) and $(size(Rdest)))")) end checkbounds(dest, first(Rdest)) checkbounds(dest, last(Rdest)) checkbounds(src, first(Rsrc)) checkbounds(src, last(Rsrc)) srcโ€ฒ = unalias(dest, src) CRdest = CartesianIndices(Rdest) CRsrc = CartesianIndices(Rsrc) ฮ”I = first(CRdest) - first(CRsrc) if @generated quote @nloops $N i (n->CRsrc.indices[n]) begin @inbounds @nref($N,dest,n->Rdest.indices[n][i_n+ฮ”I[n]]) = @nref($N,srcโ€ฒ,n->Rsrc.indices[n][i_n]) end end else for I in CRsrc @inbounds dest[Rdest[I + ฮ”I]] = srcโ€ฒ[Rsrc[I]] end end dest end """ copyto!(dest, Rdest::CartesianIndices, src, Rsrc::CartesianIndices) -> dest Copy the block of `src` in the range of `Rsrc` to the block of `dest` in the range of `Rdest`. The sizes of the two regions must match. # Examples ```jldoctest julia> A = zeros(5, 5); julia> B = [1 2; 3 4]; julia> Ainds = CartesianIndices((2:3, 2:3)); julia> Binds = CartesianIndices(B); julia> copyto!(A, Ainds, B, Binds) 5ร—5 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 0.0 1.0 2.0 0.0 0.0 0.0 3.0 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ``` """ copyto!(::AbstractArray, ::CartesianIndices, ::AbstractArray, ::CartesianIndices) # circshift! circshift!(dest::AbstractArray, src, ::Tuple{}) = copyto!(dest, src) """ circshift!(dest, src, shifts) Circularly shift, i.e. rotate, the data in `src`, storing the result in `dest`. `shifts` specifies the amount to shift in each dimension. $(_DOCS_ALIASING_WARNING) See also [`circshift`](@ref). """ @noinline function circshift!(dest::AbstractArray{T,N}, src, shiftamt::DimsInteger) where {T,N} dest === src && throw(ArgumentError("dest and src must be separate arrays")) inds = axes(src) axes(dest) == inds || throw(ArgumentError("indices of src and dest must match (got $inds and $(axes(dest)))")) isempty(src) && return dest _circshift!(dest, (), src, (), inds, fill_to_length(shiftamt, 0, Val(N))) end circshift!(dest::AbstractArray, src, shiftamt) = circshift!(dest, src, map(Integer, (shiftamt...,))) # For each dimension, we copy the first half of src to the second half # of dest, and the second half of src to the first half of dest. This # uses a recursive bifurcation strategy so that these splits can be # encoded by ranges, which means that we need only one call to `mod` # per dimension rather than one call per index. # `rdest` and `rsrc` are tuples-of-ranges that grow one dimension at a # time; when all the dimensions have been filled in, you call `copyto!` # for that block. In other words, in two dimensions schematically we # have the following call sequence (--> means a call): # circshift!(dest, src, shiftamt) --> # _circshift!(dest, src, ("first half of dim1",)) --> # _circshift!(dest, src, ("first half of dim1", "first half of dim2")) --> copyto! # _circshift!(dest, src, ("first half of dim1", "second half of dim2")) --> copyto! # _circshift!(dest, src, ("second half of dim1",)) --> # _circshift!(dest, src, ("second half of dim1", "first half of dim2")) --> copyto! # _circshift!(dest, src, ("second half of dim1", "second half of dim2")) --> copyto! @inline function _circshift!(dest, rdest, src, rsrc, inds::Tuple{AbstractUnitRange,Vararg{Any}}, shiftamt::Tuple{Integer,Vararg{Any}})::typeof(dest) ind1, d = inds[1], shiftamt[1] s = mod(d, length(ind1)) sf, sl = first(ind1)+s, last(ind1)-s r1, r2 = first(ind1):sf-1, sf:last(ind1) r3, r4 = first(ind1):sl, sl+1:last(ind1) tinds, tshiftamt = tail(inds), tail(shiftamt) _circshift!(dest, (rdest..., r1), src, (rsrc..., r4), tinds, tshiftamt) _circshift!(dest, (rdest..., r2), src, (rsrc..., r3), tinds, tshiftamt) end # At least one of inds, shiftamt is empty function _circshift!(dest, rdest, src, rsrc, inds, shiftamt) copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc)) end # circcopy! """ circcopy!(dest, src) Copy `src` to `dest`, indexing each dimension modulo its length. `src` and `dest` must have the same size, but can be offset in their indices; any offset results in a (circular) wraparound. If the arrays have overlapping indices, then on the domain of the overlap `dest` agrees with `src`. $(_DOCS_ALIASING_WARNING) See also: [`circshift`](@ref). # Examples ```julia-repl julia> src = reshape(Vector(1:16), (4,4)) 4ร—4 Array{Int64,2}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 julia> dest = OffsetArray{Int}(undef, (0:3,2:5)) julia> circcopy!(dest, src) OffsetArrays.OffsetArray{Int64,2,Array{Int64,2}} with indices 0:3ร—2:5: 8 12 16 4 5 9 13 1 6 10 14 2 7 11 15 3 julia> dest[1:3,2:4] == src[1:3,2:4] true ``` """ function circcopy!(dest, src) dest === src && throw(ArgumentError("dest and src must be separate arrays")) indssrc, indsdest = axes(src), axes(dest) if (szsrc = map(length, indssrc)) != (szdest = map(length, indsdest)) throw(DimensionMismatch("src and dest must have the same sizes (got $szsrc and $szdest)")) end shift = map((isrc, idest)->first(isrc)-first(idest), indssrc, indsdest) all(x->x==0, shift) && return copyto!(dest, src) _circcopy!(dest, (), indsdest, src, (), indssrc) end # This uses the same strategy described above for _circshift! @inline function _circcopy!(dest, rdest, indsdest::Tuple{AbstractUnitRange,Vararg{Any}}, src, rsrc, indssrc::Tuple{AbstractUnitRange,Vararg{Any}})::typeof(dest) indd1, inds1 = indsdest[1], indssrc[1] l = length(indd1) s = mod(first(inds1)-first(indd1), l) sdf = first(indd1)+s rd1, rd2 = first(indd1):sdf-1, sdf:last(indd1) ssf = last(inds1)-s rs1, rs2 = first(inds1):ssf, ssf+1:last(inds1) tindsd, tindss = tail(indsdest), tail(indssrc) _circcopy!(dest, (rdest..., rd1), tindsd, src, (rsrc..., rs2), tindss) _circcopy!(dest, (rdest..., rd2), tindsd, src, (rsrc..., rs1), tindss) end # At least one of indsdest, indssrc are empty (and both should be, since we've checked) function _circcopy!(dest, rdest, indsdest, src, rsrc, indssrc) copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc)) end ### BitArrays ## getindex # contiguous multidimensional indexing: if the first dimension is a range, # we can get some performance from using copy_chunks! @inline function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{AbstractUnitRange{Int},Slice}) copy_chunks!(X.chunks, 1, B.chunks, indexoffset(I0)+1, length(I0)) return X end # Optimization where the inner dimension is contiguous improves perf dramatically @generated function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{Slice,UnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Slice}...) N = length(I) quote $(Expr(:meta, :inline)) @nexprs $N d->(I_d = I[d]) idxlens = @ncall $N index_lengths I0 I f0 = indexoffset(I0)+1 l0 = idxlens[1] gap_lst_1 = 0 @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) stride = 1 ind = f0 @nexprs $N d->begin stride *= size(B, d) stride_lst_d = stride ind += stride * indexoffset(I_d) gap_lst_{d+1} *= stride end storeind = 1 Xc, Bc = X.chunks, B.chunks @nloops($N, i, d->(1:idxlens[d+1]), d->nothing, # PRE d->(ind += stride_lst_d - gap_lst_d), # POST begin # BODY copy_chunks!(Xc, storeind, Bc, ind, l0) storeind += l0 end) return X end end # in the general multidimensional non-scalar case, can we do about 10% better # in most cases by manually hoisting the bitarray chunks access out of the loop # (This should really be handled by the compiler or with an immutable BitArray) @generated function _unsafe_getindex!(X::BitArray, B::BitArray, I::Union{Int,AbstractArray{Int}}...) N = length(I) quote $(Expr(:meta, :inline)) stride_1 = 1 @nexprs $N d->(stride_{d+1} = stride_d*size(B, d)) $(Symbol(:offset_, N)) = 1 ind = 0 Xc, Bc = X.chunks, B.chunks @nloops $N i d->I[d] d->(@inbounds offset_{d-1} = offset_d + (i_d-1)*stride_d) begin ind += 1 unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind) end return X end end ## setindex! function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray, pos_s::Int, numbits::Int) bind = pos_d cind = pos_s lastind = pos_d + numbits - 1 @inbounds while bind โ‰ค lastind unsafe_bitsetindex!(Bc, Bool(C[cind]), bind) bind += 1 cind += 1 end end # Note: the next two functions rely on the following definition of the conversion to Bool: # convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(...)) # they're used to preemptively check in bulk when possible, which is much faster. # Also, the functions can be overloaded for custom types T<:Real : # a) in the unlikely eventuality that they use a different logic for Bool conversion # b) to skip the check if not necessary @inline try_bool_conversion(x::Real) = x == 0 || x == 1 || throw(InexactError(:try_bool_conversion, Bool, x)) @inline unchecked_bool_convert(x::Real) = x == 1 function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray{<:Real}, pos_s::Int, numbits::Int) @inbounds for i = (1:numbits) .+ (pos_s - 1) try_bool_conversion(C[i]) end kd0, ld0 = get_chunks_id(pos_d) kd1, ld1 = get_chunks_id(pos_d + numbits - 1) delta_kd = kd1 - kd0 u = _msk64 if delta_kd == 0 msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) lt0 = ld1 else msk_d0 = ~(u << ld0) msk_d1 = (u << (ld1+1)) lt0 = 63 end bind = kd0 ind = pos_s @inbounds if ld0 > 0 c = UInt64(0) for j = ld0:lt0 c |= (UInt64(unchecked_bool_convert(C[ind])) << j) ind += 1 end Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) bind += 1 end nc = _div64(numbits - ind + pos_s) @inbounds for i = 1:nc c = UInt64(0) for j = 0:63 c |= (UInt64(unchecked_bool_convert(C[ind])) << j) ind += 1 end Bc[bind] = c bind += 1 end @inbounds if bind โ‰ค kd1 @assert bind == kd1 c = UInt64(0) for j = 0:ld1 c |= (UInt64(unchecked_bool_convert(C[ind])) << j) ind += 1 end Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) end end # contiguous multidimensional indexing: if the first dimension is a range, # we can get some performance from using copy_chunks! @inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray}, J0::Union{Colon,AbstractUnitRange{Int}}) I0 = to_indices(B, (J0,))[1] @boundscheck checkbounds(B, I0) l0 = length(I0) setindex_shape_check(X, l0) l0 == 0 && return B f0 = indexoffset(I0)+1 copy_to_bitarray_chunks!(B.chunks, f0, X, 1, l0) return B end @inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray}, I0::Union{Colon,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Colon}...) J = to_indices(B, (I0, I...)) @boundscheck checkbounds(B, J...) _unsafe_setindex!(B, X, J...) end @generated function _unsafe_setindex!(B::BitArray, X::Union{StridedArray,BitArray}, I0::Union{Slice,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Slice}...) N = length(I) quote idxlens = @ncall $N index_lengths I0 d->I[d] @ncall $N setindex_shape_check X idxlens[1] d->idxlens[d+1] isempty(X) && return B f0 = indexoffset(I0)+1 l0 = idxlens[1] gap_lst_1 = 0 @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) stride = 1 ind = f0 @nexprs $N d->begin stride *= size(B, d) stride_lst_d = stride ind += stride * indexoffset(I[d]) gap_lst_{d+1} *= stride end refind = 1 Bc = B.chunks @nloops($N, i, d->I[d], d->nothing, # PRE d->(ind += stride_lst_d - gap_lst_d), # POST begin # BODY copy_to_bitarray_chunks!(Bc, ind, X, refind, l0) refind += l0 end) return B end end @propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, I0::Union{Colon,AbstractUnitRange{Int}}, I::Union{Int,AbstractUnitRange{Int},Colon}...) _setindex!(IndexStyle(B), B, X, to_indices(B, (I0, I...))...) end ## fill! contiguous views of BitArrays with a single value function fill!(V::SubArray{Bool, <:Any, <:BitArray, <:Tuple{AbstractUnitRange{Int}}}, x) B = V.parent I0 = V.indices[1] l0 = length(I0) l0 == 0 && return V fill_chunks!(B.chunks, Bool(x), first(I0), l0) return V end fill!(V::SubArray{Bool, <:Any, <:BitArray, <:Tuple{AbstractUnitRange{Int}, Vararg{Union{Int,AbstractUnitRange{Int}}}}}, x) = _unsafe_fill_indices!(V.parent, x, V.indices...) @generated function _unsafe_fill_indices!(B::BitArray, x, I0::AbstractUnitRange{Int}, I::Union{Int,AbstractUnitRange{Int}}...) N = length(I) quote y = Bool(x) idxlens = @ncall $N index_lengths I0 d->I[d] f0 = indexoffset(I0)+1 l0 = idxlens[1] l0 == 0 && return B @nexprs $N d->(isempty(I[d]) && return B) gap_lst_1 = 0 @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) stride = 1 ind = f0 @nexprs $N d->begin stride *= size(B, d) stride_lst_d = stride ind += stride * indexoffset(I[d]) gap_lst_{d+1} *= stride end @nloops($N, i, d->I[d], d->nothing, # PRE d->(ind += stride_lst_d - gap_lst_d), # POST fill_chunks!(B.chunks, y, ind, l0) # BODY ) return B end end ## isassigned @generated function isassigned(B::BitArray, I_0::Int, I::Int...) N = length(I) quote @nexprs $N d->(I_d = I[d]) stride = 1 index = I_0 @nexprs $N d->begin l = size(B,d) stride *= l @boundscheck 1 <= I_{d-1} <= l || return false index += (I_d - 1) * stride end return isassigned(B, index) end end isassigned(a::AbstractArray, i::CartesianIndex) = isassigned(a, Tuple(i)...) function isassigned(A::AbstractArray, i::Union{Integer, CartesianIndex}...) isa(i, Tuple{Vararg{Int}}) || return isassigned(A, CartesianIndex(to_indices(A, i))) @boundscheck checkbounds(Bool, A, i...) || return false S = IndexStyle(A) ninds = length(i) if (isa(S, IndexLinear) && ninds != 1) return @inbounds isassigned(A, _to_linear_index(A, i...)) elseif (!isa(S, IndexLinear) && ninds != ndims(A)) return @inbounds isassigned(A, _to_subscript_indices(A, i...)...) else try A[i...] true catch e if isa(e, BoundsError) || isa(e, UndefRefError) return false else rethrow() end end end end ## permutedims ## Permute array dims ## function permutedims(B::StridedArray, perm) dimsB = size(B) ndimsB = length(dimsB) (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions")) dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB) P = similar(B, dimsP) permutedims!(P, B, perm) end function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N} indsB = axes(B) length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))")) isperm(perm) || throw(ArgumentError("input is not a permutation")) indsP = axes(P) for i = 1:length(perm) indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size")) end nothing end for (V, PT, BT) in Any[((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)] @eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...) quote checkdims_perm(P, B, perm) #calculates all the strides native_strides = size_to_strides(1, size(B)...) strides_1 = 0 @nexprs $N d->(strides_{d+1} = native_strides[perm[d]]) #Creates offset, because indexing starts at 1 offset = 1 - sum(@ntuple $N d->strides_{d+1}) sumc = 0 ind = 1 @nloops($N, i, P, d->(sumc += i_d*strides_{d+1}), # PRE d->(sumc -= i_d*strides_{d+1}), # POST begin # BODY @inbounds P[ind] = B[sumc+offset] ind += 1 end) return P end end end ## unique across dim # TODO: this doesn't fit into the new hashing scheme in any obvious way struct Prehashed hash::UInt end hash(x::Prehashed) = x.hash """ unique(A::AbstractArray; dims::Int) Return unique regions of `A` along dimension `dims`. # Examples ```jldoctest julia> A = map(isodd, reshape(Vector(1:8), (2,2,2))) 2ร—2ร—2 Array{Bool, 3}: [:, :, 1] = 1 1 0 0 [:, :, 2] = 1 1 0 0 julia> unique(A) 2-element Vector{Bool}: 1 0 julia> unique(A, dims=2) 2ร—1ร—2 Array{Bool, 3}: [:, :, 1] = 1 0 [:, :, 2] = 1 0 julia> unique(A, dims=3) 2ร—2ร—1 Array{Bool, 3}: [:, :, 1] = 1 1 0 0 ``` """ unique(A::AbstractArray; dims::Union{Colon,Integer} = :) = _unique_dims(A, dims) _unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A) @generated function _unique_dims(A::AbstractArray{T,N}, dim::Integer) where {T,N} quote 1 <= dim <= $N || return copy(A) hashes = zeros(UInt, axes(A, dim)) # Compute hash for each row k = 0 @nloops $N i A d->(if d == dim; k = i_d; end) begin @inbounds hashes[k] = hash(hashes[k], hash((@nref $N A i))) end # Collect index of first row for each hash uniquerow = similar(Array{Int}, axes(A, dim)) firstrow = Dict{Prehashed,Int}() for k = axes(A, dim) uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k) end uniquerows = collect(values(firstrow)) # Check for collisions collided = falses(axes(A, dim)) @inbounds begin @nloops $N i A d->(if d == dim k = i_d j_d = uniquerow[k] else j_d = i_d end) begin if !isequal((@nref $N A j), (@nref $N A i)) collided[k] = true end end end if any(collided) nowcollided = similar(BitArray, axes(A, dim)) while any(collided) # Collect index of first row for each collided hash empty!(firstrow) for j = axes(A, dim) collided[j] || continue uniquerow[j] = get!(firstrow, Prehashed(hashes[j]), j) end for v in values(firstrow) push!(uniquerows, v) end # Check for collisions fill!(nowcollided, false) @nloops $N i A d->begin if d == dim k = i_d j_d = uniquerow[k] (!collided[k] || j_d == k) && continue else j_d = i_d end end begin if !isequal((@nref $N A j), (@nref $N A i)) nowcollided[k] = true end end (collided, nowcollided) = (nowcollided, collided) end end @nref $N A d->d == dim ? sort!(uniquerows) : (axes(A, d)) end end # Show for pairs() with Cartesian indices. Needs to be here rather than show.jl for bootstrap order function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T <: Union{AbstractVector, Tuple} print(io, "pairs(::$T)") end function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T <: AbstractArray print(io, "pairs(::$T)") end function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T<:AbstractVector print(io, "pairs(IndexCartesian(), ::$T)") end ## sortslices """ sortslices(A; dims, alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Sort slices of an array `A`. The required keyword argument `dims` must be either an integer or a tuple of integers. It specifies the dimension(s) over which the slices are sorted. E.g., if `A` is a matrix, `dims=1` will sort rows, `dims=2` will sort columns. Note that the default comparison function on one dimensional slices sorts lexicographically. For the remaining keyword arguments, see the documentation of [`sort!`](@ref). # Examples ```jldoctest julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1) # Sort rows 3ร—3 Matrix{Int64}: -1 6 4 7 3 5 9 -2 8 julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, lt=(x,y)->isless(x[2],y[2])) 3ร—3 Matrix{Int64}: 9 -2 8 7 3 5 -1 6 4 julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, rev=true) 3ร—3 Matrix{Int64}: 9 -2 8 7 3 5 -1 6 4 julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2) # Sort columns 3ร—3 Matrix{Int64}: 3 5 7 -1 -4 6 -2 8 9 julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, alg=InsertionSort, lt=(x,y)->isless(x[2],y[2])) 3ร—3 Matrix{Int64}: 5 3 7 -4 -1 6 8 -2 9 julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, rev=true) 3ร—3 Matrix{Int64}: 7 5 3 6 -4 -1 9 8 -2 ``` # Higher dimensions `sortslices` extends naturally to higher dimensions. E.g., if `A` is a a 2x2x2 array, `sortslices(A, dims=3)` will sort slices within the 3rd dimension, passing the 2x2 slices `A[:, :, 1]` and `A[:, :, 2]` to the comparison function. Note that while there is no default order on higher-dimensional slices, you may use the `by` or `lt` keyword argument to specify such an order. If `dims` is a tuple, the order of the dimensions in `dims` is relevant and specifies the linear order of the slices. E.g., if `A` is three dimensional and `dims` is `(1, 2)`, the orderings of the first two dimensions are re-arranged such that the slices (of the remaining third dimension) are sorted. If `dims` is `(2, 1)` instead, the same slices will be taken, but the result order will be row-major instead. # Higher dimensional examples ``` julia> A = permutedims(reshape([4 3; 2 1; 'A' 'B'; 'C' 'D'], (2, 2, 2)), (1, 3, 2)) 2ร—2ร—2 Array{Any, 3}: [:, :, 1] = 4 3 2 1 [:, :, 2] = 'A' 'B' 'C' 'D' julia> sortslices(A, dims=(1,2)) 2ร—2ร—2 Array{Any, 3}: [:, :, 1] = 1 3 2 4 [:, :, 2] = 'D' 'B' 'C' 'A' julia> sortslices(A, dims=(2,1)) 2ร—2ร—2 Array{Any, 3}: [:, :, 1] = 1 2 3 4 [:, :, 2] = 'D' 'C' 'B' 'A' julia> sortslices(reshape([5; 4; 3; 2; 1], (1,1,5)), dims=3, by=x->x[1,1]) 1ร—1ร—5 Array{Int64, 3}: [:, :, 1] = 1 [:, :, 2] = 2 [:, :, 3] = 3 [:, :, 4] = 4 [:, :, 5] = 5 ``` """ function sortslices(A::AbstractArray; dims::Union{Integer, Tuple{Vararg{Integer}}}, kws...) _sortslices(A, Val{dims}(); kws...) end # Works around inference's lack of ability to recognize partial constness struct DimSelector{dims, T} A::T end DimSelector{dims}(x::T) where {dims, T} = DimSelector{dims, T}(x) (ds::DimSelector{dims, T})(i) where {dims, T} = i in dims ? axes(ds.A, i) : (:,) _negdims(n, dims) = filter(i->!(i in dims), 1:n) function compute_itspace(A, ::Val{dims}) where {dims} negdims = _negdims(ndims(A), dims) axs = Iterators.product(ntuple(DimSelector{dims}(A), ndims(A))...) vec(permutedims(collect(axs), (dims..., negdims...))) end function _sortslices(A::AbstractArray, d::Val{dims}; kws...) where dims itspace = compute_itspace(A, d) vecs = map(its->view(A, its...), itspace) p = sortperm(vecs; kws...) if ndims(A) == 2 && isa(dims, Integer) && isa(A, Array) # At the moment, the performance of the generic version is subpar # (about 5x slower). Hardcode a fast-path until we're able to # optimize this. return dims == 1 ? A[p, :] : A[:, p] else B = similar(A) for (x, its) in zip(p, itspace) B[its...] = vecs[x] end B end end getindex(b::Ref, ::CartesianIndex{0}) = getindex(b) setindex!(b::Ref, x, ::CartesianIndex{0}) = setindex!(b, x) P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/broadcast.jl๛๒# This file is a part of Julia. License is MIT: https://julialang.org/license """ Base.Broadcast Module containing the broadcasting implementation. """ module Broadcast using .Base.Cartesian using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, promote_typejoin_union, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias, negate import .Base: copy, copyto!, axes export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__, BroadcastFunction ## Computing the result's axes: deprecated name const broadcast_axes = axes ### Objects with customized broadcasting behavior should declare a BroadcastStyle """ `BroadcastStyle` is an abstract type and trait-function used to determine behavior of objects under broadcasting. `BroadcastStyle(typeof(x))` returns the style associated with `x`. To customize the broadcasting behavior of a type, one can declare a style by defining a type/method pair struct MyContainerStyle <: BroadcastStyle end Base.BroadcastStyle(::Type{<:MyContainer}) = MyContainerStyle() One then writes method(s) (at least [`similar`](@ref)) operating on `Broadcasted{MyContainerStyle}`. There are also several pre-defined subtypes of `BroadcastStyle` that you may be able to leverage; see the [Interfaces chapter](@ref man-interfaces-broadcasting) for more information. """ abstract type BroadcastStyle end struct Unknown <: BroadcastStyle end BroadcastStyle(::Type{Union{}}, slurp...) = Unknown() # ambiguity resolution """ `Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`, for example Base.BroadcastStyle(::Type{<:MyContainer}) = Broadcast.Style{MyContainer}() """ struct Style{T} <: BroadcastStyle end BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() """ `Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style associated with an `AbstractArray` type. The `N` parameter is the dimensionality, which can be handy for AbstractArray types that only support specific dimensionalities: struct SparseMatrixStyle <: Broadcast.AbstractArrayStyle{2} end Base.BroadcastStyle(::Type{<:SparseMatrixCSC}) = SparseMatrixStyle() For `AbstractArray` types that support arbitrary dimensionality, `N` can be set to `Any`: struct MyArrayStyle <: Broadcast.AbstractArrayStyle{Any} end Base.BroadcastStyle(::Type{<:MyArray}) = MyArrayStyle() In cases where you want to be able to mix multiple `AbstractArrayStyle`s and keep track of dimensionality, your style needs to support a [`Val`](@ref) constructor: struct MyArrayStyleDim{N} <: Broadcast.AbstractArrayStyle{N} end (::Type{<:MyArrayStyleDim})(::Val{N}) where N = MyArrayStyleDim{N}() Note that if two or more `AbstractArrayStyle` subtypes conflict, broadcasting machinery will fall back to producing `Array`s. If this is undesirable, you may need to define binary [`BroadcastStyle`](@ref) rules to control the output type. See also [`Broadcast.DefaultArrayStyle`](@ref). """ abstract type AbstractArrayStyle{N} <: BroadcastStyle end """ `Broadcast.ArrayStyle{MyArrayType}()` is a [`BroadcastStyle`](@ref) indicating that an object behaves as an array for broadcasting. It presents a simple way to construct [`Broadcast.AbstractArrayStyle`](@ref)s for specific `AbstractArray` container types. Broadcast styles created this way lose track of dimensionality; if keeping track is important for your type, you should create your own custom [`Broadcast.AbstractArrayStyle`](@ref). """ struct ArrayStyle{A<:AbstractArray} <: AbstractArrayStyle{Any} end ArrayStyle{A}(::Val) where A = ArrayStyle{A}() """ `Broadcast.DefaultArrayStyle{N}()` is a [`BroadcastStyle`](@ref) indicating that an object behaves as an `N`-dimensional array for broadcasting. Specifically, `DefaultArrayStyle` is used for any `AbstractArray` type that hasn't defined a specialized style, and in the absence of overrides from other `broadcast` arguments the resulting output type is `Array`. When there are multiple inputs to `broadcast`, `DefaultArrayStyle` "loses" to any other [`Broadcast.ArrayStyle`](@ref). """ struct DefaultArrayStyle{N} <: AbstractArrayStyle{N} end DefaultArrayStyle(::Val{N}) where N = DefaultArrayStyle{N}() DefaultArrayStyle{M}(::Val{N}) where {N,M} = DefaultArrayStyle{N}() const DefaultVectorStyle = DefaultArrayStyle{1} const DefaultMatrixStyle = DefaultArrayStyle{2} BroadcastStyle(::Type{<:AbstractArray{T,N}}) where {T,N} = DefaultArrayStyle{N}() BroadcastStyle(::Type{T}) where {T} = DefaultArrayStyle{ndims(T)}() # `ArrayConflict` is an internal type signaling that two or more different `AbstractArrayStyle` # objects were supplied as arguments, and that no rule was defined for resolving the # conflict. The resulting output is `Array`. While this is the same output type # produced by `DefaultArrayStyle`, `ArrayConflict` "poisons" the BroadcastStyle so that # 3 or more arguments still return an `ArrayConflict`. struct ArrayConflict <: AbstractArrayStyle{Any} end ArrayConflict(::Val) = ArrayConflict() ### Binary BroadcastStyle rules """ BroadcastStyle(::Style1, ::Style2) = Style3() Indicate how to resolve different `BroadcastStyle`s. For example, BroadcastStyle(::Primary, ::Secondary) = Primary() would indicate that style `Primary` has precedence over `Secondary`. You do not have to (and generally should not) define both argument orders. The result does not have to be one of the input arguments, it could be a third type. Please see the [Interfaces chapter](@ref man-interfaces-broadcasting) of the manual for more information. """ BroadcastStyle(::S, ::S) where S<:BroadcastStyle = S() # homogeneous types preserved # Fall back to Unknown. This is necessary to implement argument-swapping BroadcastStyle(::BroadcastStyle, ::BroadcastStyle) = Unknown() # Unknown loses to everything BroadcastStyle(::Unknown, ::Unknown) = Unknown() BroadcastStyle(::S, ::Unknown) where S<:BroadcastStyle = S() # Precedence rules BroadcastStyle(a::AbstractArrayStyle{0}, b::Style{Tuple}) = b BroadcastStyle(a::AbstractArrayStyle, ::Style{Tuple}) = a BroadcastStyle(::A, ::A) where A<:ArrayStyle = A() BroadcastStyle(::ArrayStyle, ::ArrayStyle) = Unknown() BroadcastStyle(::A, ::A) where A<:AbstractArrayStyle = A() function BroadcastStyle(a::A, b::B) where {A<:AbstractArrayStyle{M},B<:AbstractArrayStyle{N}} where {M,N} if Base.typename(A) === Base.typename(B) return A(Val(max(M, N))) end return Unknown() end # Any specific array type beats DefaultArrayStyle BroadcastStyle(a::AbstractArrayStyle{Any}, ::DefaultArrayStyle) = a BroadcastStyle(a::AbstractArrayStyle{N}, ::DefaultArrayStyle{N}) where N = a BroadcastStyle(a::AbstractArrayStyle{M}, ::DefaultArrayStyle{N}) where {M,N} = typeof(a)(Val(max(M, N))) ### Lazy-wrapper for broadcasting # `Broadcasted` wrap the arguments to `broadcast(f, args...)`. A statement like # y = x .* (x .+ 1) # will result in code that is essentially # y = copy(Broadcasted(*, x, Broadcasted(+, x, 1))) # `broadcast!` results in `copyto!(dest, Broadcasted(...))`. # The use of `Nothing` in place of a `BroadcastStyle` has a different # application, in the fallback method # copyto!(dest, bc::Broadcasted) = copyto!(dest, convert(Broadcasted{Nothing}, bc)) # This allows methods # copyto!(dest::DestType, bc::Broadcasted{Nothing}) # that specialize on `DestType` to be easily disambiguated from # methods that instead specialize on `BroadcastStyle`, # copyto!(dest::AbstractArray, bc::Broadcasted{MyStyle}) struct Broadcasted{Style<:Union{Nothing,BroadcastStyle}, Axes, F, Args<:Tuple} <: Base.AbstractBroadcasted style::Style f::F args::Args axes::Axes # the axes of the resulting object (may be bigger than implied by `args` if this is nested inside a larger `Broadcasted`) Broadcasted(style::Union{Nothing,BroadcastStyle}, f::Tuple, args::Tuple) = error() # disambiguation: tuple is not callable function Broadcasted(style::Union{Nothing,BroadcastStyle}, f::F, args::Tuple, axes=nothing) where {F} # using Core.Typeof rather than F preserves inferrability when f is a type return new{typeof(style), typeof(axes), Core.Typeof(f), typeof(args)}(style, f, args, axes) end function Broadcasted(f::F, args::Tuple, axes=nothing) where {F} Broadcasted(combine_styles(args...)::BroadcastStyle, f, args, axes) end function Broadcasted{Style}(f::F, args, axes=nothing) where {Style, F} return new{Style, typeof(axes), Core.Typeof(f), typeof(args)}(Style()::Style, f, args, axes) end function Broadcasted{Style,Axes,F,Args}(f, args, axes) where {Style,Axes,F,Args} return new{Style, Axes, F, Args}(Style()::Style, f, args, axes) end end struct AndAnd end const andand = AndAnd() broadcasted(::AndAnd, a, b) = broadcasted((a, b) -> a && b, a, b) function broadcasted(::AndAnd, a, bc::Broadcasted) bcf = flatten(bc) broadcasted((a, args...) -> a && bcf.f(args...), a, bcf.args...) end struct OrOr end const oror = OrOr() broadcasted(::OrOr, a, b) = broadcasted((a, b) -> a || b, a, b) function broadcasted(::OrOr, a, bc::Broadcasted) bcf = flatten(bc) broadcasted((a, args...) -> a || bcf.f(args...), a, bcf.args...) end Base.convert(::Type{Broadcasted{NewStyle}}, bc::Broadcasted{<:Any,Axes,F,Args}) where {NewStyle,Axes,F,Args} = Broadcasted{NewStyle,Axes,F,Args}(bc.f, bc.args, bc.axes)::Broadcasted{NewStyle,Axes,F,Args} function Base.show(io::IO, bc::Broadcasted{Style}) where {Style} print(io, Broadcasted) # Only show the style parameter if we have a set of axes โ€” representing an instantiated # "outermost" Broadcasted. The styles of nested Broadcasteds represent an intermediate # computation that is not relevant for dispatch, confusing, and just extra line noise. bc.axes isa Tuple && print(io, "{", Style, "}") print(io, "(", bc.f, ", ", bc.args, ")") nothing end ## Allocating the output container Base.similar(bc::Broadcasted, ::Type{T}) where {T} = similar(bc, T, axes(bc)) Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}, dims) where {N,ElType} = similar(Array{ElType}, dims) Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{Bool}, dims) where N = similar(BitArray, dims) # In cases of conflict we fall back on Array Base.similar(::Broadcasted{ArrayConflict}, ::Type{ElType}, dims) where ElType = similar(Array{ElType}, dims) Base.similar(::Broadcasted{ArrayConflict}, ::Type{Bool}, dims) = similar(BitArray, dims) @inline Base.axes(bc::Broadcasted) = _axes(bc, bc.axes) _axes(::Broadcasted, axes::Tuple) = axes @inline _axes(bc::Broadcasted, ::Nothing) = combine_axes(bc.args...) _axes(bc::Broadcasted{<:AbstractArrayStyle{0}}, ::Nothing) = () @inline Base.axes(bc::Broadcasted{<:Any, <:NTuple{N}}, d::Integer) where N = d <= N ? axes(bc)[d] : OneTo(1) BroadcastStyle(::Type{<:Broadcasted{Style}}) where {Style} = Style() BroadcastStyle(::Type{<:Broadcasted{S}}) where {S<:Union{Nothing,Unknown}} = throw(ArgumentError("Broadcasted{Unknown} wrappers do not have a style assigned")) argtype(::Type{BC}) where {BC<:Broadcasted} = fieldtype(BC, :args) argtype(bc::Broadcasted) = argtype(typeof(bc)) @inline Base.eachindex(bc::Broadcasted) = _eachindex(axes(bc)) _eachindex(t::Tuple{Any}) = t[1] _eachindex(t::Tuple) = CartesianIndices(t) Base.IndexStyle(bc::Broadcasted) = IndexStyle(typeof(bc)) Base.IndexStyle(::Type{<:Broadcasted{<:Any,<:Tuple{Any}}}) = IndexLinear() Base.IndexStyle(::Type{<:Broadcasted{<:Any}}) = IndexCartesian() Base.LinearIndices(bc::Broadcasted{<:Any,<:Tuple{Any}}) = LinearIndices(axes(bc))::LinearIndices{1} Base.ndims(bc::Broadcasted) = ndims(typeof(bc)) Base.ndims(::Type{<:Broadcasted{<:Any,<:NTuple{N,Any}}}) where {N} = N Base.size(bc::Broadcasted) = map(length, axes(bc)) Base.length(bc::Broadcasted) = prod(size(bc)) function Base.iterate(bc::Broadcasted) iter = eachindex(bc) iterate(bc, (iter,)) end Base.@propagate_inbounds function Base.iterate(bc::Broadcasted, s) y = iterate(s...) y === nothing && return nothing i, newstate = y return (bc[i], (s[1], newstate)) end Base.IteratorSize(::Type{T}) where {T<:Broadcasted} = Base.HasShape{ndims(T)}() Base.ndims(BC::Type{<:Broadcasted{<:Any,Nothing}}) = _maxndims(fieldtype(BC, :args)) Base.ndims(::Type{<:Broadcasted{<:AbstractArrayStyle{N},Nothing}}) where {N<:Integer} = N _maxndims(T::Type{<:Tuple}) = reduce(max, (ntuple(n -> _ndims(fieldtype(T, n)), Base._counttuple(T)))) _maxndims(::Type{<:Tuple{T}}) where {T} = ndims(T) _maxndims(::Type{<:Tuple{T}}) where {T<:Tuple} = _ndims(T) function _maxndims(::Type{<:Tuple{T, S}}) where {T, S} return T<:Tuple || S<:Tuple ? max(_ndims(T), _ndims(S)) : max(ndims(T), ndims(S)) end _ndims(x) = ndims(x) _ndims(::Type{<:Tuple}) = 1 Base.IteratorEltype(::Type{<:Broadcasted}) = Base.EltypeUnknown() ## Instantiation fills in the "missing" fields in Broadcasted. instantiate(x) = x """ Broadcast.instantiate(bc::Broadcasted) Construct and check the axes for the lazy Broadcasted object `bc`. Custom [`BroadcastStyle`](@ref)s may override this default in cases where it is fast and easy to compute and verify the resulting `axes` on-demand, leaving the `axis` field of the `Broadcasted` object empty (populated with [`nothing`](@ref)). """ @inline function instantiate(bc::Broadcasted) if bc.axes isa Nothing # Not done via dispatch to make it easier to extend instantiate(::Broadcasted{Style}) axes = combine_axes(bc.args...) else axes = bc.axes check_broadcast_axes(axes, bc.args...) end return Broadcasted(bc.style, bc.f, bc.args, axes) end instantiate(bc::Broadcasted{<:AbstractArrayStyle{0}}) = bc # Tuples don't need axes, but when they have axes (for .= assignment), we need to check them (#33020) instantiate(bc::Broadcasted{Style{Tuple}, Nothing}) = bc function instantiate(bc::Broadcasted{Style{Tuple}}) check_broadcast_axes(bc.axes, bc.args...) return bc end ## Flattening """ bcf = flatten(bc) Create a "flat" representation of a lazy-broadcast operation. From f.(a, g.(b, c), d) we produce the equivalent of h.(a, b, c, d) where h(w, x, y, z) = f(w, g(x, y), z) In terms of its internal representation, Broadcasted(f, a, Broadcasted(g, b, c), d) becomes Broadcasted(h, a, b, c, d) This is an optional operation that may make custom implementation of broadcasting easier in some cases. """ function flatten(bc::Broadcasted) isflat(bc) && return bc # concatenate the nested arguments into {a, b, c, d} args = cat_nested(bc) # build a function `makeargs` that takes a "flat" argument list and # and creates the appropriate input arguments for `f`, e.g., # makeargs = (w, x, y, z) -> (w, g(x, y), z) # # `makeargs` is built recursively and looks a bit like this: # makeargs(w, x, y, z) = (w, makeargs1(x, y, z)...) # = (w, g(x, y), makeargs2(z)...) # = (w, g(x, y), z) let makeargs = make_makeargs(()->(), bc.args), f = bc.f newf = @inline function(args::Vararg{Any,N}) where N f(makeargs(args...)...) end return Broadcasted(bc.style, newf, args, bc.axes) end end const NestedTuple = Tuple{<:Broadcasted,Vararg{Any}} isflat(bc::Broadcasted) = _isflat(bc.args) _isflat(args::NestedTuple) = false _isflat(args::Tuple) = _isflat(tail(args)) _isflat(args::Tuple{}) = true cat_nested(t::Broadcasted, rest...) = (cat_nested(t.args...)..., cat_nested(rest...)...) cat_nested(t::Any, rest...) = (t, cat_nested(rest...)...) cat_nested() = () """ make_makeargs(makeargs_tail::Function, t::Tuple) -> Function Each element of `t` is one (consecutive) node in a broadcast tree. Ignoring `makeargs_tail` for the moment, the job of `make_makeargs` is to return a function that takes in flattened argument list and returns a tuple (each entry corresponding to an entry in `t`, having evaluated the corresponding element in the broadcast tree). As an additional complication, the passed in tuple may be longer than the number of leaves in the subtree described by `t`. The `makeargs_tail` function should be called on such additional arguments (but not the arguments consumed by `t`). """ @inline make_makeargs(makeargs_tail, t::Tuple{}) = makeargs_tail @inline function make_makeargs(makeargs_tail, t::Tuple) makeargs = make_makeargs(makeargs_tail, tail(t)) (head, tail...)->(head, makeargs(tail...)...) end function make_makeargs(makeargs_tail, t::Tuple{<:Broadcasted, Vararg{Any}}) bc = t[1] # c.f. the same expression in the function on leaf nodes above. Here # we recurse into siblings in the broadcast tree. let makeargs_tail = make_makeargs(makeargs_tail, tail(t)), # Here we recurse into children. It would be valid to pass in makeargs_tail # here, and not use it below. However, in that case, our recursion is no # longer purely structural because we're building up one argument (the closure) # while destructuing another. makeargs_head = make_makeargs((args...)->args, bc.args), f = bc.f # Create two functions, one that splits of the first length(bc.args) # elements from the tuple and one that yields the remaining arguments. # N.B. We can't call headargs on `args...` directly because # args is flattened (i.e. our children have not been evaluated # yet). headargs, tailargs = make_headargs(bc.args), make_tailargs(bc.args) return @inline function(args::Vararg{Any,N}) where N args1 = makeargs_head(args...) a, b = headargs(args1...), makeargs_tail(tailargs(args1...)...) (f(a...), b...) end end end @inline function make_headargs(t::Tuple) let headargs = make_headargs(tail(t)) return @inline function(head, tail::Vararg{Any,N}) where N (head, headargs(tail...)...) end end end @inline function make_headargs(::Tuple{}) return @inline function(tail::Vararg{Any,N}) where N () end end @inline function make_tailargs(t::Tuple) let tailargs = make_tailargs(tail(t)) return @inline function(head, tail::Vararg{Any,N}) where N tailargs(tail...) end end end @inline function make_tailargs(::Tuple{}) return @inline function(tail::Vararg{Any,N}) where N tail end end ## Broadcasting utilities ## ## logic for deciding the BroadcastStyle """ combine_styles(cs...) -> BroadcastStyle Decides which `BroadcastStyle` to use for any number of value arguments. Uses [`BroadcastStyle`](@ref) to get the style for each argument, and uses [`result_style`](@ref) to combine styles. # Examples ```jldoctest julia> Broadcast.combine_styles([1], [1 2; 3 4]) Base.Broadcast.DefaultArrayStyle{2}() ``` """ function combine_styles end combine_styles() = DefaultArrayStyle{0}() combine_styles(c) = result_style(BroadcastStyle(typeof(c))) combine_styles(c1, c2) = result_style(combine_styles(c1), combine_styles(c2)) @inline combine_styles(c1, c2, cs...) = result_style(combine_styles(c1), combine_styles(c2, cs...)) """ result_style(s1::BroadcastStyle[, s2::BroadcastStyle]) -> BroadcastStyle Takes one or two `BroadcastStyle`s and combines them using [`BroadcastStyle`](@ref) to determine a common `BroadcastStyle`. # Examples ```jldoctest julia> Broadcast.result_style(Broadcast.DefaultArrayStyle{0}(), Broadcast.DefaultArrayStyle{3}()) Base.Broadcast.DefaultArrayStyle{3}() julia> Broadcast.result_style(Broadcast.Unknown(), Broadcast.DefaultArrayStyle{1}()) Base.Broadcast.DefaultArrayStyle{1}() ``` """ function result_style end result_style(s::BroadcastStyle) = s result_style(s1::S, s2::S) where S<:BroadcastStyle = S() # Test both orders so users typically only have to declare one order result_style(s1, s2) = result_join(s1, s2, BroadcastStyle(s1, s2), BroadcastStyle(s2, s1)) # result_join is the final arbiter. Because `BroadcastStyle` for undeclared pairs results in Unknown, # we defer to any case where the result of `BroadcastStyle` is known. result_join(::Any, ::Any, ::Unknown, ::Unknown) = Unknown() result_join(::Any, ::Any, ::Unknown, s::BroadcastStyle) = s result_join(::Any, ::Any, s::BroadcastStyle, ::Unknown) = s # For AbstractArray types with specialized broadcasting and undefined precedence rules, # we have to signal conflict. Because ArrayConflict is a subtype of AbstractArray, # this will "poison" any future operations (if we instead returned `DefaultArrayStyle`, then for # 3-array broadcasting the returned type would depend on argument order). result_join(::AbstractArrayStyle, ::AbstractArrayStyle, ::Unknown, ::Unknown) = ArrayConflict() # Fallbacks in case users define `rule` for both argument-orders (not recommended) result_join(::Any, ::Any, ::S, ::S) where S<:BroadcastStyle = S() @noinline function result_join(::S, ::T, ::U, ::V) where {S,T,U,V} error(""" conflicting broadcast rules defined Broadcast.BroadcastStyle(::$S, ::$T) = $U() Broadcast.BroadcastStyle(::$T, ::$S) = $V() One of these should be undefined (and thus return Broadcast.Unknown).""") end # Indices utilities """ combine_axes(As...) -> Tuple Determine the result axes for broadcasting across all values in `As`. ```jldoctest julia> Broadcast.combine_axes([1], [1 2; 3 4; 5 6]) (Base.OneTo(3), Base.OneTo(2)) julia> Broadcast.combine_axes(1, 1, 1) () ``` """ @inline combine_axes(A, B...) = broadcast_shape(axes(A), combine_axes(B...)) @inline combine_axes(A, B) = broadcast_shape(axes(A), axes(B)) combine_axes(A) = axes(A) """ broadcast_shape(As...) -> Tuple Determine the result axes for broadcasting across all axes (size Tuples) in `As`. ```jldoctest julia> Broadcast.broadcast_shape((1,2), (2,1)) (2, 2) julia> Broadcast.broadcast_shape((1,), (1,5), (4,5,3)) (4, 5, 3) ``` """ function broadcast_shape end # shape (i.e., tuple-of-indices) inputs broadcast_shape(shape::Tuple) = shape broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs(shape, shape1), shapes...) # _bcs consolidates two shapes into a single output shape _bcs(::Tuple{}, ::Tuple{}) = () _bcs(::Tuple{}, newshape::Tuple) = (newshape[1], _bcs((), tail(newshape))...) _bcs(shape::Tuple, ::Tuple{}) = (shape[1], _bcs(tail(shape), ())...) function _bcs(shape::Tuple, newshape::Tuple) return (_bcs1(shape[1], newshape[1]), _bcs(tail(shape), tail(newshape))...) end # _bcs1 handles the logic for a single dimension _bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $b")))) _bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $(length(b))"))) _bcs1(a, b::Integer) = _bcs1(b, a) _bcs1(a, b) = _bcsm(b, a) ? axistype(b, a) : (_bcsm(a, b) ? axistype(a, b) : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $(length(a)) and $(length(b))"))) # _bcsm tests whether the second index is consistent with the first _bcsm(a, b) = a == b || length(b) == 1 _bcsm(a, b::Number) = b == 1 _bcsm(a::Number, b::Number) = a == b || b == 1 # Ensure inferrability when dealing with axes of different AbstractUnitRange types # (We may not want to define general promotion rules between, say, OneTo and Slice, but if # we get here we know the axes are at least consistent for the purposes of broadcasting) axistype(a::T, b::T) where T = a axistype(a::OneTo, b::OneTo) = OneTo{Int}(a) axistype(a, b) = UnitRange{Int}(a) ## Check that all arguments are broadcast compatible with shape # comparing one input against a shape check_broadcast_shape(shp) = nothing check_broadcast_shape(shp, ::Tuple{}) = nothing check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing function check_broadcast_shape(::Tuple{}, Ashp::Tuple) if any(ax -> length(ax) != 1, Ashp) throw(DimensionMismatch("cannot broadcast array to have fewer non-singleton dimensions")) end nothing end function check_broadcast_shape(shp, Ashp::Tuple) _bcsm(shp[1], Ashp[1]) || throw(DimensionMismatch("array could not be broadcast to match destination")) check_broadcast_shape(tail(shp), tail(Ashp)) end @inline check_broadcast_axes(shp, A) = check_broadcast_shape(shp, axes(A)) # comparing many inputs @inline function check_broadcast_axes(shp, A, As...) check_broadcast_axes(shp, A) check_broadcast_axes(shp, As...) end ## Indexing manipulations """ newindex(argument, I) newindex(I, keep, default) Recompute index `I` such that it appropriately constrains broadcasted dimensions to the source. Two methods are supported, both allowing for `I` to be specified as either a [`CartesianIndex`](@ref) or an `Int`. * `newindex(argument, I)` dynamically constrains `I` based upon the axes of `argument`. * `newindex(I, keep, default)` constrains `I` using the pre-computed tuples `keeps` and `defaults`. * `keep` is a tuple of `Bool`s, where `keep[d] == true` means that dimension `d` in `I` should be preserved as is * `default` is a tuple of Integers, specifying what index to use in dimension `d` when `keep[d] == false`. Any remaining indices in `I` beyond the length of the `keep` tuple are truncated. The `keep` and `default` tuples may be created by `newindexer(argument)`. """ Base.@propagate_inbounds newindex(arg, I::CartesianIndex) = CartesianIndex(_newindex(axes(arg), I.I)) Base.@propagate_inbounds newindex(arg, I::Integer) = CartesianIndex(_newindex(axes(arg), (I,))) Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple) = (ifelse(length(ax[1]) == 1, ax[1][1], I[1]), _newindex(tail(ax), tail(I))...) Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple) = () Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple{}) = (ax[1][1], _newindex(tail(ax), ())...) Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple{}) = () # If dot-broadcasting were already defined, this would be `ifelse.(keep, I, Idefault)`. @inline newindex(I::CartesianIndex, keep, Idefault) = CartesianIndex(_newindex(I.I, keep, Idefault)) @inline newindex(i::Integer, keep::Tuple, idefault) = ifelse(keep[1], i, idefault[1]) @inline newindex(i::Integer, keep::Tuple{}, idefault) = CartesianIndex(()) @inline _newindex(I, keep, Idefault) = (ifelse(keep[1], I[1], Idefault[1]), _newindex(tail(I), tail(keep), tail(Idefault))...) @inline _newindex(I, keep::Tuple{}, Idefault) = () # truncate if keep is shorter than I @inline _newindex(I::Tuple{}, keep, Idefault) = () # or I is shorter @inline _newindex(I::Tuple{}, keep::Tuple{}, Idefault) = () # or both # newindexer(A) generates `keep` and `Idefault` (for use by `newindex` above) # for a particular array `A`; `shapeindexer` does so for its axes. @inline newindexer(A) = shapeindexer(axes(A)) @inline shapeindexer(ax) = _newindexer(ax) @inline _newindexer(indsA::Tuple{}) = (), () @inline function _newindexer(indsA::Tuple) ind1 = indsA[1] keep, Idefault = _newindexer(tail(indsA)) (Base.length(ind1)::Integer != 1, keep...), (first(ind1), Idefault...) end @inline function Base.getindex(bc::Broadcasted, I::Union{Integer,CartesianIndex}) @boundscheck checkbounds(bc, I) @inbounds _broadcast_getindex(bc, I) end Base.@propagate_inbounds Base.getindex( bc::Broadcasted, i1::Union{Integer,CartesianIndex}, i2::Union{Integer,CartesianIndex}, I::Union{Integer,CartesianIndex}..., ) = bc[CartesianIndex((i1, i2, I...))] Base.@propagate_inbounds Base.getindex(bc::Broadcasted) = bc[CartesianIndex(())] @inline Base.checkbounds(bc::Broadcasted, I::Union{Integer,CartesianIndex}) = Base.checkbounds_indices(Bool, axes(bc), (I,)) || Base.throw_boundserror(bc, (I,)) """ _broadcast_getindex(A, I) Index into `A` with `I`, collapsing broadcasted indices to their singleton indices as appropriate. """ Base.@propagate_inbounds _broadcast_getindex(A::Union{Ref,AbstractArray{<:Any,0},Number}, I) = A[] # Scalar-likes can just ignore all indices Base.@propagate_inbounds _broadcast_getindex(::Ref{Type{T}}, I) where {T} = T # Tuples are statically known to be singleton or vector-like Base.@propagate_inbounds _broadcast_getindex(A::Tuple{Any}, I) = A[1] Base.@propagate_inbounds _broadcast_getindex(A::Tuple, I) = A[I[1]] # Everything else falls back to dynamically dropping broadcasted indices based upon its axes Base.@propagate_inbounds _broadcast_getindex(A, I) = A[newindex(A, I)] # In some cases, it's more efficient to sort out which dimensions should be dropped # ahead of time (often when the size checks aren't able to be lifted out of the loop). # The Extruded struct computes that information ahead of time and stores it as a pair # of tuples to optimize indexing later. This is most commonly needed for `Array` and # other `AbstractArray` subtypes that wrap `Array` and dynamically ask it for its size. struct Extruded{T, K, D} x::T keeps::K # A tuple of booleans, specifying which indices should be passed normally defaults::D # A tuple of integers, specifying the index to use when keeps[i] is false (as defaults[i]) end @inline axes(b::Extruded) = axes(b.x) Base.@propagate_inbounds _broadcast_getindex(b::Extruded, i) = b.x[newindex(i, b.keeps, b.defaults)] extrude(x::AbstractArray) = Extruded(x, newindexer(x)...) extrude(x) = x # For Broadcasted Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Any}, I) args = _getindex(bc.args, I) return _broadcast_getindex_evalf(bc.f, args...) end # Hack around losing Type{T} information in the final args tuple. Julia actually # knows (in `code_typed`) the _value_ of these types, statically displaying them, # but inference is currently skipping inferring the type of the types as they are # transiently placed in a tuple as the argument list is lispily constructed. These # additional methods recover type stability when a `Type` appears in one of the # first two arguments of a function. Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Ref{Type{T}},Vararg{Any}}}, I) where {T} args = _getindex(tail(bc.args), I) return _broadcast_getindex_evalf(bc.f, T, args...) end Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Any,Ref{Type{T}},Vararg{Any}}}, I) where {T} arg1 = _broadcast_getindex(bc.args[1], I) args = _getindex(tail(tail(bc.args)), I) return _broadcast_getindex_evalf(bc.f, arg1, T, args...) end Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Ref{Type{T}},Ref{Type{S}},Vararg{Any}}}, I) where {T,S} args = _getindex(tail(tail(bc.args)), I) return _broadcast_getindex_evalf(bc.f, T, S, args...) end # Utilities for _broadcast_getindex Base.@propagate_inbounds _getindex(args::Tuple, I) = (_broadcast_getindex(args[1], I), _getindex(tail(args), I)...) Base.@propagate_inbounds _getindex(args::Tuple{Any}, I) = (_broadcast_getindex(args[1], I),) Base.@propagate_inbounds _getindex(args::Tuple{}, I) = () @inline _broadcast_getindex_evalf(f::Tf, args::Vararg{Any,N}) where {Tf,N} = f(args...) # not propagate_inbounds """ Broadcast.broadcastable(x) Return either `x` or an object like `x` such that it supports [`axes`](@ref), indexing, and its type supports [`ndims`](@ref). If `x` supports iteration, the returned value should have the same `axes` and indexing behaviors as [`collect(x)`](@ref). If `x` is not an `AbstractArray` but it supports `axes`, indexing, and its type supports `ndims`, then `broadcastable(::typeof(x))` may be implemented to just return itself. Further, if `x` defines its own [`BroadcastStyle`](@ref), then it must define its `broadcastable` method to return itself for the custom style to have any effect. # Examples ```jldoctest julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already support axes and indexing 3-element Vector{Int64}: 1 2 3 julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars Base.RefValue{Type{Int64}}(Int64) julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead Base.RefValue{String}("hello") ``` """ broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair,IO,CartesianIndex}) = Ref(x) broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T) broadcastable(x::Union{AbstractArray,Number,AbstractChar,Ref,Tuple,Broadcasted}) = x # Default to collecting iterables โ€” which will error for non-iterables broadcastable(x) = collect(x) broadcastable(::Union{AbstractDict, NamedTuple}) = throw(ArgumentError("broadcasting over dictionaries and `NamedTuple`s is reserved")) ## Computation of inferred result type, for empty and concretely inferred cases only _broadcast_getindex_eltype(bc::Broadcasted) = combine_eltypes(bc.f, bc.args) _broadcast_getindex_eltype(A) = eltype(A) # Tuple, Array, etc. eltypes(::Tuple{}) = Tuple{} eltypes(t::Tuple{Any}) = Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1])) eltypes(t::Tuple{Any,Any}) = Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1]), _broadcast_getindex_eltype(t[2])) eltypes(t::Tuple) = (TT = eltypes(tail(t)); TT === Union{} ? Union{} : Iterators.TupleOrBottom(_broadcast_getindex_eltype(t[1]), TT.parameters...)) # eltypes(t::Tuple) = Iterators.TupleOrBottom(ntuple(i -> _broadcast_getindex_eltype(t[i]), Val(length(t)))...) # Inferred eltype of result of broadcast(f, args...) function combine_eltypes(f, args::Tuple) argT = eltypes(args) argT === Union{} && return Union{} return promote_typejoin_union(Base._return_type(f, argT)) end ## Broadcasting core """ broadcast(f, As...) Broadcast the function `f` over the arrays, tuples, collections, [`Ref`](@ref)s and/or scalars `As`. Broadcasting applies the function `f` over the elements of the container arguments and the scalars themselves in `As`. Singleton and missing dimensions are expanded to match the extents of the other arguments by virtually repeating the value. By default, only a limited number of types are considered scalars, including `Number`s, `String`s, `Symbol`s, `Type`s, `Function`s and some common singletons like [`missing`](@ref) and [`nothing`](@ref). All other arguments are iterated over or indexed into elementwise. The resulting container type is established by the following rules: - If all the arguments are scalars or zero-dimensional arrays, it returns an unwrapped scalar. - If at least one argument is a tuple and all others are scalars or zero-dimensional arrays, it returns a tuple. - All other combinations of arguments default to returning an `Array`, but custom container types can define their own implementation and promotion-like rules to customize the result when they appear as arguments. A special syntax exists for broadcasting: `f.(args...)` is equivalent to `broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a single broadcast loop. # Examples ```jldoctest julia> A = [1, 2, 3, 4, 5] 5-element Vector{Int64}: 1 2 3 4 5 julia> B = [1 2; 3 4; 5 6; 7 8; 9 10] 5ร—2 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 10 julia> broadcast(+, A, B) 5ร—2 Matrix{Int64}: 2 3 5 6 8 9 11 12 14 15 julia> parse.(Int, ["1", "2"]) 2-element Vector{Int64}: 1 2 julia> abs.((1, -2)) (1, 2) julia> broadcast(+, 1.0, (0, -2.0)) (1.0, -1.0) julia> (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1])) 2-element Vector{Vector{Int64}}: [1, 1] [2, 2] julia> string.(("one","two","three","four"), ": ", 1:4) 4-element Vector{String}: "one: 1" "two: 2" "three: 3" "four: 4" ``` """ broadcast(f::Tf, As...) where {Tf} = materialize(broadcasted(f, As...)) # special cases defined for performance @inline broadcast(f, x::Number...) = f(x...) @inline broadcast(f, t::NTuple{N,Any}, ts::Vararg{NTuple{N,Any}}) where {N} = map(f, t, ts...) """ broadcast!(f, dest, As...) Like [`broadcast`](@ref), but store the result of `broadcast(f, As...)` in the `dest` array. Note that `dest` is only used to store the result, and does not supply arguments to `f` unless it is also listed in the `As`, as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`. # Examples ```jldoctest julia> A = [1.0; 0.0]; B = [0.0; 0.0]; julia> broadcast!(+, B, A, (0, -2.0)); julia> B 2-element Vector{Float64}: 1.0 -2.0 julia> A 2-element Vector{Float64}: 1.0 0.0 julia> broadcast!(+, A, A, (0, -2.0)); julia> A 2-element Vector{Float64}: 1.0 -2.0 ``` """ broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = (materialize!(dest, broadcasted(f, As...)); dest) """ broadcast_preserving_zero_d(f, As...) Like [`broadcast`](@ref), except in the case of a 0-dimensional result where it returns a 0-dimensional container Broadcast automatically unwraps zero-dimensional results to be just the element itself, but in some cases it is necessary to always return a container โ€” even in the 0-dimensional case. """ @inline function broadcast_preserving_zero_d(f, As...) bc = broadcasted(f, As...) r = materialize(bc) return length(axes(bc)) == 0 ? fill!(similar(bc, typeof(r)), r) : r end @inline broadcast_preserving_zero_d(f) = fill(f()) @inline broadcast_preserving_zero_d(f, as::Number...) = fill(f(as...)) """ Broadcast.materialize(bc) Take a lazy `Broadcasted` object and compute the result """ @inline materialize(bc::Broadcasted) = copy(instantiate(bc)) materialize(x) = x @inline function materialize!(dest, x) return materialize!(dest, instantiate(Broadcasted(identity, (x,), axes(dest)))) end @inline function materialize!(dest, bc::Broadcasted{<:Any}) return materialize!(combine_styles(dest, bc), dest, bc) end @inline function materialize!(::BroadcastStyle, dest, bc::Broadcasted{<:Any}) return copyto!(dest, instantiate(Broadcasted(bc.style, bc.f, bc.args, axes(dest)))) end ## general `copy` methods @inline copy(bc::Broadcasted{<:AbstractArrayStyle{0}}) = bc[CartesianIndex()] copy(bc::Broadcasted{<:Union{Nothing,Unknown}}) = throw(ArgumentError("broadcasting requires an assigned BroadcastStyle")) const NonleafHandlingStyles = Union{DefaultArrayStyle,ArrayConflict} @inline function copy(bc::Broadcasted) ElType = combine_eltypes(bc.f, bc.args) if Base.isconcretetype(ElType) # We can trust it and defer to the simpler `copyto!` return copyto!(similar(bc, ElType), bc) end # When ElType is not concrete, use narrowing. Use the first output # value to determine the starting output eltype; copyto_nonleaf! # will widen `dest` as needed to accommodate later values. bcโ€ฒ = preprocess(nothing, bc) iter = eachindex(bcโ€ฒ) y = iterate(iter) if y === nothing # if empty, take the ElType at face value return similar(bcโ€ฒ, ElType) end # Initialize using the first value I, state = y @inbounds val = bcโ€ฒ[I] dest = similar(bcโ€ฒ, typeof(val)) @inbounds dest[I] = val # Now handle the remaining values # The typeassert gives inference a helping hand on the element type and dimensionality # (work-around for #28382) ElTypeโ€ฒ = ElType === Union{} ? Any : ElType <: Type ? Type : ElType RT = dest isa AbstractArray ? AbstractArray{<:ElTypeโ€ฒ, ndims(dest)} : Any return copyto_nonleaf!(dest, bcโ€ฒ, iter, state, 1)::RT end ## general `copyto!` methods # The most general method falls back to a method that replaces Style->Nothing # This permits specialization on typeof(dest) without introducing ambiguities @inline copyto!(dest::AbstractArray, bc::Broadcasted) = copyto!(dest, convert(Broadcasted{Nothing}, bc)) # Performance optimization for the common identity scalar case: dest .= val @inline function copyto!(dest::AbstractArray, bc::Broadcasted{<:AbstractArrayStyle{0}}) # Typically, we must independently execute bc for every storage location in `dest`, but: # IF we're in the common no-op identity case with no nested args (like `dest .= val`), if bc.f === identity && bc.args isa Tuple{Any} && isflat(bc) # THEN we can just extract the argument and `fill!` the destination with it return fill!(dest, bc.args[1][]) else # Otherwise, fall back to the default implementation like above return copyto!(dest, convert(Broadcasted{Nothing}, bc)) end end # For broadcasted assignments like `broadcast!(f, A, ..., A, ...)`, where `A` # appears on both the LHS and the RHS of the `.=`, then we know we're only # going to make one pass through the array, and even though `A` is aliasing # against itself, the mutations won't affect the result as the indices on the # LHS and RHS will always match. This is not true in general, but with the `.op=` # syntax it's fairly common for an argument to be `===` a source. broadcast_unalias(dest, src) = dest === src ? src : unalias(dest, src) broadcast_unalias(::Nothing, src) = src # Preprocessing a `Broadcasted` does two things: # * unaliases any arguments from `dest` # * "extrudes" the arguments where it is advantageous to pre-compute the broadcasted indices @inline preprocess(dest, bc::Broadcasted) = Broadcasted(bc.style, bc.f, preprocess_args(dest, bc.args), bc.axes) preprocess(dest, x) = extrude(broadcast_unalias(dest, x)) @inline preprocess_args(dest, args::Tuple) = (preprocess(dest, args[1]), preprocess_args(dest, tail(args))...) @inline preprocess_args(dest, args::Tuple{Any}) = (preprocess(dest, args[1]),) @inline preprocess_args(dest, args::Tuple{}) = () # Specialize this method if all you want to do is specialize on typeof(dest) @inline function copyto!(dest::AbstractArray, bc::Broadcasted{Nothing}) axes(dest) == axes(bc) || throwdm(axes(dest), axes(bc)) # Performance optimization: broadcast!(identity, dest, A) is equivalent to copyto!(dest, A) if indices match if bc.f === identity && bc.args isa Tuple{AbstractArray} # only a single input argument to broadcast! A = bc.args[1] if axes(dest) == axes(A) return copyto!(dest, A) end end bcโ€ฒ = preprocess(dest, bc) # Performance may vary depending on whether `@inbounds` is placed outside the # for loop or not. (cf. https://github.com/JuliaLang/julia/issues/38086) @inbounds @simd for I in eachindex(bcโ€ฒ) dest[I] = bcโ€ฒ[I] end return dest end # Performance optimization: for BitArray outputs, we cache the result # in a "small" Vector{Bool}, and then copy in chunks into the output @inline function copyto!(dest::BitArray, bc::Broadcasted{Nothing}) axes(dest) == axes(bc) || throwdm(axes(dest), axes(bc)) ischunkedbroadcast(dest, bc) && return chunkedcopyto!(dest, bc) length(dest) < 256 && return invoke(copyto!, Tuple{AbstractArray, Broadcasted{Nothing}}, dest, bc) tmp = Vector{Bool}(undef, bitcache_size) destc = dest.chunks cind = 1 bcโ€ฒ = preprocess(dest, bc) @inbounds for P in Iterators.partition(eachindex(bcโ€ฒ), bitcache_size) ind = 1 @simd for I in P tmp[ind] = bcโ€ฒ[I] ind += 1 end @simd for i in ind:bitcache_size tmp[i] = false end dumpbitcache(destc, cind, tmp) cind += bitcache_chunks end return dest end # For some BitArray operations, we can work at the level of chunks. The trivial # implementation just walks over the UInt64 chunks in a linear fashion. # This requires three things: # 1. The function must be known to work at the level of chunks (or can be converted to do so) # 2. The only arrays involved must be BitArrays or scalar Bools # 3. There must not be any broadcasting beyond scalar โ€” all array sizes must match # We could eventually allow for all broadcasting and other array types, but that # requires very careful consideration of all the edge effects. const ChunkableOp = Union{typeof(&), typeof(|), typeof(xor), typeof(~), typeof(identity), typeof(!), typeof(*), typeof(==)} # these are convertible to chunkable ops by liftfuncs const BroadcastedChunkableOp{Style<:Union{Nothing,BroadcastStyle}, Axes, F<:ChunkableOp, Args<:Tuple} = Broadcasted{Style,Axes,F,Args} ischunkedbroadcast(R, bc::BroadcastedChunkableOp) = ischunkedbroadcast(R, bc.args) ischunkedbroadcast(R, args) = false ischunkedbroadcast(R, args::Tuple{<:BitArray,Vararg{Any}}) = size(R) == size(args[1]) && ischunkedbroadcast(R, tail(args)) ischunkedbroadcast(R, args::Tuple{<:Bool,Vararg{Any}}) = ischunkedbroadcast(R, tail(args)) ischunkedbroadcast(R, args::Tuple{<:BroadcastedChunkableOp,Vararg{Any}}) = ischunkedbroadcast(R, args[1]) && ischunkedbroadcast(R, tail(args)) ischunkedbroadcast(R, args::Tuple{}) = true # Convert compatible functions to chunkable ones. They must also be green-lighted as ChunkableOps liftfuncs(bc::Broadcasted{<:Any,<:Any,<:Any}) = Broadcasted(bc.style, bc.f, map(liftfuncs, bc.args), bc.axes) liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(sign)}) = Broadcasted(bc.style, identity, map(liftfuncs, bc.args), bc.axes) liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(!)}) = Broadcasted(bc.style, ~, map(liftfuncs, bc.args), bc.axes) liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(*)}) = Broadcasted(bc.style, &, map(liftfuncs, bc.args), bc.axes) liftfuncs(bc::Broadcasted{<:Any,<:Any,typeof(==)}) = Broadcasted(bc.style, (~)โˆ˜(xor), map(liftfuncs, bc.args), bc.axes) liftfuncs(x) = x liftchunks(::Tuple{}) = () liftchunks(args::Tuple{<:BitArray,Vararg{Any}}) = (args[1].chunks, liftchunks(tail(args))...) # Transform scalars to repeated scalars the size of a chunk liftchunks(args::Tuple{<:Bool,Vararg{Any}}) = (ifelse(args[1], typemax(UInt64), UInt64(0)), liftchunks(tail(args))...) ithchunk(i) = () Base.@propagate_inbounds ithchunk(i, c::Vector{UInt64}, args...) = (c[i], ithchunk(i, args...)...) Base.@propagate_inbounds ithchunk(i, b::UInt64, args...) = (b, ithchunk(i, args...)...) @inline function chunkedcopyto!(dest::BitArray, bc::Broadcasted) isempty(dest) && return dest f = flatten(liftfuncs(bc)) args = liftchunks(f.args) dc = dest.chunks @simd for i in eachindex(dc) @inbounds dc[i] = f.f(ithchunk(i, args...)...) end @inbounds dc[end] &= Base._msk_end(dest) return dest end @noinline throwdm(axdest, axsrc) = throw(DimensionMismatch("destination axes $axdest are not compatible with source axes $axsrc")) function restart_copyto_nonleaf!(newdest, dest, bc, val, I, iter, state, count) # Function barrier that makes the copying to newdest type stable for II in Iterators.take(iter, count) newdest[II] = dest[II] end newdest[I] = val return copyto_nonleaf!(newdest, bc, iter, state, count+1) end function copyto_nonleaf!(dest, bc::Broadcasted, iter, state, count) T = eltype(dest) while true y = iterate(iter, state) y === nothing && break I, state = y @inbounds val = bc[I] if val isa T @inbounds dest[I] = val else # This element type doesn't fit in dest. Allocate a new dest with wider eltype, # copy over old values, and continue newdest = Base.similar(bc, promote_typejoin(T, typeof(val))) return restart_copyto_nonleaf!(newdest, dest, bc, val, I, iter, state, count) end count += 1 end return dest end ## Tuple methods @inline function copy(bc::Broadcasted{Style{Tuple}}) dim = axes(bc) length(dim) == 1 || throw(DimensionMismatch("tuple only supports one dimension")) N = length(dim[1]) return ntuple(k -> @inbounds(_broadcast_getindex(bc, k)), Val(N)) end ## scalar-range broadcast operations ## # DefaultArrayStyle and \ are not available at the time of range.jl broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange) = r broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange) = range(-first(r), step=negate(step(r)), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), -last(r), step=negate(step(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen) = StepRangeLen(-r.ref, negate(r.step), length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange) = LinRange(-r.start, -r.stop, length(r)) # For #18336 we need to prevent promotion of the step type: broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange, x::Number) = range(first(r) + x, step=step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::AbstractRange) = range(x + first(r), step=step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange, x::Integer) = range(first(r) + x, last(r) + x, step=step(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Integer, r::OrdinalRange) = range(x + first(r), x + last(r), step=step(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Integer) = range(first(r) + x, last(r) + x) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Integer, r::AbstractUnitRange) = range(x + first(r), x + last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)+x)}(r.ref + x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::StepRangeLen{T}) where T = StepRangeLen{typeof(x+T(r.ref))}(x + r.ref, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange, x::Number) = LinRange(r.start + x, r.stop + x, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::LinRange) = LinRange(x + r.start, x + r.stop, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r1::AbstractRange, r2::AbstractRange) = r1 + r2 broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r) - x, step=step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x - first(r), step=negate(step(r)), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange, x::Integer) = range(first(r) - x, last(r) - x, step=step(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Integer, r::OrdinalRange) = range(x - first(r), x - last(r), step=negate(step(r))) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Integer) = range(first(r) - x, last(r) - x) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Real) = range(first(r) - x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen{T}, x::Number) where T = StepRangeLen{typeof(T(r.ref)-x)}(r.ref - x, r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::StepRangeLen{T}) where T = StepRangeLen{typeof(x-T(r.ref))}(x - r.ref, negate(r.step), length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange, x::Number) = LinRange(r.start - x, r.stop - x, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::LinRange) = LinRange(x - r.start, x - r.stop, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r1::AbstractRange, r2::AbstractRange) = r1 - r2 # at present Base.range_start_step_length(1,0,5) is an error, so for 0 .* (-2:2) we explicitly construct StepRangeLen: broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::AbstractRange) = StepRangeLen(x*first(r), x*step(r), length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::StepRangeLen{T}) where {T} = StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::LinRange) = LinRange(x * r.start, x * r.stop, r.len) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::AbstractFloat, r::OrdinalRange) = Base.range_start_step_length(x*first(r), x*step(r), length(r)) # 0.2 .* (-2:2) needs TwicePrecision # separate in case of noncommutative multiplication: broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::AbstractRange, x::Number) = StepRangeLen(first(r)*x, step(r)*x, length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::LinRange, x::Number) = LinRange(r.start * x, r.stop * x, r.len) broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::OrdinalRange, x::AbstractFloat) = Base.range_start_step_length(first(r)*x, step(r)*x, length(r)) #broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, last(r)/x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, step=step(r)/x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::LinRange, x::Number) = LinRange(r.start / x, r.stop / x, r.len) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::AbstractRange) = range(x\first(r), step=x\step(r), length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::StepRangeLen) = StepRangeLen(x\r.ref, x\r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::LinRange) = LinRange(x \ r.start, x \ r.stop, r.len) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::UnitRange) = big(r.start):big(last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRange) = big(r.start):big(r.step):big(last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRangeLen) = StepRangeLen(big(r.ref), big(r.step), length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::LinRange) = LinRange(big(r.start), big(r.stop), length(r)) ## CartesianIndices broadcasted(::typeof(+), I::CartesianIndices{N}, j::CartesianIndex{N}) where N = CartesianIndices(map((rng, offset)->rng .+ offset, I.indices, Tuple(j))) broadcasted(::typeof(+), j::CartesianIndex{N}, I::CartesianIndices{N}) where N = I .+ j broadcasted(::typeof(-), I::CartesianIndices{N}, j::CartesianIndex{N}) where N = CartesianIndices(map((rng, offset)->rng .- offset, I.indices, Tuple(j))) function broadcasted(::typeof(-), j::CartesianIndex{N}, I::CartesianIndices{N}) where N diffrange(offset, rng) = range(offset-last(rng), length=length(rng), step=step(rng)) Iterators.reverse(CartesianIndices(map(diffrange, Tuple(j), I.indices))) end ## In specific instances, we can broadcast masked BitArrays whole chunks at a time # Very intentionally do not support much functionality here: scalar indexing would be O(n) struct BitMaskedBitArray{N,M} parent::BitArray{N} mask::BitArray{M} BitMaskedBitArray{N,M}(parent, mask) where {N,M} = new(parent, mask) end @inline function BitMaskedBitArray(parent::BitArray{N}, mask::BitArray{M}) where {N,M} @boundscheck checkbounds(parent, mask) BitMaskedBitArray{N,M}(parent, mask) end Base.@propagate_inbounds dotview(B::BitArray, i::BitArray) = BitMaskedBitArray(B, i) Base.show(io::IO, B::BitMaskedBitArray) = foreach(arg->show(io, arg), (typeof(B), (B.parent, B.mask))) # Override materialize! to prevent the BitMaskedBitArray from escaping to an overridable method @inline materialize!(B::BitMaskedBitArray, bc::Broadcasted{<:Any,<:Any,typeof(identity),Tuple{Bool}}) = fill!(B, bc.args[1]) @inline materialize!(B::BitMaskedBitArray, bc::Broadcasted{<:Any}) = materialize!(@inbounds(view(B.parent, B.mask)), bc) function Base.fill!(B::BitMaskedBitArray, b::Bool) Bc = B.parent.chunks Ic = B.mask.chunks @inbounds if b for i = 1:length(Bc) Bc[i] |= Ic[i] end else for i = 1:length(Bc) Bc[i] &= ~Ic[i] end end return B end ############################################################ # x[...] .= f.(y...) ---> broadcast!(f, dotview(x, ...), y...). # The dotview function defaults to getindex, but we override it in # a few cases to get the expected in-place behavior without affecting # explicit calls to view. (All of this can go away if slices # are changed to generate views by default.) Base.@propagate_inbounds dotview(args...) = Base.maybeview(args...) ############################################################ # The parser turns @. into a call to the __dot__ macro, # which converts all function calls and assignments into # broadcasting "dot" calls/assignments: dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) # don't add dots to dot operators dottable(x::Symbol) = (!isoperator(x) || first(string(x)) != '.' || x === :..) && x !== :(:) dottable(x::Expr) = x.head !== :$ undot(x) = x function undot(x::Expr) if x.head === :.= Expr(:(=), x.args...) elseif x.head === :block # occurs in for x=..., y=... Expr(:block, Base.mapany(undot, x.args)...) else x end end __dot__(x) = x function __dot__(x::Expr) dotargs = Base.mapany(__dot__, x.args) if x.head === :call && dottable(x.args[1]) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) elseif x.head === :comparison Expr(:comparison, (iseven(i) && dottable(arg) && arg isa Symbol && isoperator(arg) ? Symbol('.', arg) : arg for (i, arg) in pairs(dotargs))...) elseif x.head === :$ x.args[1] elseif x.head === :let # don't add dots to `let x=...` assignments Expr(:let, undot(dotargs[1]), dotargs[2]) elseif x.head === :for # don't add dots to for x=... assignments Expr(:for, undot(dotargs[1]), dotargs[2]) elseif (x.head === :(=) || x.head === :function || x.head === :macro) && Meta.isexpr(x.args[1], :call) # function or macro definition Expr(x.head, x.args[1], dotargs[2]) elseif x.head === :(<:) || x.head === :(>:) tmp = x.head === :(<:) ? :.<: : :.>: Expr(:call, tmp, dotargs...) else head = String(x.head)::String if last(head) == '=' && first(head) != '.' || head == "&&" || head == "||" Expr(Symbol('.', head), dotargs...) else Expr(x.head, dotargs...) end end end """ @. expr Convert every function call or operator in `expr` into a "dot call" (e.g. convert `f(x)` to `f.(x)`), and convert every assignment in `expr` to a "dot assignment" (e.g. convert `+=` to `.+=`). If you want to *avoid* adding dots for selected function calls in `expr`, splice those function calls in with `\$`. For example, `@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))` (no dot for `sort`). (`@.` is equivalent to a call to `@__dot__`.) # Examples ```jldoctest julia> x = 1.0:3.0; y = similar(x); julia> @. y = x + 3 * sin(x) 3-element Vector{Float64}: 3.5244129544236893 4.727892280477045 3.4233600241796016 ``` """ macro __dot__(x) esc(__dot__(x)) end @inline function broadcasted_kwsyntax(f, args...; kwargs...) if isempty(kwargs) # some BroadcastStyles dispatch on `f`, so try to preserve its type return broadcasted(f, args...) else return broadcasted((args...) -> f(args...; kwargs...), args...) end end @inline function broadcasted(f::F, args...) where {F} argsโ€ฒ = map(broadcastable, args) broadcasted(combine_styles(argsโ€ฒ...), f, argsโ€ฒ...) end # Due to the current Type{T}/DataType specialization heuristics within Tuples, # the totally generic varargs broadcasted(f, args...) method above loses Type{T}s in # mapping broadcastable across the args. These additional methods with explicit # arguments ensure we preserve Type{T}s in the first or second argument position. @inline function broadcasted(f::F, arg1, args...) where {F} arg1โ€ฒ = broadcastable(arg1) argsโ€ฒ = map(broadcastable, args) broadcasted(combine_styles(arg1โ€ฒ, argsโ€ฒ...), f, arg1โ€ฒ, argsโ€ฒ...) end @inline function broadcasted(f::F, arg1, arg2, args...) where {F} arg1โ€ฒ = broadcastable(arg1) arg2โ€ฒ = broadcastable(arg2) argsโ€ฒ = map(broadcastable, args) broadcasted(combine_styles(arg1โ€ฒ, arg2โ€ฒ, argsโ€ฒ...), f, arg1โ€ฒ, arg2โ€ฒ, argsโ€ฒ...) end @inline broadcasted(style::BroadcastStyle, f::F, args...) where {F} = Broadcasted(style, f, args) """ BroadcastFunction{F} <: Function Represents the "dotted" version of an operator, which broadcasts the operator over its arguments, so `BroadcastFunction(op)` is functionally equivalent to `(x...) -> (op).(x...)`. Can be created by just passing an operator preceded by a dot to a higher-order function. # Examples ```jldoctest julia> a = [[1 3; 2 4], [5 7; 6 8]]; julia> b = [[9 11; 10 12], [13 15; 14 16]]; julia> map(.*, a, b) 2-element Vector{Matrix{Int64}}: [9 33; 20 48] [65 105; 84 128] julia> Base.BroadcastFunction(+)(a, b) == a .+ b true ``` !!! compat "Julia 1.6" `BroadcastFunction` and the standalone `.op` syntax are available as of Julia 1.6. """ struct BroadcastFunction{F} <: Function f::F end @inline (op::BroadcastFunction)(x...; kwargs...) = op.f.(x...; kwargs...) function Base.show(io::IO, op::BroadcastFunction) print(io, BroadcastFunction, '(') show(io, op.f) print(io, ')') nothing end Base.show(io::IO, ::MIME"text/plain", op::BroadcastFunction) = show(io, op) end # module N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/missing.jl7# This file is a part of Julia. License is MIT: https://julialang.org/license # Missing, missing and ismissing are defined in essentials.jl show(io::IO, x::Missing) = print(io, "missing") """ MissingException(msg) Exception thrown when a [`missing`](@ref) value is encountered in a situation where it is not supported. The error message, in the `msg` field may provide more specific details. """ struct MissingException <: Exception msg::AbstractString end showerror(io::IO, ex::MissingException) = print(io, "MissingException: ", ex.msg) """ nonmissingtype(T::Type) If `T` is a union of types containing `Missing`, return a new type with `Missing` removed. # Examples ```jldoctest julia> nonmissingtype(Union{Int64,Missing}) Int64 julia> nonmissingtype(Any) Any ``` !!! compat "Julia 1.3" This function is exported as of Julia 1.3. """ nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing) function nonmissingtype_checked(T::Type) R = nonmissingtype(T) R >: T && error("could not compute non-missing type") R <: Union{} && error("cannot convert a value to missing for assignment") return R end promote_rule(T::Type{Missing}, S::Type) = Union{S, Missing} promote_rule(T::Type{Union{Nothing, Missing}}, S::Type) = Union{S, Nothing, Missing} function promote_rule(T::Type{>:Union{Nothing, Missing}}, S::Type) R = nonnothingtype(T) R >: T && return Any T = R R = nonmissingtype(T) R >: T && return Any T = R R = promote_type(T, S) return Union{R, Nothing, Missing} end function promote_rule(T::Type{>:Missing}, S::Type) R = nonmissingtype(T) R >: T && return Any T = R R = promote_type(T, S) return Union{R, Missing} end convert(::Type{T}, x::T) where {T>:Missing} = x convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) # Comparison operators ==(::Missing, ::Missing) = missing ==(::Missing, ::Any) = missing ==(::Any, ::Missing) = missing # To fix ambiguity ==(::Missing, ::WeakRef) = missing ==(::WeakRef, ::Missing) = missing isequal(::Missing, ::Missing) = true isequal(::Missing, ::Any) = false isequal(::Any, ::Missing) = false <(::Missing, ::Missing) = missing <(::Missing, ::Any) = missing <(::Any, ::Missing) = missing isless(::Missing, ::Missing) = false isless(::Missing, ::Any) = false isless(::Any, ::Missing) = true isapprox(::Missing, ::Missing; kwargs...) = missing isapprox(::Missing, ::Any; kwargs...) = missing isapprox(::Any, ::Missing; kwargs...) = missing # Unary operators/functions for f in (:(!), :(~), :(+), :(-), :(*), :(&), :(|), :(xor), :(zero), :(one), :(oneunit), :(isfinite), :(isinf), :(isodd), :(isinteger), :(isreal), :(isnan), :(iszero), :(transpose), :(adjoint), :(float), :(complex), :(conj), :(abs), :(abs2), :(iseven), :(ispow2), :(real), :(imag), :(sign), :(inv)) @eval ($f)(::Missing) = missing end for f in (:(Base.zero), :(Base.one), :(Base.oneunit)) @eval ($f)(::Type{Missing}) = missing @eval function $(f)(::Type{Union{T, Missing}}) where T T === Any && throw(MethodError($f, (Any,))) # To prevent StackOverflowError $f(T) end end for f in (:(Base.float), :(Base.complex)) @eval $f(::Type{Missing}) = Missing @eval function $f(::Type{Union{T, Missing}}) where T T === Any && throw(MethodError($f, (Any,))) # To prevent StackOverflowError Union{$f(T), Missing} end end # Binary operators/functions for f in (:(+), :(-), :(*), :(/), :(^), :(mod), :(rem)) @eval begin # Scalar with missing ($f)(::Missing, ::Missing) = missing ($f)(::Missing, ::Number) = missing ($f)(::Number, ::Missing) = missing end end div(::Missing, ::Missing, r::RoundingMode) = missing div(::Missing, ::Number, r::RoundingMode) = missing div(::Number, ::Missing, r::RoundingMode) = missing min(::Missing, ::Missing) = missing min(::Missing, ::Any) = missing min(::Any, ::Missing) = missing max(::Missing, ::Missing) = missing max(::Missing, ::Any) = missing max(::Any, ::Missing) = missing missing_conversion_msg(@nospecialize T) = LazyString("cannot convert a missing value to type ", T, ": use Union{", T, ", Missing} instead") # Rounding and related functions round(::Missing, ::RoundingMode=RoundNearest; sigdigits::Integer=0, digits::Integer=0, base::Integer=0) = missing round(::Type{>:Missing}, ::Missing, ::RoundingMode=RoundNearest) = missing round(::Type{T}, ::Missing, ::RoundingMode=RoundNearest) where {T} = throw(MissingException(missing_conversion_msg(T))) round(::Type{T}, x::Any, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r) # to fix ambiguities round(::Type{T}, x::Rational{Tr}, r::RoundingMode=RoundNearest) where {T>:Missing,Tr} = round(nonmissingtype_checked(T), x, r) round(::Type{T}, x::Rational{Bool}, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r) # Handle ceil, floor, and trunc separately as they have no RoundingMode argument for f in (:(ceil), :(floor), :(trunc)) @eval begin ($f)(::Missing; sigdigits::Integer=0, digits::Integer=0, base::Integer=0) = missing ($f)(::Type{>:Missing}, ::Missing) = missing ($f)(::Type{T}, ::Missing) where {T} = throw(MissingException(missing_conversion_msg(T))) ($f)(::Type{T}, x::Any) where {T>:Missing} = $f(nonmissingtype_checked(T), x) # to fix ambiguities ($f)(::Type{T}, x::Rational) where {T>:Missing} = $f(nonmissingtype_checked(T), x) end end # to avoid ambiguity warnings (^)(::Missing, ::Integer) = missing # Bit operators (&)(::Missing, ::Missing) = missing (&)(a::Missing, b::Bool) = ifelse(b, missing, false) (&)(b::Bool, a::Missing) = ifelse(b, missing, false) (&)(::Missing, ::Integer) = missing (&)(::Integer, ::Missing) = missing (|)(::Missing, ::Missing) = missing (|)(a::Missing, b::Bool) = ifelse(b, true, missing) (|)(b::Bool, a::Missing) = ifelse(b, true, missing) (|)(::Missing, ::Integer) = missing (|)(::Integer, ::Missing) = missing xor(::Missing, ::Missing) = missing xor(a::Missing, b::Bool) = missing xor(b::Bool, a::Missing) = missing xor(::Missing, ::Integer) = missing xor(::Integer, ::Missing) = missing *(d::Missing, x::Union{AbstractString,AbstractChar}) = missing *(d::Union{AbstractString,AbstractChar}, x::Missing) = missing function float(A::AbstractArray{Union{T, Missing}}) where {T} U = typeof(float(zero(T))) convert(AbstractArray{Union{U, Missing}}, A) end float(A::AbstractArray{Missing}) = A """ skipmissing(itr) Return an iterator over the elements in `itr` skipping [`missing`](@ref) values. The returned object can be indexed using indices of `itr` if the latter is indexable. Indices corresponding to missing values are not valid: they are skipped by [`keys`](@ref) and [`eachindex`](@ref), and a `MissingException` is thrown when trying to use them. Use [`collect`](@ref) to obtain an `Array` containing the non-`missing` values in `itr`. Note that even if `itr` is a multidimensional array, the result will always be a `Vector` since it is not possible to remove missings while preserving dimensions of the input. See also [`coalesce`](@ref), [`ismissing`](@ref), [`something`](@ref). # Examples ```jldoctest julia> x = skipmissing([1, missing, 2]) skipmissing(Union{Missing, Int64}[1, missing, 2]) julia> sum(x) 3 julia> x[1] 1 julia> x[2] ERROR: MissingException: the value at index (2,) is missing [...] julia> argmax(x) 3 julia> collect(keys(x)) 2-element Vector{Int64}: 1 3 julia> collect(skipmissing([1, missing, 2])) 2-element Vector{Int64}: 1 2 julia> collect(skipmissing([1 missing; 2 missing])) 2-element Vector{Int64}: 1 2 ``` """ skipmissing(itr) = SkipMissing(itr) struct SkipMissing{T} x::T end IteratorSize(::Type{<:SkipMissing}) = SizeUnknown() IteratorEltype(::Type{SkipMissing{T}}) where {T} = IteratorEltype(T) eltype(::Type{SkipMissing{T}}) where {T} = nonmissingtype(eltype(T)) function iterate(itr::SkipMissing, state...) y = iterate(itr.x, state...) y === nothing && return nothing item, state = y while item === missing y = iterate(itr.x, state) y === nothing && return nothing item, state = y end item, state end IndexStyle(::Type{<:SkipMissing{T}}) where {T} = IndexStyle(T) eachindex(itr::SkipMissing) = Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, eachindex(itr.x)) keys(itr::SkipMissing) = Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, keys(itr.x)) @propagate_inbounds function getindex(itr::SkipMissing, I...) v = itr.x[I...] v === missing && throw(MissingException(LazyString("the value at index ", I, " is missing"))) v end function show(io::IO, s::SkipMissing) print(io, "skipmissing(") show(io, s.x) print(io, ')') end # Optimized mapreduce implementation # The generic method is faster when !(eltype(A) >: Missing) since it does not need # additional loops to identify the two first non-missing values of each block mapreduce(f, op, itr::SkipMissing{<:AbstractArray}) = _mapreduce(f, op, IndexStyle(itr.x), eltype(itr.x) >: Missing ? itr : itr.x) function _mapreduce(f, op, ::IndexLinear, itr::SkipMissing{<:AbstractArray}) A = itr.x ai = missing inds = LinearIndices(A) i = first(inds) ilast = last(inds) for outer i in i:ilast @inbounds ai = A[i] ai !== missing && break end ai === missing && return mapreduce_empty(f, op, eltype(itr)) a1::eltype(itr) = ai i == typemax(typeof(i)) && return mapreduce_first(f, op, a1) i += 1 ai = missing for outer i in i:ilast @inbounds ai = A[i] ai !== missing && break end ai === missing && return mapreduce_first(f, op, a1) # We know A contains at least two non-missing entries: the result cannot be nothing something(mapreduce_impl(f, op, itr, first(inds), last(inds))) end _mapreduce(f, op, ::IndexCartesian, itr::SkipMissing) = mapfoldl(f, op, itr) mapreduce_impl(f, op, A::SkipMissing, ifirst::Integer, ilast::Integer) = mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) # Returns nothing when the input contains only missing values, and Some(x) otherwise @noinline function mapreduce_impl(f, op, itr::SkipMissing{<:AbstractArray}, ifirst::Integer, ilast::Integer, blksize::Int) A = itr.x if ifirst > ilast return nothing elseif ifirst == ilast @inbounds a1 = A[ifirst] if a1 === missing return nothing else return Some(mapreduce_first(f, op, a1)) end elseif ilast - ifirst < blksize # sequential portion ai = missing i = ifirst for outer i in i:ilast @inbounds ai = A[i] ai !== missing && break end ai === missing && return nothing a1 = ai::eltype(itr) i == typemax(typeof(i)) && return Some(mapreduce_first(f, op, a1)) i += 1 ai = missing for outer i in i:ilast @inbounds ai = A[i] ai !== missing && break end ai === missing && return Some(mapreduce_first(f, op, a1)) a2 = ai::eltype(itr) i == typemax(typeof(i)) && return Some(op(f(a1), f(a2))) i += 1 v = op(f(a1), f(a2)) @simd for i = i:ilast @inbounds ai = A[i] if ai !== missing v = op(v, f(ai)) end end return Some(v) else # pairwise portion imid = ifirst + (ilast - ifirst) >> 1 v1 = mapreduce_impl(f, op, itr, ifirst, imid, blksize) v2 = mapreduce_impl(f, op, itr, imid+1, ilast, blksize) if v1 === nothing && v2 === nothing return nothing elseif v1 === nothing return v2 elseif v2 === nothing return v1 else return Some(op(something(v1), something(v2))) end end end """ filter(f, itr::SkipMissing{<:AbstractArray}) Return a vector similar to the array wrapped by the given `SkipMissing` iterator but with all missing elements and those for which `f` returns `false` removed. !!! compat "Julia 1.2" This method requires Julia 1.2 or later. # Examples ```jldoctest julia> x = [1 2; missing 4] 2ร—2 Matrix{Union{Missing, Int64}}: 1 2 missing 4 julia> filter(isodd, skipmissing(x)) 1-element Vector{Int64}: 1 ``` """ function filter(f, itr::SkipMissing{<:AbstractArray}) y = similar(itr.x, eltype(itr), 0) for xi in itr.x if xi !== missing && f(xi) push!(y, xi) end end y end """ coalesce(x...) Return the first value in the arguments which is not equal to [`missing`](@ref), if any. Otherwise return `missing`. See also [`skipmissing`](@ref), [`something`](@ref), [`@coalesce`](@ref). # Examples ```jldoctest julia> coalesce(missing, 1) 1 julia> coalesce(1, missing) 1 julia> coalesce(nothing, 1) # returns `nothing` julia> coalesce(missing, missing) missing ``` """ function coalesce end coalesce() = missing coalesce(x::Missing, y...) = coalesce(y...) coalesce(x::Any, y...) = x """ @coalesce(x...) Short-circuiting version of [`coalesce`](@ref). # Examples ```jldoctest julia> f(x) = (println("f(\$x)"); missing); julia> a = 1; julia> a = @coalesce a f(2) f(3) error("`a` is still missing") 1 julia> b = missing; julia> b = @coalesce b f(2) f(3) error("`b` is still missing") f(2) f(3) ERROR: `b` is still missing [...] ``` !!! compat "Julia 1.7" This macro is available as of Julia 1.7. """ macro coalesce(args...) expr = :(missing) for arg in reverse(args) expr = :((val = $arg) !== missing ? val : $expr) end return esc(:(let val; $expr; end)) end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/version.jl*-# This file is a part of Julia. License is MIT: https://julialang.org/license ## semantic version numbers (https://semver.org/) const VerTuple = Tuple{Vararg{Union{UInt64,String}}} const VInt = UInt32 """ VersionNumber Version number type which follows the specifications of [semantic versioning (semver)](https://semver.org/), composed of major, minor and patch numeric values, followed by pre-release and build alpha-numeric annotations. `VersionNumber` objects can be compared with all of the standard comparison operators (`==`, `<`, `<=`, etc.), with the result following semver rules. See also [`@v_str`](@ref) to efficiently construct `VersionNumber` objects from semver-format literal strings, [`VERSION`](@ref) for the `VersionNumber` of Julia itself, and [Version Number Literals](@ref man-version-number-literals) in the manual. # Examples ```jldoctest julia> a = VersionNumber(1, 2, 3) v"1.2.3" julia> a >= v"1.2" true julia> b = VersionNumber("2.0.1-rc1") v"2.0.1-rc1" julia> b >= v"2.0.1" false ``` """ struct VersionNumber major::VInt minor::VInt patch::VInt prerelease::VerTuple build::VerTuple function VersionNumber(major::VInt, minor::VInt, patch::VInt, pre::VerTuple, bld::VerTuple) major >= 0 || throw(ArgumentError("invalid negative major version: $major")) minor >= 0 || throw(ArgumentError("invalid negative minor version: $minor")) patch >= 0 || throw(ArgumentError("invalid negative patch version: $patch")) for ident in pre if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative pre-release identifier: $ident")) else if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || isempty(ident) && !(length(pre)==1 && isempty(bld)) throw(ArgumentError("invalid pre-release identifier: $(repr(ident))")) end end end for ident in bld if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative build identifier: $ident")) else if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || isempty(ident) && length(bld)!=1 throw(ArgumentError("invalid build identifier: $(repr(ident))")) end end end new(major, minor, patch, pre, bld) end end VersionNumber(major::Integer, minor::Integer = 0, patch::Integer = 0, pre::Tuple{Vararg{Union{Integer,AbstractString}}} = (), bld::Tuple{Vararg{Union{Integer,AbstractString}}} = ()) = VersionNumber(VInt(major), VInt(minor), VInt(patch), map(x->x isa Integer ? UInt64(x) : String(x), pre), map(x->x isa Integer ? UInt64(x) : String(x), bld)) VersionNumber(v::Tuple) = VersionNumber(v...) VersionNumber(v::VersionNumber) = v function print(io::IO, v::VersionNumber) v == typemax(VersionNumber) && return print(io, "โˆž") print(io, v.major) print(io, '.') print(io, v.minor) print(io, '.') print(io, v.patch) if !isempty(v.prerelease) print(io, '-') join(io, v.prerelease,'.') end if !isempty(v.build) print(io, '+') join(io, v.build,'.') end end show(io::IO, v::VersionNumber) = print(io, "v\"", v, "\"") Broadcast.broadcastable(v::VersionNumber) = Ref(v) const VERSION_REGEX = r"^ v? # prefix (optional) (\d+) # major (required) (?:\.(\d+))? # minor (optional) (?:\.(\d+))? # patch (optional) (?:(-)| # pre-release (optional) ([a-z][0-9a-z-]*(?:\.[0-9a-z-]+)*|-(?:[0-9a-z-]+\.)*[0-9a-z-]+)? (?:(\+)| (?:\+((?:[0-9a-z-]+\.)*[0-9a-z-]+))? # build (optional) )) $"ix function split_idents(s::AbstractString) idents = eachsplit(s, '.') pidents = Union{UInt64,String}[occursin(r"^\d+$", ident) ? parse(UInt64, ident) : String(ident) for ident in idents] return tuple(pidents...)::VerTuple end function tryparse(::Type{VersionNumber}, v::AbstractString) v == "โˆž" && return typemax(VersionNumber) m = match(VERSION_REGEX, String(v)::String) m === nothing && return nothing major, minor, patch, minus, prerl, plus, build = m.captures major = parse(VInt, major::AbstractString) minor = minor !== nothing ? parse(VInt, minor) : VInt(0) patch = patch !== nothing ? parse(VInt, patch) : VInt(0) if prerl !== nothing && !isempty(prerl) && prerl[1] == '-' prerl = prerl[2:end] # strip leading '-' end prerl = prerl !== nothing ? split_idents(prerl) : minus !== nothing ? ("",) : () build = build !== nothing ? split_idents(build) : plus !== nothing ? ("",) : () return VersionNumber(major, minor, patch, prerl::VerTuple, build::VerTuple) end function parse(::Type{VersionNumber}, v::AbstractString) ver = tryparse(VersionNumber, v) ver === nothing && throw(ArgumentError("invalid version string: $v")) return ver end VersionNumber(v::AbstractString) = parse(VersionNumber, v) """ @v_str String macro used to parse a string to a [`VersionNumber`](@ref). # Examples ```jldoctest julia> v"1.2.3" v"1.2.3" julia> v"2.0.1-rc1" v"2.0.1-rc1" ``` """ macro v_str(v); VersionNumber(v); end function typemax(::Type{VersionNumber}) โˆž = typemax(VInt) VersionNumber(โˆž, โˆž, โˆž, (), ("",)) end typemin(::Type{VersionNumber}) = v"0-" ident_cmp(a::Integer, b::Integer) = cmp(a, b) ident_cmp(a::Integer, b::String ) = isempty(b) ? +1 : -1 ident_cmp(a::String, b::Integer) = isempty(a) ? -1 : +1 ident_cmp(a::String, b::String ) = cmp(a, b) function ident_cmp(A::VerTuple, B::VerTuple) for (a, b) in Iterators.Zip{Tuple{VerTuple,VerTuple}}((A, B)) c = ident_cmp(a, b) (c != 0) && return c end length(A) < length(B) ? -1 : length(B) < length(A) ? +1 : 0 end function ==(a::VersionNumber, b::VersionNumber) (a.major != b.major) && return false (a.minor != b.minor) && return false (a.patch != b.patch) && return false (ident_cmp(a.prerelease, b.prerelease) != 0) && return false (ident_cmp(a.build, b.build) != 0) && return false return true end issupbuild(v::VersionNumber) = length(v.build)==1 && isempty(v.build[1]) function isless(a::VersionNumber, b::VersionNumber) (a.major < b.major) && return true (a.major > b.major) && return false (a.minor < b.minor) && return true (a.minor > b.minor) && return false (a.patch < b.patch) && return true (a.patch > b.patch) && return false (!isempty(a.prerelease) && isempty(b.prerelease)) && return true (isempty(a.prerelease) && !isempty(b.prerelease)) && return false c = ident_cmp(a.prerelease,b.prerelease) (c < 0) && return true (c > 0) && return false (!issupbuild(a) && issupbuild(b)) && return true (issupbuild(a) && !issupbuild(b)) && return false c = ident_cmp(a.build,b.build) (c < 0) && return true return false end function hash(v::VersionNumber, h::UInt) h += 0x8ff4ffdb75f9fede % UInt h = hash(v.major, h) h = hash(v.minor, h) h = hash(v.patch, h) h = hash(v.prerelease, ~h) h = hash(v.build, ~h) end lowerbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, ("",), ()) upperbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, (), ("",)) thispatch(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch) thisminor(v::VersionNumber) = VersionNumber(v.major, v.minor, 0) thismajor(v::VersionNumber) = VersionNumber(v.major, 0, 0) nextpatch(v::VersionNumber) = v < thispatch(v) ? thispatch(v) : VersionNumber(v.major, v.minor, v.patch+1) nextminor(v::VersionNumber) = v < thisminor(v) ? thisminor(v) : VersionNumber(v.major, v.minor+1, 0) nextmajor(v::VersionNumber) = v < thismajor(v) ? thismajor(v) : VersionNumber(v.major+1, 0, 0) ## julia version info """ VERSION A [`VersionNumber`](@ref) object describing which version of Julia is in use. See also [Version Number Literals](@ref man-version-number-literals). """ const VERSION = try ver = VersionNumber(VERSION_STRING) if !isempty(ver.prerelease) && !GIT_VERSION_INFO.tagged_commit if GIT_VERSION_INFO.build_number >= 0 ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., GIT_VERSION_INFO.build_number), ver.build) else println("WARNING: no build number found for pre-release version") ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., "unknown"), ver.build) end elseif GIT_VERSION_INFO.build_number > 0 println("WARNING: ignoring non-zero build number for VERSION") end ver catch e println("while creating Base.VERSION, ignoring error $e") VersionNumber(0) end const libllvm_version = if endswith(libllvm_version_string, "jl") # strip the "jl" SONAME suffix (JuliaLang/julia#33058) # (LLVM does never report a prerelease version anyway) VersionNumber(libllvm_version_string[1:end-2]) else VersionNumber(libllvm_version_string) end libllvm_path() = ccall(:jl_get_libllvm, Any, ()) function banner(io::IO = stdout) if GIT_VERSION_INFO.tagged_commit commit_string = TAGGED_RELEASE_BANNER elseif isempty(GIT_VERSION_INFO.commit) commit_string = "" else days = Int(floor((ccall(:jl_clock_now, Float64, ()) - GIT_VERSION_INFO.fork_master_timestamp) / (60 * 60 * 24))) days = max(0, days) unit = days == 1 ? "day" : "days" distance = GIT_VERSION_INFO.fork_master_distance commit = GIT_VERSION_INFO.commit_short if distance == 0 commit_string = "Commit $(commit) ($(days) $(unit) old master)" else branch = GIT_VERSION_INFO.branch commit_string = "$(branch)/$(commit) (fork: $(distance) commits, $(days) $(unit))" end end commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : " ($(split(Base.GIT_VERSION_INFO.date_string)[1]))" if get(io, :color, false)::Bool c = text_colors tx = c[:normal] # text jl = c[:normal] # julia d1 = c[:bold] * c[:blue] # first dot d2 = c[:bold] * c[:red] # second dot d3 = c[:bold] * c[:green] # third dot d4 = c[:bold] * c[:magenta] # fourth dot print(io,""" $(d3)_$(tx) $(d1)_$(tx) $(jl)_$(tx) $(d2)_$(d3)(_)$(d4)_$(tx) | Documentation: https://docs.julialang.org $(d1)(_)$(jl) | $(d2)(_)$(tx) $(d4)(_)$(tx) | $(jl)_ _ _| |_ __ _$(tx) | Type \"?\" for help, \"]?\" for Pkg help. $(jl)| | | | | | |/ _` |$(tx) | $(jl)| | |_| | | | (_| |$(tx) | Version $(VERSION)$(commit_date) $(jl)_/ |\\__'_|_|_|\\__'_|$(tx) | $(commit_string) $(jl)|__/$(tx) | """) else print(io,""" _ _ _ _(_)_ | Documentation: https://docs.julialang.org (_) | (_) (_) | _ _ _| |_ __ _ | Type \"?\" for help, \"]?\" for Pkg help. | | | | | | |/ _` | | | | |_| | | | (_| | | Version $(VERSION)$(commit_date) _/ |\\__'_|_|_|\\__'_| | $(commit_string) |__/ | """) end end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/sysinfo.jlMD# This file is a part of Julia. License is MIT: https://julialang.org/license module Sys @doc """ Provide methods for retrieving information about hardware and the operating system. """ Sys export BINDIR, STDLIB, CPU_THREADS, CPU_NAME, WORD_SIZE, ARCH, MACHINE, KERNEL, JIT, cpu_info, cpu_summary, uptime, loadavg, free_memory, total_memory, free_physical_memory, total_physical_memory, isapple, isbsd, isdragonfly, isfreebsd, islinux, isnetbsd, isopenbsd, isunix, iswindows, isjsvm, isexecutable, which import ..Base: show """ Sys.BINDIR::String A string containing the full path to the directory containing the `julia` executable. """ global BINDIR::String = ccall(:jl_get_julia_bindir, Any, ())::String """ Sys.STDLIB::String A string containing the full path to the directory containing the `stdlib` packages. """ global STDLIB::String = "$BINDIR/../share/julia/stdlib/v$(VERSION.major).$(VERSION.minor)" # for bootstrap # In case STDLIB change after julia is built, the variable below can be used # to update cached method locations to updated ones. const BUILD_STDLIB_PATH = STDLIB # helper to avoid triggering precompile warnings """ Sys.CPU_THREADS::Int The number of logical CPU cores available in the system, i.e. the number of threads that the CPU can run concurrently. Note that this is not necessarily the number of CPU cores, for example, in the presence of [hyper-threading](https://en.wikipedia.org/wiki/Hyper-threading). See Hwloc.jl or CpuId.jl for extended information, including number of physical cores. """ global CPU_THREADS::Int = 1 # for bootstrap, changed on startup """ Sys.ARCH::Symbol A symbol representing the architecture of the build configuration. """ const ARCH = ccall(:jl_get_ARCH, Any, ())::Symbol """ Sys.KERNEL::Symbol A symbol representing the name of the operating system, as returned by `uname` of the build configuration. """ const KERNEL = ccall(:jl_get_UNAME, Any, ())::Symbol """ Sys.MACHINE::String A string containing the build triple. """ const MACHINE = Base.MACHINE::String """ Sys.WORD_SIZE::Int Standard word size on the current machine, in bits. """ const WORD_SIZE = Core.sizeof(Int) * 8 global SC_CLK_TCK::Clong, CPU_NAME::String, JIT::String function __init__() env_threads = nothing if haskey(ENV, "JULIA_CPU_THREADS") env_threads = ENV["JULIA_CPU_THREADS"] end global CPU_THREADS = if env_threads !== nothing env_threads = tryparse(Int, env_threads) if !(env_threads isa Int && env_threads > 0) env_threads = Int(ccall(:jl_cpu_threads, Int32, ())) Core.print(Core.stderr, "WARNING: couldn't parse `JULIA_CPU_THREADS` environment variable. Defaulting Sys.CPU_THREADS to $env_threads.\n") end env_threads else Int(ccall(:jl_cpu_threads, Int32, ())) end global SC_CLK_TCK = ccall(:jl_SC_CLK_TCK, Clong, ()) global CPU_NAME = ccall(:jl_get_cpu_name, Ref{String}, ()) global JIT = ccall(:jl_get_JIT, Ref{String}, ()) __init_build() nothing end # Populate the paths needed by sysimg compilation, e.g. `generate_precompile.jl`, # without pulling in anything unnecessary like `CPU_NAME` function __init_build() global BINDIR = ccall(:jl_get_julia_bindir, Any, ())::String vers = "v$(VERSION.major).$(VERSION.minor)" global STDLIB = abspath(BINDIR, "..", "share", "julia", "stdlib", vers) nothing end mutable struct UV_cpu_info_t model::Ptr{UInt8} speed::Int32 cpu_times!user::UInt64 cpu_times!nice::UInt64 cpu_times!sys::UInt64 cpu_times!idle::UInt64 cpu_times!irq::UInt64 end mutable struct CPUinfo model::String speed::Int32 cpu_times!user::UInt64 cpu_times!nice::UInt64 cpu_times!sys::UInt64 cpu_times!idle::UInt64 cpu_times!irq::UInt64 CPUinfo(model,speed,u,n,s,id,ir)=new(model,speed,u,n,s,id,ir) end CPUinfo(info::UV_cpu_info_t) = CPUinfo(unsafe_string(info.model), info.speed, info.cpu_times!user, info.cpu_times!nice, info.cpu_times!sys, info.cpu_times!idle, info.cpu_times!irq) function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ") tck = SC_CLK_TCK if header println(io, info.model, ": ") print(io, " "^length(prefix)) println(io, " ", lpad("speed", 5), " ", lpad("user", 9), " ", lpad("nice", 9), " ", lpad("sys", 9), " ", lpad("idle", 9), " ", lpad("irq", 9)) end print(io, prefix) unit = tck > 0 ? " s " : " " tc = max(tck, 1) d(i, unit=unit) = lpad(string(round(Int64,i)), 9) * unit print(io, lpad(string(info.speed), 5), " MHz ", d(info.cpu_times!user / tc), d(info.cpu_times!nice / tc), d(info.cpu_times!sys / tc), d(info.cpu_times!idle / tc), d(info.cpu_times!irq / tc, tck > 0 ? " s" : " ")) if tck <= 0 print(io, "ticks") end end show(io::IO, info::CPUinfo) = _show_cpuinfo(io, info, true, " ") function _cpu_summary(io::IO, cpu::AbstractVector{CPUinfo}, i, j) if j-i < 9 header = true for x = i:j header || println(io) _show_cpuinfo(io, cpu[x], header, "#$(x-i+1) ") header = false end else summary = CPUinfo(cpu[i].model,0,0,0,0,0,0) count = j - i + 1 for x = i:j summary.speed += cpu[i].speed summary.cpu_times!user += cpu[x].cpu_times!user summary.cpu_times!nice += cpu[x].cpu_times!nice summary.cpu_times!sys += cpu[x].cpu_times!sys summary.cpu_times!idle += cpu[x].cpu_times!idle summary.cpu_times!irq += cpu[x].cpu_times!irq end summary.speed = div(summary.speed,count) _show_cpuinfo(io, summary, true, "#1-$(count) ") end println(io) end function cpu_summary(io::IO=stdout, cpu::AbstractVector{CPUinfo} = cpu_info()) model = cpu[1].model first = 1 for i = 2:length(cpu) if model != cpu[i].model _cpu_summary(io, cpu, first, i-1) first = i end end _cpu_summary(io, cpu, first, length(cpu)) end function cpu_info() UVcpus = Ref{Ptr{UV_cpu_info_t}}() count = Ref{Int32}() err = ccall(:uv_cpu_info, Int32, (Ptr{Ptr{UV_cpu_info_t}}, Ptr{Int32}), UVcpus, count) Base.uv_error("uv_cpu_info", err) cpus = Vector{CPUinfo}(undef, count[]) for i = 1:length(cpus) cpus[i] = CPUinfo(unsafe_load(UVcpus[], i)) end ccall(:uv_free_cpu_info, Cvoid, (Ptr{UV_cpu_info_t}, Int32), UVcpus[], count[]) return cpus end """ Sys.uptime() Gets the current system uptime in seconds. """ function uptime() uptime_ = Ref{Float64}() err = ccall(:uv_uptime, Int32, (Ptr{Float64},), uptime_) Base.uv_error("uv_uptime", err) return uptime_[] end """ Sys.loadavg() Get the load average. See: https://en.wikipedia.org/wiki/Load_(computing). """ function loadavg() loadavg_ = Vector{Float64}(undef, 3) ccall(:uv_loadavg, Cvoid, (Ptr{Float64},), loadavg_) return loadavg_ end """ Sys.free_physical_memory() Get the free memory of the system in bytes. The entire amount may not be available to the current process; use `Sys.free_memory()` for the actually available amount. """ free_physical_memory() = ccall(:uv_get_free_memory, UInt64, ()) """ Sys.total_physical_memory() Get the total memory in RAM (including that which is currently used) in bytes. The entire amount may not be available to the current process; see `Sys.total_memory()`. """ total_physical_memory() = ccall(:uv_get_total_memory, UInt64, ()) """ Sys.free_memory() Get the total free memory in RAM in bytes. """ free_memory() = ccall(:uv_get_available_memory, UInt64, ()) """ Sys.total_memory() Get the total memory in RAM (including that which is currently used) in bytes. This amount may be constrained, e.g., by Linux control groups. For the unconstrained amount, see `Sys.total_physical_memory()`. """ function total_memory() constrained = ccall(:uv_get_constrained_memory, UInt64, ()) physical = total_physical_memory() if 0 < constrained <= physical return constrained else return physical end end """ Sys.get_process_title() Get the process title. On some systems, will always return an empty string. """ function get_process_title() buf = Vector{UInt8}(undef, 512) err = ccall(:uv_get_process_title, Cint, (Ptr{UInt8}, Cint), buf, 512) Base.uv_error("get_process_title", err) return unsafe_string(pointer(buf)) end """ Sys.set_process_title(title::AbstractString) Set the process title. No-op on some operating systems. """ function set_process_title(title::AbstractString) err = ccall(:uv_set_process_title, Cint, (Cstring,), title) Base.uv_error("set_process_title", err) end """ Sys.maxrss() Get the maximum resident set size utilized in bytes. See also: - man page of `getrusage`(2) on Linux and FreeBSD. - Windows API `GetProcessMemoryInfo`. """ maxrss() = ccall(:jl_maxrss, Csize_t, ()) """ Sys.isunix([os]) Predicate for testing if the OS provides a Unix-like interface. See documentation in [Handling Operating System Variation](@ref). """ function isunix(os::Symbol) if iswindows(os) return false elseif islinux(os) || isbsd(os) return true elseif os === :Emscripten # Emscripten implements the POSIX ABI and provides traditional # Unix-style operating system functions such as file system support. # Therefore, we consider it a unix, even though this need not be # generally true for a jsvm embedding. return true else throw(ArgumentError("unknown operating system \"$os\"")) end end """ Sys.islinux([os]) Predicate for testing if the OS is a derivative of Linux. See documentation in [Handling Operating System Variation](@ref). """ islinux(os::Symbol) = (os === :Linux) """ Sys.isbsd([os]) Predicate for testing if the OS is a derivative of BSD. See documentation in [Handling Operating System Variation](@ref). !!! note The Darwin kernel descends from BSD, which means that `Sys.isbsd()` is `true` on macOS systems. To exclude macOS from a predicate, use `Sys.isbsd() && !Sys.isapple()`. """ isbsd(os::Symbol) = (isfreebsd(os) || isopenbsd(os) || isnetbsd(os) || isdragonfly(os) || isapple(os)) """ Sys.isfreebsd([os]) Predicate for testing if the OS is a derivative of FreeBSD. See documentation in [Handling Operating System Variation](@ref). !!! note Not to be confused with `Sys.isbsd()`, which is `true` on FreeBSD but also on other BSD-based systems. `Sys.isfreebsd()` refers only to FreeBSD. !!! compat "Julia 1.1" This function requires at least Julia 1.1. """ isfreebsd(os::Symbol) = (os === :FreeBSD) """ Sys.isopenbsd([os]) Predicate for testing if the OS is a derivative of OpenBSD. See documentation in [Handling Operating System Variation](@ref). !!! note Not to be confused with `Sys.isbsd()`, which is `true` on OpenBSD but also on other BSD-based systems. `Sys.isopenbsd()` refers only to OpenBSD. !!! compat "Julia 1.1" This function requires at least Julia 1.1. """ isopenbsd(os::Symbol) = (os === :OpenBSD) """ Sys.isnetbsd([os]) Predicate for testing if the OS is a derivative of NetBSD. See documentation in [Handling Operating System Variation](@ref). !!! note Not to be confused with `Sys.isbsd()`, which is `true` on NetBSD but also on other BSD-based systems. `Sys.isnetbsd()` refers only to NetBSD. !!! compat "Julia 1.1" This function requires at least Julia 1.1. """ isnetbsd(os::Symbol) = (os === :NetBSD) """ Sys.isdragonfly([os]) Predicate for testing if the OS is a derivative of DragonFly BSD. See documentation in [Handling Operating System Variation](@ref). !!! note Not to be confused with `Sys.isbsd()`, which is `true` on DragonFly but also on other BSD-based systems. `Sys.isdragonfly()` refers only to DragonFly. !!! compat "Julia 1.1" This function requires at least Julia 1.1. """ isdragonfly(os::Symbol) = (os === :DragonFly) """ Sys.iswindows([os]) Predicate for testing if the OS is a derivative of Microsoft Windows NT. See documentation in [Handling Operating System Variation](@ref). """ iswindows(os::Symbol) = (os === :Windows || os === :NT) """ Sys.isapple([os]) Predicate for testing if the OS is a derivative of Apple Macintosh OS X or Darwin. See documentation in [Handling Operating System Variation](@ref). """ isapple(os::Symbol) = (os === :Apple || os === :Darwin) """ Sys.isjsvm([os]) Predicate for testing if Julia is running in a JavaScript VM (JSVM), including e.g. a WebAssembly JavaScript embedding in a web browser. !!! compat "Julia 1.2" This function requires at least Julia 1.2. """ isjsvm(os::Symbol) = (os === :Emscripten) for f in (:isunix, :islinux, :isbsd, :isapple, :iswindows, :isfreebsd, :isopenbsd, :isnetbsd, :isdragonfly, :isjsvm) @eval $f() = $(getfield(@__MODULE__, f)(KERNEL)) end if iswindows() function windows_version() verinfo = ccall(:GetVersion, UInt32, ()) VersionNumber(verinfo & 0xFF, (verinfo >> 8) & 0xFF, verinfo >> 16) end else windows_version() = v"0.0" end """ Sys.windows_version() Return the version number for the Windows NT Kernel as a `VersionNumber`, i.e. `v"major.minor.build"`, or `v"0.0.0"` if this is not running on Windows. """ windows_version const WINDOWS_VISTA_VER = v"6.0" """ Sys.isexecutable(path::String) Return `true` if the given `path` has executable permissions. !!! note Prior to Julia 1.6, this did not correctly interrogate filesystem ACLs on Windows, therefore it would return `true` for any file. From Julia 1.6 on, it correctly determines whether the file is marked as executable or not. """ function isexecutable(path::String) # We use `access()` and `X_OK` to determine if a given path is # executable by the current user. `X_OK` comes from `unistd.h`. X_OK = 0x01 return ccall(:jl_fs_access, Cint, (Ptr{UInt8}, Cint), path, X_OK) == 0 end isexecutable(path::AbstractString) = isexecutable(String(path)) """ Sys.which(program_name::String) Given a program name, search the current `PATH` to find the first binary with the proper executable permissions that can be run and return an absolute path to it, or return `nothing` if no such program is available. If a path with a directory in it is passed in for `program_name`, tests that exact path for executable permissions only (with `.exe` and `.com` extensions added on Windows platforms); no searching of `PATH` is performed. """ function which(program_name::String) if isempty(program_name) return nothing end # Build a list of program names that we're going to try program_names = String[] base_pname = basename(program_name) if iswindows() # If the file already has an extension, try that name first if !isempty(splitext(base_pname)[2]) push!(program_names, base_pname) end # But also try appending .exe and .com` for pe in (".exe", ".com") push!(program_names, string(base_pname, pe)) end else # On non-windows, we just always search for what we've been given push!(program_names, base_pname) end path_dirs = String[] program_dirname = dirname(program_name) # If we've been given a path that has a directory name in it, then we # check to see if that path exists. Otherwise, we search the PATH. if isempty(program_dirname) # If we have been given just a program name (not a relative or absolute # path) then we should search `PATH` for it here: pathsep = iswindows() ? ';' : ':' path_dirs = map(abspath, eachsplit(get(ENV, "PATH", ""), pathsep)) # On windows we always check the current directory as well if iswindows() pushfirst!(path_dirs, pwd()) end else push!(path_dirs, abspath(program_dirname)) end # Here we combine our directories with our program names, searching for the # first match among all combinations. for path_dir in path_dirs for pname in program_names program_path = joinpath(path_dir, pname) try # If we find something that matches our name and we can execute if isfile(program_path) && isexecutable(program_path) return program_path end catch e # If we encounter a permission error, we skip this directory # and continue to the next directory in the PATH variable. if isa(e, Base.IOError) && e.code == Base.UV_EACCES # Permission denied, continue searching continue else # Rethrow the exception if it's not a permission error rethrow(e) end end end end # If we couldn't find anything, don't return anything nothing end which(program_name::AbstractString) = which(String(program_name)) end # module Sys K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/libc.jlW<# This file is a part of Julia. License is MIT: https://julialang.org/license module Libc @doc """ Interface to libc, the C standard library. """ Libc import Base: transcode, windowserror, show # these need to be defined seperately for bootstrapping but belong to Libc import Base: memcpy, memmove, memset, memcmp import Core.Intrinsics: bitcast export FILE, TmStruct, strftime, strptime, getpid, gethostname, free, malloc, memcpy, memmove, memset, calloc, realloc, errno, strerror, flush_cstdio, systemsleep, time, transcode if Sys.iswindows() export GetLastError, FormatMessage end include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "errno_h.jl")) # include($BUILDROOT/base/errno_h.jl) ## RawFD ## # Wrapper for an OS file descriptor (on both Unix and Windows) """ RawFD Primitive type which wraps the native OS file descriptor. `RawFD`s can be passed to methods like [`stat`](@ref) to discover information about the underlying file, and can also be used to open streams, with the `RawFD` describing the OS file backing the stream. """ primitive type RawFD 32 end RawFD(fd::Integer) = bitcast(RawFD, Cint(fd)) RawFD(fd::RawFD) = fd Base.cconvert(::Type{Cint}, fd::RawFD) = bitcast(Cint, fd) dup(x::RawFD) = ccall((@static Sys.iswindows() ? :_dup : :dup), RawFD, (RawFD,), x) dup(src::RawFD, target::RawFD) = systemerror("dup", -1 == ccall((@static Sys.iswindows() ? :_dup2 : :dup2), Int32, (RawFD, RawFD), src, target)) show(io::IO, fd::RawFD) = print(io, "RawFD(", bitcast(UInt32, fd), ')') # avoids invalidation via show_default # Wrapper for an OS file descriptor (for Windows) if Sys.iswindows() primitive type WindowsRawSocket sizeof(Ptr) * 8 end # On Windows file descriptors are HANDLE's and 64-bit on 64-bit Windows WindowsRawSocket(handle::Ptr{Cvoid}) = bitcast(WindowsRawSocket, handle) WindowsRawSocket(handle::WindowsRawSocket) = handle Base.cconvert(::Type{Ptr{Cvoid}}, fd::WindowsRawSocket) = bitcast(Ptr{Cvoid}, fd) _get_osfhandle(fd::RawFD) = ccall(:_get_osfhandle, WindowsRawSocket, (RawFD,), fd) _get_osfhandle(fd::WindowsRawSocket) = fd function dup(src::WindowsRawSocket) new_handle = Ref(WindowsRawSocket(Ptr{Cvoid}(-1))) my_process = ccall(:GetCurrentProcess, stdcall, Ptr{Cvoid}, ()) DUPLICATE_SAME_ACCESS = 0x2 status = ccall(:DuplicateHandle, stdcall, Int32, (Ptr{Cvoid}, WindowsRawSocket, Ptr{Cvoid}, Ptr{WindowsRawSocket}, UInt32, Int32, UInt32), my_process, src, my_process, new_handle, 0, false, DUPLICATE_SAME_ACCESS) windowserror("dup failed", status == 0) return new_handle[] end function dup(src::WindowsRawSocket, target::RawFD) fd = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), dup(src), 0) dup(fd, target) ccall(:_close, Int32, (RawFD,), fd) nothing end else _get_osfhandle(fd::RawFD) = fd end ## FILE (not auto-finalized) ## struct FILE ptr::Ptr{Cvoid} end modestr(s::IO) = modestr(isreadable(s), iswritable(s)) modestr(r::Bool, w::Bool) = r ? (w ? "r+" : "r") : (w ? "w" : throw(ArgumentError("neither readable nor writable"))) function FILE(fd::RawFD, mode) FILEp = ccall((@static Sys.iswindows() ? :_fdopen : :fdopen), Ptr{Cvoid}, (Cint, Cstring), fd, mode) systemerror("fdopen", FILEp == C_NULL) FILE(FILEp) end function FILE(s::IO) f = FILE(dup(RawFD(fd(s))),modestr(s)) seek(f, position(s)) f end Base.unsafe_convert(T::Union{Type{Ptr{Cvoid}},Type{Ptr{FILE}}}, f::FILE) = convert(T, f.ptr) Base.close(f::FILE) = systemerror("fclose", ccall(:fclose, Cint, (Ptr{Cvoid},), f.ptr) != 0) function Base.seek(h::FILE, offset::Integer) systemerror("fseek", ccall(:fseek, Cint, (Ptr{Cvoid}, Clong, Cint), h.ptr, offset, 0) != 0) h end Base.position(h::FILE) = ccall(:ftell, Clong, (Ptr{Cvoid},), h.ptr) # flush C stdio output from external libraries """ flush_cstdio() Flushes the C `stdout` and `stderr` streams (which may have been written to by external C code). """ flush_cstdio() = ccall(:jl_flush_cstdio, Cvoid, ()) ## time-related functions ## # TODO: check for usleep errors? if Sys.isunix() systemsleep(s::Real) = ccall(:usleep, Int32, (UInt32,), round(UInt32, s*1e6)) elseif Sys.iswindows() function systemsleep(s::Real) ccall(:Sleep, stdcall, Cvoid, (UInt32,), round(UInt32, s * 1e3)) return Int32(0) end else error("systemsleep undefined for this OS") end """ systemsleep(s::Real) Suspends execution for `s` seconds. This function does not yield to Julia's scheduler and therefore blocks the Julia thread that it is running on for the duration of the sleep time. See also [`sleep`](@ref). """ systemsleep struct TimeVal sec::Int64 usec::Int64 end function TimeVal() tv = Ref{TimeVal}() status = ccall(:jl_gettimeofday, Cint, (Ref{TimeVal},), tv) status != 0 && error("unable to determine current time: ", status) return tv[] end """ TmStruct([seconds]) Convert a number of seconds since the epoch to broken-down format, with fields `sec`, `min`, `hour`, `mday`, `month`, `year`, `wday`, `yday`, and `isdst`. """ mutable struct TmStruct sec::Int32 min::Int32 hour::Int32 mday::Int32 month::Int32 year::Int32 wday::Int32 yday::Int32 isdst::Int32 # on some platforms the struct is 14 words, even though 9 are specified _10::Int32 _11::Int32 _12::Int32 _13::Int32 _14::Int32 TmStruct(sec, min, hour, mday, month, year, wday, yday, isdst) = new(sec, min, hour, mday, month, year, wday, yday, isdst, 0,0,0,0,0) TmStruct() = new(0,0,0,0,0,0,0,0,0,0,0,0,0,0) function TmStruct(t::Real) t = floor(t) tm = TmStruct() # TODO: add support for UTC via gmtime_r() ccall(:localtime_r, Ptr{TmStruct}, (Ref{Int}, Ref{TmStruct}), t, tm) return tm end end """ strftime([format], time) Convert time, given as a number of seconds since the epoch or a `TmStruct`, to a formatted string using the given format. Supported formats are the same as those in the standard C library. """ strftime(t) = strftime("%c", t) strftime(fmt::AbstractString, t::Real) = strftime(fmt, TmStruct(t)) # Use wcsftime instead of strftime to support different locales function strftime(fmt::AbstractString, tm::TmStruct) wctimestr = Vector{Cwchar_t}(undef, 128) n = ccall(:wcsftime, Csize_t, (Ptr{Cwchar_t}, Csize_t, Cwstring, Ref{TmStruct}), wctimestr, length(wctimestr), fmt, tm) n == 0 && return "" return transcode(String, resize!(wctimestr, n)) end """ strptime([format], timestr) Parse a formatted time string into a `TmStruct` giving the seconds, minute, hour, date, etc. Supported formats are the same as those in the standard C library. On some platforms, timezones will not be parsed correctly. If the result of this function will be passed to `time` to convert it to seconds since the epoch, the `isdst` field should be filled in manually. Setting it to `-1` will tell the C library to use the current system settings to determine the timezone. """ strptime(timestr::AbstractString) = strptime("%c", timestr) function strptime(fmt::AbstractString, timestr::AbstractString) tm = TmStruct() r = ccall(:strptime, Cstring, (Cstring, Cstring, Ref{TmStruct}), timestr, fmt, tm) # the following would tell mktime() that this is a local time, and that # it should try to guess the timezone. not sure if/how this should be # exposed in the API. # tm.isdst = -1 if r == C_NULL # TODO: better error message throw(ArgumentError("invalid arguments")) end @static if Sys.isapple() # if we didn't explicitly parse the weekday or year day, use mktime # to fill them in automatically. if !occursin(r"([^%]|^)%(a|A|j|w|Ow)"a, fmt) ccall(:mktime, Int, (Ref{TmStruct},), tm) end end return tm end # system date in seconds """ time(t::TmStruct) -> Float64 Converts a `TmStruct` struct to a number of seconds since the epoch. """ time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm)) """ time() -> Float64 Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. """ time() = ccall(:jl_clock_now, Float64, ()) ## process-related functions ## """ getpid() -> Int32 Get Julia's process ID. """ getpid() = ccall(:uv_os_getpid, Int32, ()) ## network functions ## """ gethostname() -> String Get the local machine's host name. """ function gethostname() hn = Vector{UInt8}(undef, 256) err = @static if Sys.iswindows() ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) else ccall(:gethostname, Int32, (Ptr{UInt8}, UInt), hn, length(hn)) end systemerror("gethostname", err != 0) return GC.@preserve hn unsafe_string(pointer(hn)) end ## system error handling ## """ errno([code]) Get the value of the C library's `errno`. If an argument is specified, it is used to set the value of `errno`. The value of `errno` is only valid immediately after a `ccall` to a C library routine that sets it. Specifically, you cannot call `errno` at the next prompt in a REPL, because lots of code is executed between prompts. """ errno() = ccall(:jl_errno, Cint, ()) errno(e::Integer) = ccall(:jl_set_errno, Cvoid, (Cint,), e) """ strerror(n=errno()) Convert a system call error code to a descriptive string """ strerror(e::Integer) = unsafe_string(ccall(:strerror, Cstring, (Int32,), e)) strerror() = strerror(errno()) """ GetLastError() Call the Win32 `GetLastError` function [only available on Windows]. """ function GetLastError end """ FormatMessage(n=GetLastError()) Convert a Win32 system call error code to a descriptive string [only available on Windows]. """ function FormatMessage end if Sys.iswindows() GetLastError() = ccall(:GetLastError, stdcall, UInt32, ()) FormatMessage(e) = FormatMessage(UInt32(e)) function FormatMessage(e::UInt32=GetLastError()) FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) lpMsgBuf = Ref{Ptr{UInt16}}() lpMsgBuf[] = 0 len = ccall(:FormatMessageW, stdcall, UInt32, (UInt32, Ptr{Cvoid}, UInt32, UInt32, Ptr{Ptr{UInt16}}, UInt32, Ptr{Cvoid}), FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, C_NULL, e, 0, lpMsgBuf, 0, C_NULL) p = lpMsgBuf[] len == 0 && return "" buf = Vector{UInt16}(undef, len) GC.@preserve buf unsafe_copyto!(pointer(buf), p, len) ccall(:LocalFree, stdcall, Ptr{Cvoid}, (Ptr{Cvoid},), p) return transcode(String, buf) end end ## Memory related ## """ free(addr::Ptr) Call `free` from the C standard library. Only use this on memory obtained from [`malloc`](@ref), not on pointers retrieved from other C libraries. [`Ptr`](@ref) objects obtained from C libraries should be freed by the free functions defined in that library, to avoid assertion failures if multiple `libc` libraries exist on the system. """ free(p::Ptr) = ccall(:free, Cvoid, (Ptr{Cvoid},), p) free(p::Cstring) = free(convert(Ptr{UInt8}, p)) free(p::Cwstring) = free(convert(Ptr{Cwchar_t}, p)) """ malloc(size::Integer) -> Ptr{Cvoid} Call `malloc` from the C standard library. """ malloc(size::Integer) = ccall(:malloc, Ptr{Cvoid}, (Csize_t,), size) """ realloc(addr::Ptr, size::Integer) -> Ptr{Cvoid} Call `realloc` from the C standard library. See warning in the documentation for [`free`](@ref) regarding only using this on memory originally obtained from [`malloc`](@ref). """ realloc(p::Ptr, size::Integer) = ccall(:realloc, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), p, size) """ calloc(num::Integer, size::Integer) -> Ptr{Cvoid} Call `calloc` from the C standard library. """ calloc(num::Integer, size::Integer) = ccall(:calloc, Ptr{Cvoid}, (Csize_t, Csize_t), num, size) ## Random numbers ## # Access to very high quality (kernel) randomness function getrandom!(A::Union{Array,Base.RefValue}) ret = ccall(:uv_random, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cuint, Ptr{Cvoid}), C_NULL, C_NULL, A, sizeof(A), 0, C_NULL) Base.uv_error("getrandom", ret) return A end _make_uint64_seed() = getrandom!(Base.RefValue{UInt64}())[] # To limit dependency on rand functionality implemented in the Random module, # Libc.rand is used in Base (it also is independent from Random.seed, so is # only affected by `Libc.srand(seed)` calls) """ rand([T::Type]=UInt32) Generate a random number of type `T`. `T` can be `UInt32` or `Float64`. """ rand() = ccall(:jl_rand, UInt64, ()) % UInt32 rand(::Type{UInt32}) = rand() rand(::Type{Float64}) = rand() * 2.0^-32 """ srand([seed]) Set a value for the current global `seed`. """ function srand(seed::Integer=_make_uint64_seed()) ccall(:jl_srand, Cvoid, (UInt64,), seed % UInt64) end struct Cpasswd username::Cstring uid::Culong gid::Culong shell::Cstring homedir::Cstring gecos::Cstring Cpasswd() = new(C_NULL, typemax(Culong), typemax(Culong), C_NULL, C_NULL, C_NULL) end mutable struct Cgroup groupname::Cstring # group name gid::Culong # group ID mem::Ptr{Cstring} # group members Cgroup() = new(C_NULL, typemax(Culong), C_NULL) end struct Passwd username::String uid::UInt gid::UInt shell::String homedir::String gecos::String end struct Group groupname::String gid::UInt mem::Vector{String} end function getpwuid(uid::Unsigned, throw_error::Bool=true) ref_pd = Ref(Cpasswd()) ret = ccall(:uv_os_get_passwd2, Cint, (Ref{Cpasswd}, Culong), ref_pd, uid) if ret != 0 throw_error && Base.uv_error("getpwuid", ret) return end pd = ref_pd[] pd = Passwd( pd.username == C_NULL ? "" : unsafe_string(pd.username), pd.uid, pd.gid, pd.shell == C_NULL ? "" : unsafe_string(pd.shell), pd.homedir == C_NULL ? "" : unsafe_string(pd.homedir), pd.gecos == C_NULL ? "" : unsafe_string(pd.gecos), ) ccall(:uv_os_free_passwd, Cvoid, (Ref{Cpasswd},), ref_pd) return pd end function getgrgid(gid::Unsigned, throw_error::Bool=true) ref_gp = Ref(Cgroup()) ret = ccall(:uv_os_get_group, Cint, (Ref{Cgroup}, Culong), ref_gp, gid) if ret != 0 throw_error && Base.uv_error("getgrgid", ret) return end gp = ref_gp[] members = String[] if gp.mem != C_NULL while true mem = unsafe_load(gp.mem, length(members) + 1) mem == C_NULL && break push!(members, unsafe_string(mem)) end end gp = Group( gp.groupname == C_NULL ? "" : unsafe_string(gp.groupname), gp.gid, members, ) ccall(:uv_os_free_group, Cvoid, (Ref{Cgroup},), ref_gp) return gp end getuid() = ccall(:jl_getuid, Culong, ()) geteuid() = ccall(:jl_geteuid, Culong, ()) # Include dlopen()/dlpath() code include("libdl.jl") using .Libdl end # module P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./errno_h.jlข const E2BIG = Int32(7) const EACCES = Int32(13) const EADDRINUSE = Int32(98) const EADDRNOTAVAIL = Int32(99) const EADV = Int32(68) const EAFNOSUPPORT = Int32(97) const EAGAIN = Int32(11) const EALREADY = Int32(114) const EBADE = Int32(52) const EBADF = Int32(9) const EBADFD = Int32(77) const EBADMSG = Int32(74) const EBADR = Int32(53) const EBADRQC = Int32(56) const EBADSLT = Int32(57) const EBFONT = Int32(59) const EBUSY = Int32(16) const ECANCELED = Int32(125) const ECHILD = Int32(10) const ECHRNG = Int32(44) const ECOMM = Int32(70) const ECONNABORTED = Int32(103) const ECONNREFUSED = Int32(111) const ECONNRESET = Int32(104) const EDEADLK = Int32(35) const EDESTADDRREQ = Int32(89) const EDOM = Int32(33) const EDOTDOT = Int32(73) const EDQUOT = Int32(122) const EEXIST = Int32(17) const EFAULT = Int32(14) const EFBIG = Int32(27) const EHOSTDOWN = Int32(112) const EHOSTUNREACH = Int32(113) const EHWPOISON = Int32(133) const EIDRM = Int32(43) const EILSEQ = Int32(84) const EINPROGRESS = Int32(115) const EINTR = Int32(4) const EINVAL = Int32(22) const EIO = Int32(5) const EISCONN = Int32(106) const EISDIR = Int32(21) const EISNAM = Int32(120) const EKEYEXPIRED = Int32(127) const EKEYREJECTED = Int32(129) const EKEYREVOKED = Int32(128) const EL2HLT = Int32(51) const EL2NSYNC = Int32(45) const EL3HLT = Int32(46) const EL3RST = Int32(47) const ELIBACC = Int32(79) const ELIBBAD = Int32(80) const ELIBEXEC = Int32(83) const ELIBMAX = Int32(82) const ELIBSCN = Int32(81) const ELNRNG = Int32(48) const ELOOP = Int32(40) const EMEDIUMTYPE = Int32(124) const EMFILE = Int32(24) const EMLINK = Int32(31) const EMSGSIZE = Int32(90) const EMULTIHOP = Int32(72) const ENAMETOOLONG = Int32(36) const ENAVAIL = Int32(119) const ENETDOWN = Int32(100) const ENETRESET = Int32(102) const ENETUNREACH = Int32(101) const ENFILE = Int32(23) const ENOANO = Int32(55) const ENOBUFS = Int32(105) const ENOCSI = Int32(50) const ENODATA = Int32(61) const ENODEV = Int32(19) const ENOENT = Int32(2) const ENOEXEC = Int32(8) const ENOKEY = Int32(126) const ENOLCK = Int32(37) const ENOLINK = Int32(67) const ENOMEDIUM = Int32(123) const ENOMEM = Int32(12) const ENOMSG = Int32(42) const ENONET = Int32(64) const ENOPKG = Int32(65) const ENOPROTOOPT = Int32(92) const ENOSPC = Int32(28) const ENOSR = Int32(63) const ENOSTR = Int32(60) const ENOSYS = Int32(38) const ENOTBLK = Int32(15) const ENOTCONN = Int32(107) const ENOTDIR = Int32(20) const ENOTEMPTY = Int32(39) const ENOTNAM = Int32(118) const ENOTRECOVERABLE = Int32(131) const ENOTSOCK = Int32(88) const ENOTTY = Int32(25) const ENOTUNIQ = Int32(76) const ENXIO = Int32(6) const EOPNOTSUPP = Int32(95) const EOVERFLOW = Int32(75) const EOWNERDEAD = Int32(130) const EPERM = Int32(1) const EPFNOSUPPORT = Int32(96) const EPIPE = Int32(32) const EPROTO = Int32(71) const EPROTONOSUPPORT = Int32(93) const EPROTOTYPE = Int32(91) const ERANGE = Int32(34) const EREMCHG = Int32(78) const EREMOTE = Int32(66) const EREMOTEIO = Int32(121) const ERESTART = Int32(85) const ERFKILL = Int32(132) const EROFS = Int32(30) const ESHUTDOWN = Int32(108) const ESOCKTNOSUPPORT = Int32(94) const ESPIPE = Int32(29) const ESRCH = Int32(3) const ESRMNT = Int32(69) const ESTALE = Int32(116) const ESTRPIPE = Int32(86) const ETIME = Int32(62) const ETIMEDOUT = Int32(110) const ETOOMANYREFS = Int32(109) const ETXTBSY = Int32(26) const EUCLEAN = Int32(117) const EUNATCH = Int32(49) const EUSERS = Int32(87) const EXDEV = Int32(18) const EXFULL = Int32(54) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/libdl.jl4&# This file is a part of Julia. License is MIT: https://julialang.org/license module Libdl @doc """ Interface to libdl. Provides dynamic linking support. """ Libdl import Base.DL_LOAD_PATH export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, dlpath, find_library, dlext, dllist """ DL_LOAD_PATH When calling [`dlopen`](@ref), the paths in this list will be searched first, in order, before searching the system locations for a valid library handle. """ DL_LOAD_PATH # note: constants to match JL_RTLD_* in src/julia.h, translated # to system-specific values by JL_RTLD macro in src/dlload.c const RTLD_LOCAL = 0x00000001 const RTLD_GLOBAL = 0x00000002 const RTLD_LAZY = 0x00000004 const RTLD_NOW = 0x00000008 const RTLD_NODELETE = 0x00000010 const RTLD_NOLOAD = 0x00000020 const RTLD_DEEPBIND = 0x00000040 const RTLD_FIRST = 0x00000080 """ RTLD_DEEPBIND RTLD_FIRST RTLD_GLOBAL RTLD_LAZY RTLD_LOCAL RTLD_NODELETE RTLD_NOLOAD RTLD_NOW Enum constant for [`dlopen`](@ref). See your platform man page for details, if applicable. """ (RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW) """ dlsym(handle, sym; throw_error::Bool = true) Look up a symbol from a shared library handle, return callable function pointer on success. If the symbol cannot be found, this method throws an error, unless the keyword argument `throw_error` is set to `false`, in which case this method returns `nothing`. """ function dlsym(hnd::Ptr, s::Union{Symbol,AbstractString}; throw_error::Bool = true) hnd == C_NULL && throw(ArgumentError("NULL library handle")) val = Ref(Ptr{Cvoid}(0)) symbol_found = ccall(:jl_dlsym, Cint, (Ptr{Cvoid}, Cstring, Ref{Ptr{Cvoid}}, Cint), hnd, s, val, Int64(throw_error) ) if symbol_found == 0 return nothing end return val[] end """ dlsym_e(handle, sym) Look up a symbol from a shared library handle, silently return `C_NULL` on lookup failure. This method is now deprecated in favor of `dlsym(handle, sym; throw_error=false)`. """ function dlsym_e(hnd::Ptr, s::Union{Symbol,AbstractString}) return something(dlsym(hnd, s; throw_error=false), C_NULL) end """ dlopen(libfile::AbstractString [, flags::Integer]; throw_error:Bool = true) Load a shared library, returning an opaque handle. The extension given by the constant `dlext` (`.so`, `.dll`, or `.dylib`) can be omitted from the `libfile` string, as it is automatically appended if needed. If `libfile` is not an absolute path name, then the paths in the array `DL_LOAD_PATH` are searched for `libfile`, followed by the system load path. The optional flags argument is a bitwise-or of zero or more of `RTLD_LOCAL`, `RTLD_GLOBAL`, `RTLD_LAZY`, `RTLD_NOW`, `RTLD_NODELETE`, `RTLD_NOLOAD`, `RTLD_DEEPBIND`, and `RTLD_FIRST`. These are converted to the corresponding flags of the POSIX (and/or GNU libc and/or MacOS) dlopen command, if possible, or are ignored if the specified functionality is not available on the current platform. The default flags are platform specific. On MacOS the default `dlopen` flags are `RTLD_LAZY|RTLD_DEEPBIND|RTLD_GLOBAL` while on other platforms the defaults are `RTLD_LAZY|RTLD_DEEPBIND|RTLD_LOCAL`. An important usage of these flags is to specify non default behavior for when the dynamic library loader binds library references to exported symbols and if the bound references are put into process local or global scope. For instance `RTLD_LAZY|RTLD_DEEPBIND|RTLD_GLOBAL` allows the library's symbols to be available for usage in other shared libraries, addressing situations where there are dependencies between shared libraries. If the library cannot be found, this method throws an error, unless the keyword argument `throw_error` is set to `false`, in which case this method returns `nothing`. !!! note From Julia 1.6 on, this method replaces paths starting with `@executable_path/` with the path to the Julia executable, allowing for relocatable relative-path loads. In Julia 1.5 and earlier, this only worked on macOS. """ function dlopen end dlopen(s::Symbol, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; kwargs...) = dlopen(string(s), flags; kwargs...) function dlopen(s::AbstractString, flags::Integer = RTLD_LAZY | RTLD_DEEPBIND; throw_error::Bool = true) ret = ccall(:jl_load_dynamic_library, Ptr{Cvoid}, (Cstring,UInt32,Cint), s, flags, Cint(throw_error)) if ret == C_NULL return nothing end return ret end """ dlopen(f::Function, args...; kwargs...) Wrapper for usage with `do` blocks to automatically close the dynamic library once control flow leaves the `do` block scope. # Example ```julia vendor = dlopen("libblas") do lib if Libdl.dlsym(lib, :openblas_set_num_threads; throw_error=false) !== nothing return :openblas else return :other end end ``` """ function dlopen(f::Function, args...; kwargs...) hdl = nothing try hdl = dlopen(args...; kwargs...) f(hdl) finally dlclose(hdl) end end """ dlopen_e(libfile::AbstractString [, flags::Integer]) Similar to [`dlopen`](@ref), except returns `C_NULL` instead of raising errors. This method is now deprecated in favor of `dlopen(libfile::AbstractString [, flags::Integer]; throw_error=false)`. """ dlopen_e(args...) = something(dlopen(args...; throw_error=false), C_NULL) """ dlclose(handle) Close shared library referenced by handle. """ function dlclose(p::Ptr) 0 == ccall(:jl_dlclose, Cint, (Ptr{Cvoid},), p) end """ dlclose(::Nothing) For the very common pattern usage pattern of try hdl = dlopen(library_name) ... do something finally dlclose(hdl) end We define a `dlclose()` method that accepts a parameter of type `Nothing`, so that user code does not have to change its behavior for the case that `library_name` was not found. """ function dlclose(p::Nothing) end """ find_library(names [, locations]) Searches for the first library in `names` in the paths in the `locations` list, `DL_LOAD_PATH`, or system library paths (in that order) which can successfully be dlopen'd. On success, the return value will be one of the names (potentially prefixed by one of the paths in locations). This string can be assigned to a `global const` and used as the library name in future `ccall`'s. On failure, it returns the empty string. """ function find_library(libnames, extrapaths=String[]) for lib in libnames for path in extrapaths l = joinpath(path, lib) p = dlopen(l, RTLD_LAZY; throw_error=false) if p !== nothing dlclose(p) return l end end p = dlopen(lib, RTLD_LAZY; throw_error=false) if p !== nothing dlclose(p) return lib end end return "" end find_library(libname::Union{Symbol,AbstractString}, extrapaths=String[]) = find_library([string(libname)], extrapaths) """ dlpath(handle::Ptr{Cvoid}) Given a library `handle` from `dlopen`, return the full path. """ function dlpath(handle::Ptr{Cvoid}) p = ccall(:jl_pathname_for_handle, Cstring, (Ptr{Cvoid},), handle) s = unsafe_string(p) Sys.iswindows() && Libc.free(p) return s end """ dlpath(libname::Union{AbstractString, Symbol}) Get the full path of the library `libname`. # Example ```julia-repl julia> dlpath("libjulia") ``` """ function dlpath(libname::Union{AbstractString, Symbol}) handle = dlopen(libname) path = dlpath(handle) dlclose(handle) return path end if Sys.isapple() const dlext = "dylib" elseif Sys.iswindows() const dlext = "dll" else #assume Sys.islinux, or similar const dlext = "so" end """ dlext File extension for dynamic libraries (e.g. dll, dylib, so) on the current platform. """ dlext if (Sys.islinux() || Sys.isbsd()) && !Sys.isapple() struct dl_phdr_info # Base address of object addr::Cuint # Null-terminated name of object name::Ptr{UInt8} # Pointer to array of ELF program headers for this object phdr::Ptr{Cvoid} # Number of program headers for this object phnum::Cshort end # This callback function called by dl_iterate_phdr() on Linux and BSD's # DL_ITERATE_PHDR(3) on freebsd function dl_phdr_info_callback(di::dl_phdr_info, size::Csize_t, dynamic_libraries::Array{String,1}) name = unsafe_string(di.name) push!(dynamic_libraries, name) return Cint(0) end end """ dllist() Return the paths of dynamic libraries currently loaded in a `Vector{String}`. """ function dllist() dynamic_libraries = Vector{String}() @static if Sys.isapple() numImages = ccall(:_dyld_image_count, Cint, ()) # start at 1 instead of 0 to skip self for i in 1:numImages-1 name = unsafe_string(ccall(:_dyld_get_image_name, Cstring, (UInt32,), i)) push!(dynamic_libraries, name) end elseif Sys.islinux() || Sys.isbsd() callback = @cfunction(dl_phdr_info_callback, Cint, (Ref{dl_phdr_info}, Csize_t, Ref{Vector{String}})) ccall(:dl_iterate_phdr, Cint, (Ptr{Cvoid}, Ref{Vector{String}}), callback, dynamic_libraries) popfirst!(dynamic_libraries) filter!(!isempty, dynamic_libraries) elseif Sys.iswindows() ccall(:jl_dllist, Cint, (Any,), dynamic_libraries) else # unimplemented end return dynamic_libraries end end # module Libdl N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/logging.jlฑZ# This file is a part of Julia. License is MIT: https://julialang.org/license module CoreLogging import Base: isless, +, -, convert, show export AbstractLogger, LogLevel, NullLogger, @debug, @info, @warn, @error, @logmsg, with_logger, current_logger, global_logger, disable_logging, SimpleLogger #------------------------------------------------------------------------------- # The AbstractLogger interface """ A logger controls how log records are filtered and dispatched. When a log record is generated, the logger is the first piece of user configurable code which gets to inspect the record and decide what to do with it. """ abstract type AbstractLogger ; end """ handle_message(logger, level, message, _module, group, id, file, line; key1=val1, ...) Log a message to `logger` at `level`. The logical location at which the message was generated is given by module `_module` and `group`; the source location by `file` and `line`. `id` is an arbitrary unique value (typically a [`Symbol`](@ref)) to be used as a key to identify the log statement when filtering. """ function handle_message end """ shouldlog(logger, level, _module, group, id) Return `true` when `logger` accepts a message at `level`, generated for `_module`, `group` and with unique log identifier `id`. """ function shouldlog end """ min_enabled_level(logger) Return the minimum enabled level for `logger` for early filtering. That is, the log level below or equal to which all messages are filtered. """ function min_enabled_level end """ catch_exceptions(logger) Return `true` if the logger should catch exceptions which happen during log record construction. By default, messages are caught By default all exceptions are caught to prevent log message generation from crashing the program. This lets users confidently toggle little-used functionality - such as debug logging - in a production system. If you want to use logging as an audit trail you should disable this for your logger type. """ catch_exceptions(logger) = true # Prevent invalidation when packages define custom loggers # Using invoke in combination with @nospecialize eliminates backedges to these methods Base.@constprop :none function _invoked_shouldlog(logger, level, _module, group, id) @nospecialize return invoke( shouldlog, Tuple{typeof(logger), typeof(level), typeof(_module), typeof(group), typeof(id)}, logger, level, _module, group, id )::Bool end function _invoked_min_enabled_level(@nospecialize(logger)) return invoke(min_enabled_level, Tuple{typeof(logger)}, logger)::LogLevel end function _invoked_catch_exceptions(@nospecialize(logger)) return invoke(catch_exceptions, Tuple{typeof(logger)}, logger)::Bool end """ NullLogger() Logger which disables all messages and produces no output - the logger equivalent of /dev/null. """ struct NullLogger <: AbstractLogger; end min_enabled_level(::NullLogger) = AboveMaxLevel shouldlog(::NullLogger, args...) = false handle_message(::NullLogger, args...; kwargs...) = (@nospecialize; error("Null logger handle_message() should not be called")) #------------------------------------------------------------------------------- # Standard log levels """ LogLevel(level) Severity/verbosity of a log record. The log level provides a key against which potential log records may be filtered, before any other work is done to construct the log record data structure itself. # Examples ```julia-repl julia> Logging.LogLevel(0) == Logging.Info true ``` """ struct LogLevel level::Int32 end LogLevel(level::LogLevel) = level isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) +(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) -(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) convert(::Type{LogLevel}, level::Integer) = LogLevel(level) const BelowMinLevel = LogLevel(-1000001) """ Debug Alias for [`LogLevel(-1000)`](@ref LogLevel). """ const Debug = LogLevel( -1000) """ Info Alias for [`LogLevel(0)`](@ref LogLevel). """ const Info = LogLevel( 0) """ Warn Alias for [`LogLevel(1000)`](@ref LogLevel). """ const Warn = LogLevel( 1000) """ Error Alias for [`LogLevel(2000)`](@ref LogLevel). """ const Error = LogLevel( 2000) const AboveMaxLevel = LogLevel( 1000001) # Global log limiting mechanism for super fast but inflexible global log limiting. const _min_enabled_level = Ref{LogLevel}(Debug) function show(io::IO, level::LogLevel) if level == BelowMinLevel print(io, "BelowMinLevel") elseif level == Debug print(io, "Debug") elseif level == Info print(io, "Info") elseif level == Warn print(io, "Warn") elseif level == Error print(io, "Error") elseif level == AboveMaxLevel print(io, "AboveMaxLevel") else print(io, "LogLevel($(level.level))") end end #------------------------------------------------------------------------------- # Logging macros _logmsg_docs = """ @debug message [key=value | value ...] @info message [key=value | value ...] @warn message [key=value | value ...] @error message [key=value | value ...] @logmsg level message [key=value | value ...] Create a log record with an informational `message`. For convenience, four logging macros `@debug`, `@info`, `@warn` and `@error` are defined which log at the standard severity levels `Debug`, `Info`, `Warn` and `Error`. `@logmsg` allows `level` to be set programmatically to any `LogLevel` or custom log level types. `message` should be an expression which evaluates to a string which is a human readable description of the log event. By convention, this string will be formatted as markdown when presented. The optional list of `key=value` pairs supports arbitrary user defined metadata which will be passed through to the logging backend as part of the log record. If only a `value` expression is supplied, a key representing the expression will be generated using [`Symbol`](@ref). For example, `x` becomes `x=x`, and `foo(10)` becomes `Symbol("foo(10)")=foo(10)`. For splatting a list of key value pairs, use the normal splatting syntax, `@info "blah" kws...`. There are some keys which allow automatically generated log data to be overridden: * `_module=mod` can be used to specify a different originating module from the source location of the message. * `_group=symbol` can be used to override the message group (this is normally derived from the base name of the source file). * `_id=symbol` can be used to override the automatically generated unique message identifier. This is useful if you need to very closely associate messages generated on different source lines. * `_file=string` and `_line=integer` can be used to override the apparent source location of a log message. There's also some key value pairs which have conventional meaning: * `maxlog=integer` should be used as a hint to the backend that the message should be displayed no more than `maxlog` times. * `exception=ex` should be used to transport an exception with a log message, often used with `@error`. An associated backtrace `bt` may be attached using the tuple `exception=(ex,bt)`. # Examples ```julia @debug "Verbose debugging information. Invisible by default" @info "An informational message" @warn "Something was odd. You should pay attention" @error "A non fatal error occurred" x = 10 @info "Some variables attached to the message" x a=42.0 @debug begin sA = sum(A) "sum(A) = \$sA is an expensive operation, evaluated only when `shouldlog` returns true" end for i=1:10000 @info "With the default backend, you will only see (i = \$i) ten times" maxlog=10 @debug "Algorithm1" i progress=i/10000 end ``` """ # Get (module,filepath,line) for the location of the caller of a macro. # Designed to be used from within the body of a macro. macro _sourceinfo() esc(quote (__module__, __source__.file === nothing ? "?" : String(__source__.file::Symbol), __source__.line) end) end macro logmsg(level, exs...) logmsg_code((@_sourceinfo)..., esc(level), exs...) end macro debug(exs...) logmsg_code((@_sourceinfo)..., :Debug, exs...) end macro info(exs...) logmsg_code((@_sourceinfo)..., :Info, exs...) end macro warn(exs...) logmsg_code((@_sourceinfo)..., :Warn, exs...) end macro error(exs...) logmsg_code((@_sourceinfo)..., :Error, exs...) end # Logging macros share documentation @eval @doc $_logmsg_docs :(@logmsg) @eval @doc $_logmsg_docs :(@debug) @eval @doc $_logmsg_docs :(@info) @eval @doc $_logmsg_docs :(@warn) @eval @doc $_logmsg_docs :(@error) _log_record_ids = Set{Symbol}() # Generate a unique, stable, short, somewhat human readable identifier for a # logging *statement*. The idea here is to have a key against which log events # can be filtered and otherwise manipulated. The key should uniquely identify # the source location in the originating module, but ideally should be stable # across versions of the originating module, provided the log generating # statement itself doesn't change. function log_record_id(_module, level, message, log_kws) @nospecialize modname = _module === nothing ? "" : join(fullname(_module), "_") # Use an arbitrarily chosen eight hex digits here. TODO: Figure out how to # make the id exactly the same on 32 and 64 bit systems. h = UInt32(hash(string(modname, level, message, log_kws)::String) & 0xFFFFFFFF) while true id = Symbol(modname, '_', string(h, base = 16, pad = 8)) # _log_record_ids is a registry of log record ids for use during # compilation, to ensure uniqueness of ids. Note that this state will # only persist during module compilation so it will be empty when a # precompiled module is loaded. if !(id in _log_record_ids) push!(_log_record_ids, id) return id end h += 1 end end default_group(file) = Symbol(splitext(basename(file))[1]) function issimple(@nospecialize val) val isa String && return true val isa Symbol && return true val isa QuoteNode && return true val isa Number && return true val isa Char && return true if val isa Expr val.head === :quote && issimple(val.args[1]) && return true val.head === :inert && return true end return false end function issimplekw(@nospecialize val) if val isa Expr if val.head === :kw val = val.args[2] if val isa Expr && val.head === :escape issimple(val.args[1]) && return true end end end return false end # helper function to get the current logger, if enabled for the specified message type @noinline Base.@constprop :none function current_logger_for_env(std_level::LogLevel, group, _module) logstate = @inline current_logstate() if std_level >= logstate.min_enabled_level || env_override_minlevel(group, _module) return logstate.logger end return nothing end # Generate code for logging macros function logmsg_code(_module, file, line, level, message, exs...) @nospecialize log_data = process_logmsg_exs(_module, file, line, level, message, exs...) if !isa(message, Symbol) && issimple(message) && isempty(log_data.kwargs) logrecord = quote msg = $(message) kwargs = (;) true end elseif issimple(message) && all(issimplekw, log_data.kwargs) # if message and kwargs are just values and variables, we can avoid try/catch # complexity by adding the code for testing the UndefVarError by hand checkerrors = nothing for kwarg in reverse(log_data.kwargs) if isa(kwarg.args[2].args[1], Symbol) checkerrors = Expr(:if, Expr(:isdefined, kwarg.args[2]), checkerrors, Expr(:call, Expr(:core, :UndefVarError), QuoteNode(kwarg.args[2].args[1]))) end end if isa(message, Symbol) message = esc(message) checkerrors = Expr(:if, Expr(:isdefined, message), checkerrors, Expr(:call, Expr(:core, :UndefVarError), QuoteNode(message.args[1]))) end logrecord = quote let err = $checkerrors if err === nothing msg = $(message) kwargs = (;$(log_data.kwargs...)) true else @invokelatest logging_error(logger, level, _module, group, id, file, line, err, false) false end end end else logrecord = quote try msg = $(esc(message)) kwargs = (;$(log_data.kwargs...)) true catch err @invokelatest logging_error(logger, level, _module, group, id, file, line, err, true) false end end end return quote let level = $level # simplify std_level code emitted, if we know it is one of our global constants std_level = $(level isa Symbol ? :level : :(level isa $LogLevel ? level : convert($LogLevel, level)::$LogLevel)) if std_level >= $(_min_enabled_level)[] group = $(log_data._group) _module = $(log_data._module) logger = $(current_logger_for_env)(std_level, group, _module) if !(logger === nothing) id = $(log_data._id) # Second chance at an early bail-out (before computing the message), # based on arbitrary logger-specific logic. if invokelatest($shouldlog, logger, level, _module, group, id) file = $(log_data._file) if file isa String file = Base.fixup_stdlib_path(file) end line = $(log_data._line) local msg, kwargs $(logrecord) && invokelatest($handle_message, logger, level, msg, _module, group, id, file, line; kwargs...) end end end nothing end end end function process_logmsg_exs(_orig_module, _file, _line, level, message, exs...) @nospecialize local _group, _id _module = _orig_module kwargs = Any[] for ex in exs if ex isa Expr && ex.head === :(=) k, v = ex.args if !(k isa Symbol) k = Symbol(k) end # Recognize several special keyword arguments if k === :_group _group = esc(v) elseif k === :_id _id = esc(v) elseif k === :_module _module = esc(v) elseif k === :_file _file = esc(v) elseif k === :_line _line = esc(v) else # Copy across key value pairs for structured log records push!(kwargs, Expr(:kw, k, esc(v))) end elseif ex isa Expr && ex.head === :... # Keyword splatting push!(kwargs, esc(ex)) else # Positional arguments - will be converted to key value pairs automatically. push!(kwargs, Expr(:kw, Symbol(ex), esc(ex))) end end if !@isdefined(_group) _group = default_group_code(_file) end if !@isdefined(_id) _id = Expr(:quote, log_record_id(_orig_module, level, message, exs)) end return (;_module, _group, _id, _file, _line, kwargs) end function default_group_code(file) @nospecialize if file isa String && isdefined(Base, :basename) QuoteNode(default_group(file)) # precompute if we can else ref = Ref{Symbol}() # memoized run-time execution :(isassigned($ref) ? $ref[] : $ref[] = default_group(something($file, ""))::Symbol) end end # Report an error in log message creation @noinline function logging_error(logger, level, _module, group, id, filepath, line, @nospecialize(err), real::Bool) @nospecialize if !_invoked_catch_exceptions(logger) real ? rethrow(err) : throw(err) end msg = try "Exception while generating log record in module $_module at $filepath:$line" catch ex LazyString("Exception handling log message: ", ex) end bt = real ? catch_backtrace() : backtrace() handle_message( logger, Error, msg, _module, :logevent_error, id, filepath, line; exception=(err,bt)) nothing end # Log a message. Called from the julia C code; kwargs is in the format # Any[key1,val1, ...] for simplicity in construction on the C side. function logmsg_shim(level, message, _module, group, id, file, line, kwargs) @nospecialize real_kws = Any[(kwargs[i], kwargs[i+1]) for i in 1:2:length(kwargs)] @logmsg(convert(LogLevel, level), message, _module=_module, _id=id, _group=group, _file=String(file), _line=line, real_kws...) nothing end # LogState - a cache of data extracted from the logger, plus the logger itself. struct LogState min_enabled_level::LogLevel logger::AbstractLogger end LogState(logger) = LogState(LogLevel(_invoked_min_enabled_level(logger)), logger) function current_logstate() logstate = current_task().logstate return (logstate !== nothing ? logstate : _global_logstate)::LogState end function with_logstate(f::Function, logstate) @nospecialize t = current_task() old = t.logstate try t.logstate = logstate f() finally t.logstate = old end end #------------------------------------------------------------------------------- # Control of the current logger and early log filtering """ disable_logging(level) Disable all log messages at log levels equal to or less than `level`. This is a *global* setting, intended to make debug logging extremely cheap when disabled. # Examples ```julia Logging.disable_logging(Logging.Info) # Disable debug and info ``` """ function disable_logging(level::LogLevel) _min_enabled_level[] = level + 1 end let _debug_groups_include::Vector{Symbol} = Symbol[], _debug_groups_exclude::Vector{Symbol} = Symbol[], _debug_str::String = "" global Base.@constprop :none function env_override_minlevel(group, _module) debug = get(ENV, "JULIA_DEBUG", "") if !(debug === _debug_str) _debug_str = debug empty!(_debug_groups_include) empty!(_debug_groups_exclude) for g in split(debug, ',') if !isempty(g) if startswith(g, "!") if !isempty(g[2:end]) push!(_debug_groups_exclude, Symbol(g[2:end])) end else push!(_debug_groups_include, Symbol(g)) end end end unique!(_debug_groups_include) unique!(_debug_groups_exclude) end if !(:all in _debug_groups_exclude) && (:all in _debug_groups_include || !isempty(_debug_groups_exclude)) if isempty(_debug_groups_exclude) return true elseif isa(group, Symbol) && group in _debug_groups_exclude return false elseif isa(_module, Module) && (nameof(_module) in _debug_groups_exclude || nameof(Base.moduleroot(_module)) in _debug_groups_exclude) return false else return true end else if isempty(_debug_groups_include) return false elseif isa(group, Symbol) && group in _debug_groups_include return true elseif isa(_module, Module) && (nameof(_module) in _debug_groups_include || nameof(Base.moduleroot(_module)) in _debug_groups_include) return true else return false end end return false end end """ global_logger() Return the global logger, used to receive messages when no specific logger exists for the current task. global_logger(logger) Set the global logger to `logger`, and return the previous global logger. """ global_logger() = _global_logstate.logger function global_logger(logger::AbstractLogger) prev = _global_logstate.logger global _global_logstate = LogState(logger) prev end """ with_logger(function, logger) Execute `function`, directing all log messages to `logger`. # Example ```julia function test(x) @info "x = \$x" end with_logger(logger) do test(1) test([1,2]) end ``` """ function with_logger(@nospecialize(f::Function), logger::AbstractLogger) with_logstate(f, LogState(logger)) end """ current_logger() Return the logger for the current task, or the global logger if none is attached to the task. """ current_logger() = current_logstate().logger const closed_stream = IOBuffer(UInt8[]) close(closed_stream) #------------------------------------------------------------------------------- # SimpleLogger """ SimpleLogger([stream,] min_level=Info) Simplistic logger for logging all messages with level greater than or equal to `min_level` to `stream`. If stream is closed then messages with log level greater or equal to `Warn` will be logged to `stderr` and below to `stdout`. """ struct SimpleLogger <: AbstractLogger stream::IO min_level::LogLevel message_limits::Dict{Any,Int} end SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) SimpleLogger(level=Info) = SimpleLogger(closed_stream, level) shouldlog(logger::SimpleLogger, level, _module, group, id) = get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::SimpleLogger) = logger.min_level catch_exceptions(logger::SimpleLogger) = false function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, group, id, filepath, line; kwargs...) @nospecialize maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts remaining = get!(logger.message_limits, id, Int(maxlog)::Int) logger.message_limits[id] = remaining - 1 remaining > 0 || return end buf = IOBuffer() stream::IO = logger.stream if !(isopen(stream)::Bool) stream = stderr end iob = IOContext(buf, stream) levelstr = level == Warn ? "Warning" : string(level) msglines = eachsplit(chomp(convert(String, string(message))::String), '\n') msg1, rest = Iterators.peel(msglines) println(iob, "โ”Œ ", levelstr, ": ", msg1) for msg in rest println(iob, "โ”‚ ", msg) end for (key, val) in kwargs key === :maxlog && continue println(iob, "โ”‚ ", key, " = ", val) end println(iob, "โ”” @ ", _module, " ", filepath, ":", line) write(stream, take!(buf)) nothing end _global_logstate = LogState(SimpleLogger()) end # CoreLogging R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/linked_list.jl๚# This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct IntrusiveLinkedList{T} # Invasive list requires that T have a field `.next >: U{T, Nothing}` and `.queue >: U{ILL{T}, Nothing}` head::Union{T, Nothing} tail::Union{T, Nothing} IntrusiveLinkedList{T}() where {T} = new{T}(nothing, nothing) end #const list_append!! = append! #const list_deletefirst! = delete! eltype(::Type{<:IntrusiveLinkedList{T}}) where {T} = @isdefined(T) ? T : Any iterate(q::IntrusiveLinkedList) = (h = q.head; h === nothing ? nothing : (h, h)) iterate(q::IntrusiveLinkedList{T}, v::T) where {T} = (h = v.next; h === nothing ? nothing : (h, h)) isempty(q::IntrusiveLinkedList) = (q.head === nothing) function length(q::IntrusiveLinkedList) i = 0 head = q.head while head !== nothing i += 1 head = head.next end return i end function list_append!!(q::IntrusiveLinkedList{T}, q2::IntrusiveLinkedList{T}) where T q === q2 && error("can't append list to itself") head2 = q2.head if head2 !== nothing tail2 = q2.tail::T q2.head = nothing q2.tail = nothing tail = q.tail q.tail = tail2 if tail === nothing q.head = head2 else tail.next = head2 end while head2 !== nothing head2.queue = q head2 = head2.next end end return q end function push!(q::IntrusiveLinkedList{T}, val::T) where T val.queue === nothing || error("val already in a list") val.queue = q tail = q.tail if tail === nothing q.head = q.tail = val else tail.next = val q.tail = val end return q end function pushfirst!(q::IntrusiveLinkedList{T}, val::T) where T val.queue === nothing || error("val already in a list") val.queue = q head = q.head if head === nothing q.head = q.tail = val else val.next = head q.head = val end return q end function pop!(q::IntrusiveLinkedList{T}) where {T} val = q.tail::T list_deletefirst!(q, val) # expensive! return val end function popfirst!(q::IntrusiveLinkedList{T}) where {T} val = q.head::T list_deletefirst!(q, val) # cheap return val end # this function assumes `val` is found in `q` function list_deletefirst!(q::IntrusiveLinkedList{T}, val::T) where T val.queue === q || return head = q.head::T if head === val if q.tail::T === val q.head = q.tail = nothing else q.head = val.next::T end else head_next = head.next::T while head_next !== val head = head_next head_next = head.next::T end if q.tail::T === val head.next = nothing q.tail = head else head.next = val.next::T end end val.next = nothing val.queue = nothing return q end #function list_deletefirst!(q::Array{T}, val::T) where T # i = findfirst(isequal(val), q) # i === nothing || deleteat!(q, i) # return q #end mutable struct LinkedListItem{T} # Adapter class to use any `T` in a LinkedList next::Union{LinkedListItem{T}, Nothing} queue::Union{IntrusiveLinkedList{LinkedListItem{T}}, Nothing} value::T LinkedListItem{T}(value::T) where {T} = new{T}(nothing, nothing, value) end const LinkedList{T} = IntrusiveLinkedList{LinkedListItem{T}} # delegate methods, as needed eltype(::Type{<:LinkedList{T}}) where {T} = @isdefined(T) ? T : Any iterate(q::LinkedList) = (h = q.head; h === nothing ? nothing : (h.value, h)) iterate(q::IntrusiveLinkedList{LLT}, v::LLT) where {LLT<:LinkedListItem} = (h = v.next; h === nothing ? nothing : (h.value, h)) push!(q::LinkedList{T}, val::T) where {T} = push!(q, LinkedListItem{T}(val)) pushfirst!(q::LinkedList{T}, val::T) where {T} = pushfirst!(q, LinkedListItem{T}(val)) pop!(q::LinkedList) = invoke(pop!, Tuple{IntrusiveLinkedList,}, q).value popfirst!(q::LinkedList) = invoke(popfirst!, Tuple{IntrusiveLinkedList,}, q).value function list_deletefirst!(q::LinkedList{T}, val::T) where T h = q.head while h !== nothing if isequal(h.value, val) list_deletefirst!(q, h) break end h = h.next end return q end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/condition.jl)# This file is a part of Julia. License is MIT: https://julialang.org/license ## thread/task locking abstraction @noinline function concurrency_violation() # can be useful for debugging #try; error(); catch; ccall(:jlbacktrace, Cvoid, ()); end throw(ConcurrencyViolationError("lock must be held")) end """ AbstractLock Abstract supertype describing types that implement the synchronization primitives: [`lock`](@ref), [`trylock`](@ref), [`unlock`](@ref), and [`islocked`](@ref). """ abstract type AbstractLock end function lock end function unlock end function trylock end function islocked end unlockall(l::AbstractLock) = unlock(l) # internal function for implementing `wait` relockall(l::AbstractLock, token::Nothing) = lock(l) # internal function for implementing `wait` assert_havelock(l::AbstractLock, tid::Integer) = (islocked(l) && tid == Threads.threadid()) ? nothing : concurrency_violation() assert_havelock(l::AbstractLock, tid::Task) = (islocked(l) && tid === current_task()) ? nothing : concurrency_violation() assert_havelock(l::AbstractLock, tid::Nothing) = concurrency_violation() """ AlwaysLockedST This struct does not implement a real lock, but instead pretends to be always locked on the original thread it was allocated on, and simply ignores all other interactions. It also does not synchronize tasks; for that use a real lock such as [`ReentrantLock`](@ref). This can be used in the place of a real lock to, instead, simply and cheaply assert that the operation is only occurring on a single cooperatively-scheduled thread. It is thus functionally equivalent to allocating a real, recursive, task-unaware lock immediately calling `lock` on it, and then never calling a matching `unlock`, except that calling `lock` from another thread will throw a concurrency violation exception. """ struct AlwaysLockedST <: AbstractLock ownertid::Int16 AlwaysLockedST() = new(Threads.threadid()) end assert_havelock(l::AlwaysLockedST) = assert_havelock(l, l.ownertid) lock(l::AlwaysLockedST) = assert_havelock(l) unlock(l::AlwaysLockedST) = assert_havelock(l) trylock(l::AlwaysLockedST) = l.ownertid == Threads.threadid() islocked(::AlwaysLockedST) = true ## condition variables """ GenericCondition Abstract implementation of a condition object for synchronizing tasks objects with a given lock. """ struct GenericCondition{L<:AbstractLock} waitq::IntrusiveLinkedList{Task} lock::L GenericCondition{L}() where {L<:AbstractLock} = new{L}(IntrusiveLinkedList{Task}(), L()) GenericCondition{L}(l::L) where {L<:AbstractLock} = new{L}(IntrusiveLinkedList{Task}(), l) GenericCondition(l::AbstractLock) = new{typeof(l)}(IntrusiveLinkedList{Task}(), l) end assert_havelock(c::GenericCondition) = assert_havelock(c.lock) lock(c::GenericCondition) = lock(c.lock) unlock(c::GenericCondition) = unlock(c.lock) trylock(c::GenericCondition) = trylock(c.lock) islocked(c::GenericCondition) = islocked(c.lock) lock(f, c::GenericCondition) = lock(f, c.lock) # have waiter wait for c function _wait2(c::GenericCondition, waiter::Task, first::Bool=false) ct = current_task() assert_havelock(c) if first pushfirst!(c.waitq, waiter) else push!(c.waitq, waiter) end # since _wait2 is similar to schedule, we should observe the sticky bit now if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() # Issue #41324 # t.sticky && tid == 0 is a task that needs to be co-scheduled with # the parent task. If the parent (current_task) is not sticky we must # set it to be sticky. # XXX: Ideally we would be able to unset this ct.sticky = true tid = Threads.threadid() ccall(:jl_set_task_tid, Cint, (Any, Cint), waiter, tid-1) end return end """ wait([x]) Block the current task until some event occurs, depending on the type of the argument: * [`Channel`](@ref): Wait for a value to be appended to the channel. * [`Condition`](@ref): Wait for [`notify`](@ref) on a condition and return the `val` parameter passed to `notify`. Waiting on a condition additionally allows passing `first=true` which results in the waiter being put _first_ in line to wake up on `notify` instead of the usual first-in-first-out behavior. * `Process`: Wait for a process or process chain to exit. The `exitcode` field of a process can be used to determine success or failure. * [`Task`](@ref): Wait for a `Task` to finish. If the task fails with an exception, a `TaskFailedException` (which wraps the failed task) is thrown. * [`RawFD`](@ref): Wait for changes on a file descriptor (see the `FileWatching` package). If no argument is passed, the task blocks for an undefined period. A task can only be restarted by an explicit call to [`schedule`](@ref) or [`yieldto`](@ref). Often `wait` is called within a `while` loop to ensure a waited-for condition is met before proceeding. """ function wait(c::GenericCondition; first::Bool=false) ct = current_task() _wait2(c, ct, first) token = unlockall(c.lock) try return wait() catch ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) rethrow() finally relockall(c.lock, token) end end """ notify(condition, val=nothing; all=true, error=false) Wake up tasks waiting for a condition, passing them `val`. If `all` is `true` (the default), all waiting tasks are woken, otherwise only one is. If `error` is `true`, the passed value is raised as an exception in the woken tasks. Return the count of tasks woken up. Return 0 if no tasks are waiting on `condition`. """ @constprop :none notify(c::GenericCondition, @nospecialize(arg = nothing); all=true, error=false) = notify(c, arg, all, error) function notify(c::GenericCondition, @nospecialize(arg), all, error) assert_havelock(c) cnt = 0 while !isempty(c.waitq) t = popfirst!(c.waitq) schedule(t, arg, error=error) cnt += 1 all || break end return cnt end notify_error(c::GenericCondition, err) = notify(c, err, true, true) """ isempty(condition) Return `true` if no tasks are waiting on the condition, `false` otherwise. """ isempty(c::GenericCondition) = isempty(c.waitq) # default (Julia v1.0) is currently single-threaded # (although it uses MT-safe versions, when possible) """ Condition() Create an edge-triggered event source that tasks can wait for. Tasks that call [`wait`](@ref) on a `Condition` are suspended and queued. Tasks are woken up when [`notify`](@ref) is later called on the `Condition`. Edge triggering means that only tasks waiting at the time [`notify`](@ref) is called can be woken up. For level-triggered notifications, you must keep extra state to keep track of whether a notification has happened. The [`Channel`](@ref) and [`Threads.Event`](@ref) types do this, and can be used for level-triggered events. This object is NOT thread-safe. See [`Threads.Condition`](@ref) for a thread-safe version. """ const Condition = GenericCondition{AlwaysLockedST} lock(c::GenericCondition{AlwaysLockedST}) = throw(ArgumentError("`Condition` is not thread-safe. Please use `Threads.Condition` instead for multi-threaded code.")) unlock(c::GenericCondition{AlwaysLockedST}) = throw(ArgumentError("`Condition` is not thread-safe. Please use `Threads.Condition` instead for multi-threaded code.")) N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/threads.jl)# This file is a part of Julia. License is MIT: https://julialang.org/license """ Multithreading support. """ module Threads global Condition # we'll define this later, make sure we don't import Base.Condition include("threadingconstructs.jl") include("atomics.jl") include("locks-mt.jl") end Z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/threadingconstructs.jl=# This file is a part of Julia. License is MIT: https://julialang.org/license export threadid, nthreads, @threads, @spawn, threadpool, nthreadpools """ Threads.threadid() -> Int Get the ID number of the current thread of execution. The master thread has ID `1`. # Examples ```julia-repl julia> Threads.threadid() 1 julia> Threads.@threads for i in 1:4 println(Threads.threadid()) end 4 2 5 4 ``` !!! note The thread that a task runs on may change if the task yields, which is known as [`Task Migration`](@ref man-task-migration). For this reason in most cases it is not safe to use `threadid()` to index into, say, a vector of buffer or stateful objects. """ threadid() = Int(ccall(:jl_threadid, Int16, ())+1) # lower bound on the largest threadid() """ Threads.maxthreadid() -> Int Get a lower bound on the number of threads (across all thread pools) available to the Julia process, with atomic-acquire semantics. The result will always be greater than or equal to [`threadid()`](@ref) as well as `threadid(task)` for any task you were able to observe before calling `maxthreadid`. """ maxthreadid() = Int(Core.Intrinsics.atomic_pointerref(cglobal(:jl_n_threads, Cint), :acquire)) """ Threads.nthreads(:default | :interactive) -> Int Get the current number of threads within the specified thread pool. The threads in `:interactive` have id numbers `1:nthreads(:interactive)`, and the threads in `:default` have id numbers in `nthreads(:interactive) .+ (1:nthreads(:default))`. See also `BLAS.get_num_threads` and `BLAS.set_num_threads` in the [`LinearAlgebra`](@ref man-linalg) standard library, and `nprocs()` in the [`Distributed`](@ref man-distributed) standard library and [`Threads.maxthreadid()`](@ref). """ nthreads(pool::Symbol) = threadpoolsize(pool) function _nthreads_in_pool(tpid::Int8) p = unsafe_load(cglobal(:jl_n_threads_per_pool, Ptr{Cint})) return Int(unsafe_load(p, tpid + 1)) end function _tpid_to_sym(tpid::Int8) if tpid == 0 return :interactive elseif tpid == 1 return :default elseif tpid == -1 return :foreign else throw(ArgumentError("Unrecognized threadpool id $tpid")) end end function _sym_to_tpid(tp::Symbol) if tp === :interactive return Int8(0) elseif tp === :default return Int8(1) elseif tp == :foreign return Int8(-1) else throw(ArgumentError("Unrecognized threadpool name `$(repr(tp))`")) end end """ Threads.threadpool(tid = threadid()) -> Symbol Returns the specified thread's threadpool; either `:default`, `:interactive`, or `:foreign`. """ function threadpool(tid = threadid()) tpid = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) return _tpid_to_sym(tpid) end """ Threads.nthreadpools() -> Int Returns the number of threadpools currently configured. """ nthreadpools() = Int(unsafe_load(cglobal(:jl_n_threadpools, Cint))) """ Threads.threadpoolsize(pool::Symbol = :default) -> Int Get the number of threads available to the default thread pool (or to the specified thread pool). See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the [`LinearAlgebra`](@ref man-linalg) standard library, and `nprocs()` in the [`Distributed`](@ref man-distributed) standard library. """ function threadpoolsize(pool::Symbol = :default) if pool === :default || pool === :interactive tpid = _sym_to_tpid(pool) elseif pool == :foreign error("Threadpool size of `:foreign` is indeterminant") else error("invalid threadpool specified") end return _nthreads_in_pool(tpid) end """ threadpooltids(pool::Symbol) Returns a vector of IDs of threads in the given pool. """ function threadpooltids(pool::Symbol) ni = _nthreads_in_pool(Int8(0)) if pool === :interactive return collect(1:ni) elseif pool === :default return collect(ni+1:ni+_nthreads_in_pool(Int8(1))) else error("invalid threadpool specified") end end """ Threads.ngcthreads() -> Int Returns the number of GC threads currently configured. This includes both mark threads and concurrent sweep threads. """ ngcthreads() = Int(unsafe_load(cglobal(:jl_n_gcthreads, Cint))) + 1 function threading_run(fun, static) ccall(:jl_enter_threaded_region, Cvoid, ()) n = threadpoolsize() tid_offset = threadpoolsize(:interactive) tasks = Vector{Task}(undef, n) for i = 1:n t = Task(() -> fun(i)) # pass in tid t.sticky = static if static ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid_offset + i-1) else # TODO: this should be the current pool (except interactive) if there # are ever more than two pools. @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) == 1 end tasks[i] = t schedule(t) end for i = 1:n Base._wait(tasks[i]) end ccall(:jl_exit_threaded_region, Cvoid, ()) failed_tasks = filter!(istaskfailed, tasks) if !isempty(failed_tasks) throw(CompositeException(map(TaskFailedException, failed_tasks))) end end function _threadsfor(iter, lbody, schedule) lidx = iter.args[1] # index range = iter.args[2] quote local threadsfor_fun let range = $(esc(range)) function threadsfor_fun(tid = 1; onethread = false) r = range # Load into local variable lenr = length(r) # divide loop iterations among threads if onethread tid = 1 len, rem = lenr, 0 else len, rem = divrem(lenr, threadpoolsize()) end # not enough iterations for all the threads? if len == 0 if tid > rem return end len, rem = 1, 0 end # compute this thread's iterations f = firstindex(r) + ((tid-1) * len) l = f + len - 1 # distribute remaining iterations evenly if rem > 0 if tid <= rem f = f + (tid-1) l = l + tid else f = f + rem l = l + rem end end # run this thread's iterations for i = f:l local $(esc(lidx)) = @inbounds r[i] $(esc(lbody)) end end end if $(schedule === :dynamic || schedule === :default) threading_run(threadsfor_fun, false) elseif ccall(:jl_in_threaded_region, Cint, ()) != 0 # :static error("`@threads :static` cannot be used concurrently or nested") else # :static threading_run(threadsfor_fun, true) end nothing end end """ Threads.@threads [schedule] for ... end A macro to execute a `for` loop in parallel. The iteration space is distributed to coarse-grained tasks. This policy can be specified by the `schedule` argument. The execution of the loop waits for the evaluation of all iterations. See also: [`@spawn`](@ref Threads.@spawn) and `pmap` in [`Distributed`](@ref man-distributed). # Extended help ## Semantics Unless stronger guarantees are specified by the scheduling option, the loop executed by `@threads` macro have the following semantics. The `@threads` macro executes the loop body in an unspecified order and potentially concurrently. It does not specify the exact assignments of the tasks and the worker threads. The assignments can be different for each execution. The loop body code (including any code transitively called from it) must not make any assumptions about the distribution of iterations to tasks or the worker thread in which they are executed. The loop body for each iteration must be able to make forward progress independent of other iterations and be free from data races. As such, invalid synchronizations across iterations may deadlock while unsynchronized memory accesses may result in undefined behavior. For example, the above conditions imply that: - A lock taken in an iteration *must* be released within the same iteration. - Communicating between iterations using blocking primitives like `Channel`s is incorrect. - Write only to locations not shared across iterations (unless a lock or atomic operation is used). - Unless the `:static` schedule is used, the value of [`threadid()`](@ref Threads.threadid) may change even within a single iteration. See [`Task Migration`](@ref man-task-migration). ## Schedulers Without the scheduler argument, the exact scheduling is unspecified and varies across Julia releases. Currently, `:dynamic` is used when the scheduler is not specified. !!! compat "Julia 1.5" The `schedule` argument is available as of Julia 1.5. ### `:dynamic` (default) `:dynamic` scheduler executes iterations dynamically to available worker threads. Current implementation assumes that the workload for each iteration is uniform. However, this assumption may be removed in the future. This scheduling option is merely a hint to the underlying execution mechanism. However, a few properties can be expected. The number of `Task`s used by `:dynamic` scheduler is bounded by a small constant multiple of the number of available worker threads ([`Threads.threadpoolsize()`](@ref)). Each task processes contiguous regions of the iteration space. Thus, `@threads :dynamic for x in xs; f(x); end` is typically more efficient than `@sync for x in xs; @spawn f(x); end` if `length(xs)` is significantly larger than the number of the worker threads and the run-time of `f(x)` is relatively smaller than the cost of spawning and synchronizing a task (typically less than 10 microseconds). !!! compat "Julia 1.8" The `:dynamic` option for the `schedule` argument is available and the default as of Julia 1.8. ### `:static` `:static` scheduler creates one task per thread and divides the iterations equally among them, assigning each task specifically to each thread. In particular, the value of [`threadid()`](@ref Threads.threadid) is guaranteed to be constant within one iteration. Specifying `:static` is an error if used from inside another `@threads` loop or from a thread other than 1. !!! note `:static` scheduling exists for supporting transition of code written before Julia 1.3. In newly written library functions, `:static` scheduling is discouraged because the functions using this option cannot be called from arbitrary worker threads. ## Example To illustrate of the different scheduling strategies, consider the following function `busywait` containing a non-yielding timed loop that runs for a given number of seconds. ```julia-repl julia> function busywait(seconds) tstart = time_ns() while (time_ns() - tstart) / 1e9 < seconds end end julia> @time begin Threads.@spawn busywait(5) Threads.@threads :static for i in 1:Threads.threadpoolsize() busywait(1) end end 6.003001 seconds (16.33 k allocations: 899.255 KiB, 0.25% compilation time) julia> @time begin Threads.@spawn busywait(5) Threads.@threads :dynamic for i in 1:Threads.threadpoolsize() busywait(1) end end 2.012056 seconds (16.05 k allocations: 883.919 KiB, 0.66% compilation time) ``` The `:dynamic` example takes 2 seconds since one of the non-occupied threads is able to run two of the 1-second iterations to complete the for loop. """ macro threads(args...) na = length(args) if na == 2 sched, ex = args if sched isa QuoteNode sched = sched.value elseif sched isa Symbol # for now only allow quoted symbols sched = nothing end if sched !== :static && sched !== :dynamic throw(ArgumentError("unsupported schedule argument in @threads")) end elseif na == 1 sched = :default ex = args[1] else throw(ArgumentError("wrong number of arguments in @threads")) end if !(isa(ex, Expr) && ex.head === :for) throw(ArgumentError("@threads requires a `for` loop expression")) end if !(ex.args[1] isa Expr && ex.args[1].head === :(=)) throw(ArgumentError("nested outer loops are not currently supported by @threads")) end return _threadsfor(ex.args[1], ex.args[2], sched) end function _spawn_set_thrpool(t::Task, tp::Symbol) tpid = _sym_to_tpid(tp) if tpid == -1 || _nthreads_in_pool(tpid) == 0 tpid = _sym_to_tpid(:default) end @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) == 1 nothing end """ Threads.@spawn [:default|:interactive] expr Create a [`Task`](@ref) and [`schedule`](@ref) it to run on any available thread in the specified threadpool (`:default` if unspecified). The task is allocated to a thread once one becomes available. To wait for the task to finish, call [`wait`](@ref) on the result of this macro, or call [`fetch`](@ref) to wait and then obtain its return value. Values can be interpolated into `@spawn` via `\$`, which copies the value directly into the constructed underlying closure. This allows you to insert the _value_ of a variable, isolating the asynchronous code from changes to the variable's value in the current task. !!! note The thread that the task runs on may change if the task yields, therefore `threadid()` should not be treated as constant for a task. See [`Task Migration`](@ref man-task-migration), and the broader [multi-threading](@ref man-multithreading) manual for further important caveats. See also the chapter on [threadpools](@ref man-threadpools). !!! compat "Julia 1.3" This macro is available as of Julia 1.3. !!! compat "Julia 1.4" Interpolating values via `\$` is available as of Julia 1.4. !!! compat "Julia 1.9" A threadpool may be specified as of Julia 1.9. # Examples ```julia-repl julia> t() = println("Hello from ", Threads.threadid()); julia> tasks = fetch.([Threads.@spawn t() for i in 1:4]); Hello from 1 Hello from 1 Hello from 3 Hello from 4 ``` """ macro spawn(args...) tp = QuoteNode(:default) na = length(args) if na == 2 ttype, ex = args if ttype isa QuoteNode ttype = ttype.value if ttype !== :interactive && ttype !== :default throw(ArgumentError("unsupported threadpool in @spawn: $ttype")) end tp = QuoteNode(ttype) else tp = ttype end elseif na == 1 ex = args[1] else throw(ArgumentError("wrong number of arguments in @spawn")) end letargs = Base._lift_one_interp!(ex) thunk = Base.replace_linenums!(:(()->($(esc(ex)))), __source__) var = esc(Base.sync_varname) quote let $(letargs...) local task = Task($thunk) task.sticky = false _spawn_set_thrpool(task, $(esc(tp))) if $(Expr(:islocal, var)) put!($var, task) end schedule(task) task end end end # This is a stub that can be overloaded for downstream structures like `Channel` function foreach end # Scheduling traits that can be employed for downstream overloads abstract type AbstractSchedule end struct StaticSchedule <: AbstractSchedule end struct FairSchedule <: AbstractSchedule end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/atomics.jlใ1# This file is a part of Julia. License is MIT: https://julialang.org/license using Core.Intrinsics: llvmcall import .Base: setindex!, getindex, unsafe_convert import .Base.Sys: ARCH, WORD_SIZE export Atomic, atomic_cas!, atomic_xchg!, atomic_add!, atomic_sub!, atomic_and!, atomic_nand!, atomic_or!, atomic_xor!, atomic_max!, atomic_min!, atomic_fence ## # Filter out unsupported atomic types on platforms # - 128-bit atomics do not exist on AArch32. # - Omitting 128-bit types on 32bit x86 and ppc64 # - LLVM doesn't currently support atomics on floats for ppc64 # C++20 is adding limited support for atomics on float, but as of # now Clang does not support that yet. if Sys.ARCH === :i686 || startswith(string(Sys.ARCH), "arm") || Sys.ARCH === :powerpc64le || Sys.ARCH === :ppc64le const inttypes = (Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64) else const inttypes = (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128) end const floattypes = (Float16, Float32, Float64) const arithmetictypes = (inttypes..., floattypes...) # TODO: Support Ptr if Sys.ARCH === :powerpc64le || Sys.ARCH === :ppc64le const atomictypes = (inttypes..., Bool) else const atomictypes = (arithmetictypes..., Bool) end const IntTypes = Union{inttypes...} const FloatTypes = Union{floattypes...} const ArithmeticTypes = Union{arithmetictypes...} const AtomicTypes = Union{atomictypes...} """ Threads.Atomic{T} Holds a reference to an object of type `T`, ensuring that it is only accessed atomically, i.e. in a thread-safe manner. Only certain "simple" types can be used atomically, namely the primitive boolean, integer, and float-point types. These are `Bool`, `Int8`...`Int128`, `UInt8`...`UInt128`, and `Float16`...`Float64`. New atomic objects can be created from a non-atomic values; if none is specified, the atomic object is initialized with zero. Atomic objects can be accessed using the `[]` notation: # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> x[] = 1 1 julia> x[] 1 ``` Atomic operations use an `atomic_` prefix, such as [`atomic_add!`](@ref), [`atomic_xchg!`](@ref), etc. """ mutable struct Atomic{T<:AtomicTypes} value::T Atomic{T}() where {T<:AtomicTypes} = new(zero(T)) Atomic{T}(value) where {T<:AtomicTypes} = new(value) end Atomic() = Atomic{Int}() """ Threads.atomic_cas!(x::Atomic{T}, cmp::T, newval::T) where T Atomically compare-and-set `x` Atomically compares the value in `x` with `cmp`. If equal, write `newval` to `x`. Otherwise, leaves `x` unmodified. Returns the old value in `x`. By comparing the returned value to `cmp` (via `===`) one knows whether `x` was modified and now holds the new value `newval`. For further details, see LLVM's `cmpxchg` instruction. This function can be used to implement transactional semantics. Before the transaction, one records the value in `x`. After the transaction, the new value is stored only if `x` has not been modified in the mean time. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_cas!(x, 4, 2); julia> x Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_cas!(x, 3, 2); julia> x Base.Threads.Atomic{Int64}(2) ``` """ function atomic_cas! end """ Threads.atomic_xchg!(x::Atomic{T}, newval::T) where T Atomically exchange the value in `x` Atomically exchanges the value in `x` with `newval`. Returns the **old** value. For further details, see LLVM's `atomicrmw xchg` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_xchg!(x, 2) 3 julia> x[] 2 ``` """ function atomic_xchg! end """ Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes Atomically add `val` to `x` Performs `x[] += val` atomically. Returns the **old** value. Not defined for `Atomic{Bool}`. For further details, see LLVM's `atomicrmw add` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_add!(x, 2) 3 julia> x[] 5 ``` """ function atomic_add! end """ Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes Atomically subtract `val` from `x` Performs `x[] -= val` atomically. Returns the **old** value. Not defined for `Atomic{Bool}`. For further details, see LLVM's `atomicrmw sub` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_sub!(x, 2) 3 julia> x[] 1 ``` """ function atomic_sub! end """ Threads.atomic_and!(x::Atomic{T}, val::T) where T Atomically bitwise-and `x` with `val` Performs `x[] &= val` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw and` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_and!(x, 2) 3 julia> x[] 2 ``` """ function atomic_and! end """ Threads.atomic_nand!(x::Atomic{T}, val::T) where T Atomically bitwise-nand (not-and) `x` with `val` Performs `x[] = ~(x[] & val)` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw nand` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(3) Base.Threads.Atomic{Int64}(3) julia> Threads.atomic_nand!(x, 2) 3 julia> x[] -3 ``` """ function atomic_nand! end """ Threads.atomic_or!(x::Atomic{T}, val::T) where T Atomically bitwise-or `x` with `val` Performs `x[] |= val` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw or` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(5) Base.Threads.Atomic{Int64}(5) julia> Threads.atomic_or!(x, 7) 5 julia> x[] 7 ``` """ function atomic_or! end """ Threads.atomic_xor!(x::Atomic{T}, val::T) where T Atomically bitwise-xor (exclusive-or) `x` with `val` Performs `x[] \$= val` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw xor` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(5) Base.Threads.Atomic{Int64}(5) julia> Threads.atomic_xor!(x, 7) 5 julia> x[] 2 ``` """ function atomic_xor! end """ Threads.atomic_max!(x::Atomic{T}, val::T) where T Atomically store the maximum of `x` and `val` in `x` Performs `x[] = max(x[], val)` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw max` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(5) Base.Threads.Atomic{Int64}(5) julia> Threads.atomic_max!(x, 7) 5 julia> x[] 7 ``` """ function atomic_max! end """ Threads.atomic_min!(x::Atomic{T}, val::T) where T Atomically store the minimum of `x` and `val` in `x` Performs `x[] = min(x[], val)` atomically. Returns the **old** value. For further details, see LLVM's `atomicrmw min` instruction. # Examples ```jldoctest julia> x = Threads.Atomic{Int}(7) Base.Threads.Atomic{Int64}(7) julia> Threads.atomic_min!(x, 5) 7 julia> x[] 5 ``` """ function atomic_min! end unsafe_convert(::Type{Ptr{T}}, x::Atomic{T}) where {T} = convert(Ptr{T}, pointer_from_objref(x)) setindex!(x::Atomic{T}, v) where {T} = setindex!(x, convert(T, v)) const llvmtypes = IdDict{Any,String}( Bool => "i8", # julia represents bools with 8-bits for now. # TODO: is this okay? Int8 => "i8", UInt8 => "i8", Int16 => "i16", UInt16 => "i16", Int32 => "i32", UInt32 => "i32", Int64 => "i64", UInt64 => "i64", Int128 => "i128", UInt128 => "i128", Float16 => "half", Float32 => "float", Float64 => "double", ) inttype(::Type{T}) where {T<:Integer} = T inttype(::Type{Float16}) = Int16 inttype(::Type{Float32}) = Int32 inttype(::Type{Float64}) = Int64 import ..Base.gc_alignment # All atomic operations have acquire and/or release semantics, depending on # whether the load or store values. Most of the time, this is what one wants # anyway, and it's only moderately expensive on most hardware. for typ in atomictypes lt = llvmtypes[typ] ilt = llvmtypes[inttype(typ)] rt = "$lt, $lt*" irt = "$ilt, $ilt*" @eval getindex(x::Atomic{$typ}) = GC.@preserve x llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* %rv = load atomic $rt %ptr acquire, align $(gc_alignment(typ)) ret $lt %rv """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) @eval setindex!(x::Atomic{$typ}, v::$typ) = GC.@preserve x llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* store atomic $lt %1, $lt* %ptr release, align $(gc_alignment(typ)) ret void """, Cvoid, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) # Note: atomic_cas! succeeded (i.e. it stored "new") if and only if the result is "cmp" if typ <: Integer @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = GC.@preserve x llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* %rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 acq_rel acquire %rv = extractvalue { $lt, i1 } %rs, 0 ret $lt %rv """, $typ, Tuple{Ptr{$typ},$typ,$typ}, unsafe_convert(Ptr{$typ}, x), cmp, new) else @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = GC.@preserve x llvmcall($""" %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %icmp = bitcast $lt %1 to $ilt %inew = bitcast $lt %2 to $ilt %irs = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel acquire %irv = extractvalue { $ilt, i1 } %irs, 0 %rv = bitcast $ilt %irv to $lt ret $lt %rv """, $typ, Tuple{Ptr{$typ},$typ,$typ}, unsafe_convert(Ptr{$typ}, x), cmp, new) end arithmetic_ops = [:add, :sub] for rmwop in [arithmetic_ops..., :xchg, :and, :nand, :or, :xor, :max, :min] rmw = string(rmwop) fn = Symbol("atomic_", rmw, "!") if (rmw == "max" || rmw == "min") && typ <: Unsigned # LLVM distinguishes signedness in the operation, not the integer type. rmw = "u" * rmw end if rmwop in arithmetic_ops && !(typ <: ArithmeticTypes) continue end if typ <: Integer @eval $fn(x::Atomic{$typ}, v::$typ) = GC.@preserve x llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* %rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) else rmwop === :xchg || continue @eval $fn(x::Atomic{$typ}, v::$typ) = GC.@preserve x llvmcall($""" %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %ival = bitcast $lt %1 to $ilt %irv = atomicrmw $rmw $ilt* %iptr, $ilt %ival acq_rel %rv = bitcast $ilt %irv to $lt ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) end end end # Provide atomic floating-point operations via atomic_cas! const opnames = Dict{Symbol, Symbol}(:+ => :add, :- => :sub) for op in [:+, :-, :max, :min] opname = get(opnames, op, op) @eval function $(Symbol("atomic_", opname, "!"))(var::Atomic{T}, val::T) where T<:FloatTypes IT = inttype(T) old = var[] while true new = $op(old, val) cmp = old old = atomic_cas!(var, cmp, new) reinterpret(IT, old) == reinterpret(IT, cmp) && return old # Temporary solution before we have gc transition support in codegen. ccall(:jl_gc_safepoint, Cvoid, ()) end end end """ Threads.atomic_fence() Insert a sequential-consistency memory fence Inserts a memory fence with sequentially-consistent ordering semantics. There are algorithms where this is needed, i.e. where an acquire/release ordering is insufficient. This is likely a very expensive operation. Given that all other atomic operations in Julia already have acquire/release semantics, explicit fences should not be necessary in most cases. For further details, see LLVM's `fence` instruction. """ atomic_fence() = llvmcall(""" fence seq_cst ret void """, Cvoid, Tuple{}) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/locks-mt.jl # This file is a part of Julia. License is MIT: https://julialang.org/license import .Base: unsafe_convert, lock, trylock, unlock, islocked, wait, notify, AbstractLock export SpinLock # Important Note: these low-level primitives defined here # are typically not for general usage ########################################## # Atomic Locks ########################################## """ SpinLock() Create a non-reentrant, test-and-test-and-set spin lock. Recursive use will result in a deadlock. This kind of lock should only be used around code that takes little time to execute and does not block (e.g. perform I/O). In general, [`ReentrantLock`](@ref) should be used instead. Each [`lock`](@ref) must be matched with an [`unlock`](@ref). If [`!islocked(lck::SpinLock)`](@ref islocked) holds, [`trylock(lck)`](@ref trylock) succeeds unless there are other tasks attempting to hold the lock "at the same time." Test-and-test-and-set spin locks are quickest up to about 30ish contending threads. If you have more contention than that, different synchronization approaches should be considered. """ mutable struct SpinLock <: AbstractLock # we make this much larger than necessary to minimize false-sharing @atomic owned::Int SpinLock() = new(0) end # Note: this cannot assert that the lock is held by the correct thread, because we do not # track which thread locked it. Users beware. Base.assert_havelock(l::SpinLock) = islocked(l) ? nothing : Base.concurrency_violation() function lock(l::SpinLock) while true if @inline trylock(l) return end ccall(:jl_cpu_suspend, Cvoid, ()) # Temporary solution before we have gc transition support in codegen. ccall(:jl_gc_safepoint, Cvoid, ()) end end function trylock(l::SpinLock) if l.owned == 0 GC.disable_finalizers() p = @atomicswap :acquire l.owned = 1 if p == 0 return true end GC.enable_finalizers() end return false end function unlock(l::SpinLock) if (@atomicswap :release l.owned = 0) == 0 error("unlock count must match lock count") end GC.enable_finalizers() ccall(:jl_cpu_wake, Cvoid, ()) return end function islocked(l::SpinLock) return (@atomic :monotonic l.owned) != 0 end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/lock.jl 4# This file is a part of Julia. License is MIT: https://julialang.org/license const ThreadSynchronizer = GenericCondition{Threads.SpinLock} # Advisory reentrant lock """ ReentrantLock() Creates a re-entrant lock for synchronizing [`Task`](@ref)s. The same task can acquire the lock as many times as required (this is what the "Reentrant" part of the name means). Each [`lock`](@ref) must be matched with an [`unlock`](@ref). Calling 'lock' will also inhibit running of finalizers on that thread until the corresponding 'unlock'. Use of the standard lock pattern illustrated below should naturally be supported, but beware of inverting the try/lock order or missing the try block entirely (e.g. attempting to return with the lock still held): This provides a acquire/release memory ordering on lock/unlock calls. ``` lock(l) try finally unlock(l) end ``` If [`!islocked(lck::ReentrantLock)`](@ref islocked) holds, [`trylock(lck)`](@ref trylock) succeeds unless there are other tasks attempting to hold the lock "at the same time." """ mutable struct ReentrantLock <: AbstractLock # offset = 16 @atomic locked_by::Union{Task, Nothing} # offset32 = 20, offset64 = 24 reentrancy_cnt::UInt32 # offset32 = 24, offset64 = 28 @atomic havelock::UInt8 # 0x0 = none, 0x1 = lock, 0x2 = conflict # offset32 = 28, offset64 = 32 cond_wait::ThreadSynchronizer # 2 words # offset32 = 36, offset64 = 48 # sizeof32 = 20, sizeof64 = 32 # now add padding to make this a full cache line to minimize false sharing between objects _::NTuple{Int === Int32 ? 2 : 3, Int} # offset32 = 44, offset64 = 72 == sizeof+offset # sizeof32 = 28, sizeof64 = 56 ReentrantLock() = new(nothing, 0x0000_0000, 0x00, ThreadSynchronizer()) end assert_havelock(l::ReentrantLock) = assert_havelock(l, l.locked_by) """ islocked(lock) -> Status (Boolean) Check whether the `lock` is held by any task/thread. This function alone should not be used for synchronization. However, `islocked` combined with [`trylock`](@ref) can be used for writing the test-and-test-and-set or exponential backoff algorithms *if it is supported by the `typeof(lock)`* (read its documentation). # Extended help For example, an exponential backoff can be implemented as follows if the `lock` implementation satisfied the properties documented below. ```julia nspins = 0 while true while islocked(lock) GC.safepoint() nspins += 1 nspins > LIMIT && error("timeout") end trylock(lock) && break backoff() end ``` ## Implementation A lock implementation is advised to define `islocked` with the following properties and note it in its docstring. * `islocked(lock)` is data-race-free. * If `islocked(lock)` returns `false`, an immediate invocation of `trylock(lock)` must succeed (returns `true`) if there is no interference from other tasks. """ function islocked end # Above docstring is a documentation for the abstract interface and not the one specific to # `ReentrantLock`. function islocked(rl::ReentrantLock) return (@atomic :monotonic rl.havelock) != 0 end """ trylock(lock) -> Success (Boolean) Acquire the lock if it is available, and return `true` if successful. If the lock is already locked by a different task/thread, return `false`. Each successful `trylock` must be matched by an [`unlock`](@ref). Function `trylock` combined with [`islocked`](@ref) can be used for writing the test-and-test-and-set or exponential backoff algorithms *if it is supported by the `typeof(lock)`* (read its documentation). """ function trylock end # Above docstring is a documentation for the abstract interface and not the one specific to # `ReentrantLock`. @inline function trylock(rl::ReentrantLock) ct = current_task() if rl.locked_by === ct #@assert rl.havelock !== 0x00 rl.reentrancy_cnt += 0x0000_0001 return true end return _trylock(rl, ct) end @noinline function _trylock(rl::ReentrantLock, ct::Task) GC.disable_finalizers() if (@atomicreplace :acquire rl.havelock 0x00 => 0x01).success #@assert rl.locked_by === nothing #@assert rl.reentrancy_cnt === 0 rl.reentrancy_cnt = 0x0000_0001 @atomic :release rl.locked_by = ct return true end GC.enable_finalizers() return false end """ lock(lock) Acquire the `lock` when it becomes available. If the lock is already locked by a different task/thread, wait for it to become available. Each `lock` must be matched by an [`unlock`](@ref). """ @inline function lock(rl::ReentrantLock) trylock(rl) || (@noinline function slowlock(rl::ReentrantLock) c = rl.cond_wait lock(c.lock) try while true if (@atomicreplace rl.havelock 0x01 => 0x02).old == 0x00 # :sequentially_consistent ? # now either 0x00 or 0x02 # it was unlocked, so try to lock it ourself _trylock(rl, current_task()) && break else # it was locked, so now wait for the release to notify us wait(c) end end finally unlock(c.lock) end end)(rl) return end """ unlock(lock) Releases ownership of the `lock`. If this is a recursive lock which has been acquired before, decrement an internal counter and return immediately. """ @inline function unlock(rl::ReentrantLock) rl.locked_by === current_task() || error(rl.reentrancy_cnt == 0x0000_0000 ? "unlock count must match lock count" : "unlock from wrong thread") (@noinline function _unlock(rl::ReentrantLock) n = rl.reentrancy_cnt - 0x0000_0001 rl.reentrancy_cnt = n if n == 0x0000_00000 @atomic :monotonic rl.locked_by = nothing if (@atomicswap :release rl.havelock = 0x00) == 0x02 (@noinline function notifywaiters(rl) cond_wait = rl.cond_wait lock(cond_wait) try notify(cond_wait) finally unlock(cond_wait) end end)(rl) end return true end return false end)(rl) && GC.enable_finalizers() nothing end function unlockall(rl::ReentrantLock) n = @atomicswap :not_atomic rl.reentrancy_cnt = 0x0000_0001 unlock(rl) return n end function relockall(rl::ReentrantLock, n::UInt32) lock(rl) old = @atomicswap :not_atomic rl.reentrancy_cnt = n old == 0x0000_0001 || concurrency_violation() return end """ lock(f::Function, lock) Acquire the `lock`, execute `f` with the `lock` held, and release the `lock` when `f` returns. If the lock is already locked by a different task/thread, wait for it to become available. When this function returns, the `lock` has been released, so the caller should not attempt to `unlock` it. !!! compat "Julia 1.7" Using a [`Channel`](@ref) as the second argument requires Julia 1.7 or later. """ function lock(f, l::AbstractLock) lock(l) try return f() finally unlock(l) end end function trylock(f, l::AbstractLock) if trylock(l) try return f() finally unlock(l) end end return false end """ @lock l expr Macro version of `lock(f, l::AbstractLock)` but with `expr` instead of `f` function. Expands to: ```julia lock(l) try expr finally unlock(l) end ``` This is similar to using [`lock`](@ref) with a `do` block, but avoids creating a closure and thus can improve the performance. """ macro lock(l, expr) quote temp = $(esc(l)) lock(temp) try $(esc(expr)) finally unlock(temp) end end end """ @lock_nofail l expr Equivalent to `@lock l expr` for cases in which we can guarantee that the function will not throw any error. In this case, avoiding try-catch can improve the performance. See [`@lock`](@ref). """ macro lock_nofail(l, expr) quote temp = $(esc(l)) lock(temp) val = $(esc(expr)) unlock(temp) val end end @eval Threads begin """ Threads.Condition([lock]) A thread-safe version of [`Base.Condition`](@ref). To call [`wait`](@ref) or [`notify`](@ref) on a `Threads.Condition`, you must first call [`lock`](@ref) on it. When `wait` is called, the lock is atomically released during blocking, and will be reacquired before `wait` returns. Therefore idiomatic use of a `Threads.Condition` `c` looks like the following: ``` lock(c) try while !thing_we_are_waiting_for wait(c) end finally unlock(c) end ``` !!! compat "Julia 1.2" This functionality requires at least Julia 1.2. """ const Condition = Base.GenericCondition{Base.ReentrantLock} """ Special note for [`Threads.Condition`](@ref): The caller must be holding the [`lock`](@ref) that owns a `Threads.Condition` before calling this method. The calling task will be blocked until some other task wakes it, usually by calling [`notify`](@ref) on the same `Threads.Condition` object. The lock will be atomically released when blocking (even if it was locked recursively), and will be reacquired before returning. """ wait(c::Condition) end """ Semaphore(sem_size) Create a counting semaphore that allows at most `sem_size` acquires to be in use at any time. Each acquire must be matched with a release. This provides a acquire & release memory ordering on acquire/release calls. """ mutable struct Semaphore sem_size::Int curr_cnt::Int cond_wait::Threads.Condition Semaphore(sem_size) = sem_size > 0 ? new(sem_size, 0, Threads.Condition()) : throw(ArgumentError("Semaphore size must be > 0")) end """ acquire(s::Semaphore) Wait for one of the `sem_size` permits to be available, blocking until one can be acquired. """ function acquire(s::Semaphore) lock(s.cond_wait) try while s.curr_cnt >= s.sem_size wait(s.cond_wait) end s.curr_cnt = s.curr_cnt + 1 finally unlock(s.cond_wait) end return end """ acquire(f, s::Semaphore) Execute `f` after acquiring from Semaphore `s`, and `release` on completion or error. For example, a do-block form that ensures only 2 calls of `foo` will be active at the same time: ```julia s = Base.Semaphore(2) @sync for _ in 1:100 Threads.@spawn begin Base.acquire(s) do foo() end end end ``` !!! compat "Julia 1.8" This method requires at least Julia 1.8. """ function acquire(f, s::Semaphore) acquire(s) try return f() finally release(s) end end """ release(s::Semaphore) Return one permit to the pool, possibly allowing another task to acquire it and resume execution. """ function release(s::Semaphore) lock(s.cond_wait) try s.curr_cnt > 0 || error("release count must match acquire count") s.curr_cnt -= 1 notify(s.cond_wait; all=false) finally unlock(s.cond_wait) end return end """ Event([autoreset=false]) Create a level-triggered event source. Tasks that call [`wait`](@ref) on an `Event` are suspended and queued until [`notify`](@ref) is called on the `Event`. After `notify` is called, the `Event` remains in a signaled state and tasks will no longer block when waiting for it, until `reset` is called. If `autoreset` is true, at most one task will be released from `wait` for each call to `notify`. This provides an acquire & release memory ordering on notify/wait. !!! compat "Julia 1.1" This functionality requires at least Julia 1.1. !!! compat "Julia 1.8" The `autoreset` functionality and memory ordering guarantee requires at least Julia 1.8. """ mutable struct Event notify::Threads.Condition autoreset::Bool @atomic set::Bool Event(autoreset::Bool=false) = new(Threads.Condition(), autoreset, false) end function wait(e::Event) if e.autoreset (@atomicswap :acquire_release e.set = false) && return else (@atomic e.set) && return # full barrier also end lock(e.notify) # acquire barrier try if e.autoreset (@atomicswap :acquire_release e.set = false) && return else e.set && return end wait(e.notify) finally unlock(e.notify) # release barrier end nothing end function notify(e::Event) lock(e.notify) # acquire barrier try if e.autoreset if notify(e.notify, all=false) == 0 @atomic :release e.set = true end elseif !e.set @atomic :release e.set = true notify(e.notify) end finally unlock(e.notify) end nothing end """ reset(::Event) Reset an [`Event`](@ref) back into an un-set state. Then any future calls to `wait` will block until [`notify`](@ref) is called again. """ function reset(e::Event) @atomic e.set = false # full barrier nothing end @eval Threads begin import .Base: Event export Event end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/channels.jlญB# This file is a part of Julia. License is MIT: https://julialang.org/license """ AbstractChannel{T} Representation of a channel passing objects of type `T`. """ abstract type AbstractChannel{T} end push!(c::AbstractChannel, v) = (put!(c, v); c) popfirst!(c::AbstractChannel) = take!(c) """ Channel{T=Any}(size::Int=0) Constructs a `Channel` with an internal buffer that can hold a maximum of `size` objects of type `T`. [`put!`](@ref) calls on a full channel block until an object is removed with [`take!`](@ref). `Channel(0)` constructs an unbuffered channel. `put!` blocks until a matching `take!` is called. And vice-versa. Other constructors: * `Channel()`: default constructor, equivalent to `Channel{Any}(0)` * `Channel(Inf)`: equivalent to `Channel{Any}(typemax(Int))` * `Channel(sz)`: equivalent to `Channel{Any}(sz)` !!! compat "Julia 1.3" The default constructor `Channel()` and default `size=0` were added in Julia 1.3. """ mutable struct Channel{T} <: AbstractChannel{T} cond_take::Threads.Condition # waiting for data to become available cond_wait::Threads.Condition # waiting for data to become maybe available cond_put::Threads.Condition # waiting for a writeable slot @atomic state::Symbol excp::Union{Exception, Nothing} # exception to be thrown when state !== :open data::Vector{T} @atomic n_avail_items::Int # Available items for taking, can be read without lock sz_max::Int # maximum size of channel function Channel{T}(sz::Integer = 0) where T if sz < 0 throw(ArgumentError("Channel size must be either 0, a positive integer or Inf")) end lock = ReentrantLock() cond_put, cond_take = Threads.Condition(lock), Threads.Condition(lock) cond_wait = (sz == 0 ? Threads.Condition(lock) : cond_take) # wait is distinct from take iff unbuffered return new(cond_take, cond_wait, cond_put, :open, nothing, Vector{T}(), 0, sz) end end function Channel{T}(sz::Float64) where T sz = (sz == Inf ? typemax(Int) : convert(Int, sz)) return Channel{T}(sz) end Channel(sz=0) = Channel{Any}(sz) # special constructors """ Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) Create a new task from `func`, bind it to a new channel of type `T` and size `size`, and schedule the task, all in a single call. The channel is automatically closed when the task terminates. `func` must accept the bound channel as its only argument. If you need a reference to the created task, pass a `Ref{Task}` object via the keyword argument `taskref`. If `spawn=true`, the `Task` created for `func` may be scheduled on another thread in parallel, equivalent to creating a task via [`Threads.@spawn`](@ref). If `spawn=true` and the `threadpool` argument is not set, it defaults to `:default`. If the `threadpool` argument is set (to `:default` or `:interactive`), this implies that `spawn=true` and the new Task is spawned to the specified threadpool. Return a `Channel`. # Examples ```jldoctest julia> chnl = Channel() do ch foreach(i -> put!(ch, i), 1:4) end; julia> typeof(chnl) Channel{Any} julia> for i in chnl @show i end; i = 1 i = 2 i = 3 i = 4 ``` Referencing the created task: ```jldoctest julia> taskref = Ref{Task}(); julia> chnl = Channel(taskref=taskref) do ch println(take!(ch)) end; julia> istaskdone(taskref[]) false julia> put!(chnl, "Hello"); Hello julia> istaskdone(taskref[]) true ``` !!! compat "Julia 1.3" The `spawn=` parameter was added in Julia 1.3. This constructor was added in Julia 1.3. In earlier versions of Julia, Channel used keyword arguments to set `size` and `T`, but those constructors are deprecated. !!! compat "Julia 1.9" The `threadpool=` argument was added in Julia 1.9. ```jldoctest julia> chnl = Channel{Char}(1, spawn=true) do ch for c in "hello world" put!(ch, c) end end Channel{Char}(1) (2 items available) julia> String(collect(chnl)) "hello world" ``` """ function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) where T chnl = Channel{T}(size) task = Task(() -> func(chnl)) if threadpool === nothing threadpool = :default else spawn = true end task.sticky = !spawn bind(chnl, task) if spawn Threads._spawn_set_thrpool(task, threadpool) schedule(task) # start it on (potentially) another thread else yield(task) # immediately start it, yielding the current thread end isa(taskref, Ref{Task}) && (taskref[] = task) return chnl end Channel(func::Function, args...; kwargs...) = Channel{Any}(func, args...; kwargs...) # This constructor is deprecated as of Julia v1.3, and should not be used. # (Note that this constructor also matches `Channel(::Function)` w/out any kwargs, which is # of course not deprecated.) # We use `nothing` default values to check which arguments were set in order to throw the # deprecation warning if users try to use `spawn=` with `ctype=` or `csize=`. function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing, threadpool=nothing) # The spawn= keyword argument was added in Julia v1.3, and cannot be used with the # deprecated keyword arguments `ctype=` or `csize=`. if (ctype !== nothing || csize !== nothing) && (spawn !== nothing || threadpool !== nothing) throw(ArgumentError("Cannot set `spawn=` or `threadpool=` in the deprecated constructor `Channel(f; ctype=Any, csize=0)`. Please use `Channel{T=Any}(f, size=0; taskref=nothing, spawn=false, threadpool=nothing)` instead!")) end # Set the actual default values for the arguments. ctype === nothing && (ctype = Any) csize === nothing && (csize = 0) spawn === nothing && (spawn = false) return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn, threadpool=threadpool) end closed_exception() = InvalidStateException("Channel is closed.", :closed) isbuffered(c::Channel) = c.sz_max==0 ? false : true function check_channel_state(c::Channel) if !isopen(c) # if the monotonic load succeed, now do an acquire fence (@atomic :acquire c.state) === :open && concurrency_violation() excp = c.excp excp !== nothing && throw(excp) throw(closed_exception()) end end """ close(c::Channel[, excp::Exception]) Close a channel. An exception (optionally given by `excp`), is thrown by: * [`put!`](@ref) on a closed channel. * [`take!`](@ref) and [`fetch`](@ref) on an empty, closed channel. """ close(c::Channel) = close(c, closed_exception()) # nospecialize on default arg seems to confuse makedocs function close(c::Channel, @nospecialize(excp::Exception)) lock(c) try c.excp = excp @atomic :release c.state = :closed notify_error(c.cond_take, excp) notify_error(c.cond_wait, excp) notify_error(c.cond_put, excp) finally unlock(c) end nothing end # Use acquire here to pair with release store in `close`, so that subsequent `isready` calls # are forced to see `isready == true` if they see `isopen == false`. This means users must # call `isopen` before `isready` if you are using the race-y APIs (or call `iterate`, which # does this right for you). isopen(c::Channel) = ((@atomic :acquire c.state) === :open) """ bind(chnl::Channel, task::Task) Associate the lifetime of `chnl` with a task. `Channel` `chnl` is automatically closed when the task terminates. Any uncaught exception in the task is propagated to all waiters on `chnl`. The `chnl` object can be explicitly closed independent of task termination. Terminating tasks have no effect on already closed `Channel` objects. When a channel is bound to multiple tasks, the first task to terminate will close the channel. When multiple channels are bound to the same task, termination of the task will close all of the bound channels. # Examples ```jldoctest julia> c = Channel(0); julia> task = @async foreach(i->put!(c, i), 1:4); julia> bind(c,task); julia> for i in c @show i end; i = 1 i = 2 i = 3 i = 4 julia> isopen(c) false ``` ```jldoctest julia> c = Channel(0); julia> task = @async (put!(c, 1); error("foo")); julia> bind(c, task); julia> take!(c) 1 julia> put!(c, 1); ERROR: TaskFailedException Stacktrace: [...] nested task error: foo [...] ``` """ function bind(c::Channel, task::Task) T = Task(() -> close_chnl_on_taskdone(task, c)) T.sticky = false _wait2(task, T) return c end """ channeled_tasks(n::Int, funcs...; ctypes=fill(Any,n), csizes=fill(0,n)) A convenience method to create `n` channels and bind them to tasks started from the provided functions in a single call. Each `func` must accept `n` arguments which are the created channels. Channel types and sizes may be specified via keyword arguments `ctypes` and `csizes` respectively. If unspecified, all channels are of type `Channel{Any}(0)`. Returns a tuple, `(Array{Channel}, Array{Task})`, of the created channels and tasks. """ function channeled_tasks(n::Int, funcs...; ctypes=fill(Any,n), csizes=fill(0,n)) @assert length(csizes) == n @assert length(ctypes) == n chnls = map(i -> Channel{ctypes[i]}(csizes[i]), 1:n) tasks = Task[ Task(() -> f(chnls...)) for f in funcs ] # bind all tasks to all channels and schedule them foreach(t -> foreach(c -> bind(c, t), chnls), tasks) foreach(schedule, tasks) yield() # Allow scheduled tasks to run return (chnls, tasks) end function close_chnl_on_taskdone(t::Task, c::Channel) isopen(c) || return lock(c) try isopen(c) || return if istaskfailed(t) close(c, TaskFailedException(t)) return end close(c) finally unlock(c) end nothing end struct InvalidStateException <: Exception msg::String state::Symbol end showerror(io::IO, ex::InvalidStateException) = print(io, "InvalidStateException: ", ex.msg) """ put!(c::Channel, v) Append an item `v` to the channel `c`. Blocks if the channel is full. For unbuffered channels, blocks until a [`take!`](@ref) is performed by a different task. !!! compat "Julia 1.1" `v` now gets converted to the channel's type with [`convert`](@ref) as `put!` is called. """ function put!(c::Channel{T}, v) where T check_channel_state(c) v = convert(T, v) return isbuffered(c) ? put_buffered(c, v) : put_unbuffered(c, v) end # Atomically update channel n_avail, *assuming* we hold the channel lock. function _increment_n_avail(c, inc) # We hold the channel lock so it's safe to non-atomically read and # increment c.n_avail_items newlen = c.n_avail_items + inc # Atomically store c.n_avail_items to prevent data races with other threads # reading this outside the lock. @atomic :monotonic c.n_avail_items = newlen end function put_buffered(c::Channel, v) lock(c) did_buffer = false try # Increment channel n_avail eagerly (before push!) to count data in the # buffer as well as offers from tasks which are blocked in wait(). _increment_n_avail(c, 1) while length(c.data) == c.sz_max check_channel_state(c) wait(c.cond_put) end check_channel_state(c) push!(c.data, v) did_buffer = true # notify all, since some of the waiters may be on a "fetch" call. notify(c.cond_take, nothing, true, false) finally # Decrement the available items if this task had an exception before pushing the # item to the buffer (e.g., during `wait(c.cond_put)`): did_buffer || _increment_n_avail(c, -1) unlock(c) end return v end function put_unbuffered(c::Channel, v) lock(c) taker = try _increment_n_avail(c, 1) while isempty(c.cond_take.waitq) check_channel_state(c) notify(c.cond_wait) wait(c.cond_put) end check_channel_state(c) # unfair scheduled version of: notify(c.cond_take, v, false, false); yield() popfirst!(c.cond_take.waitq) finally _increment_n_avail(c, -1) unlock(c) end schedule(taker, v) yield() # immediately give taker a chance to run, but don't block the current task return v end """ fetch(c::Channel) Waits for and returns (without removing) the first available item from the `Channel`. Note: `fetch` is unsupported on an unbuffered (0-size) `Channel`. # Examples Buffered channel: ```jldoctest julia> c = Channel(3) do ch foreach(i -> put!(ch, i), 1:3) end; julia> fetch(c) 1 julia> collect(c) # item is not removed 3-element Vector{Any}: 1 2 3 ``` """ fetch(c::Channel) = isbuffered(c) ? fetch_buffered(c) : fetch_unbuffered(c) function fetch_buffered(c::Channel) lock(c) try while isempty(c.data) check_channel_state(c) wait(c.cond_take) end return c.data[1] finally unlock(c) end end fetch_unbuffered(c::Channel) = throw(ErrorException("`fetch` is not supported on an unbuffered Channel.")) """ take!(c::Channel) Removes and returns a value from a [`Channel`](@ref) in order. Blocks until data is available. For unbuffered channels, blocks until a [`put!`](@ref) is performed by a different task. # Examples Buffered channel: ```jldoctest julia> c = Channel(1); julia> put!(c, 1); julia> take!(c) 1 ``` Unbuffered channel: ```jldoctest julia> c = Channel(0); julia> task = Task(() -> put!(c, 1)); julia> schedule(task); julia> take!(c) 1 ``` """ take!(c::Channel) = isbuffered(c) ? take_buffered(c) : take_unbuffered(c) function take_buffered(c::Channel) lock(c) try while isempty(c.data) check_channel_state(c) wait(c.cond_take) end v = popfirst!(c.data) _increment_n_avail(c, -1) notify(c.cond_put, nothing, false, false) # notify only one, since only one slot has become available for a put!. return v finally unlock(c) end end # 0-size channel function take_unbuffered(c::Channel{T}) where T lock(c) try check_channel_state(c) notify(c.cond_put, nothing, false, false) return wait(c.cond_take)::T finally unlock(c) end end """ isready(c::Channel) Determines whether a [`Channel`](@ref) has a value stored in it. Returns immediately, does not block. For unbuffered channels returns `true` if there are tasks waiting on a [`put!`](@ref). # Examples Buffered channel: ```jldoctest julia> c = Channel(1); julia> isready(c) false julia> put!(c, 1); julia> isready(c) true ``` Unbuffered channel: ```jldoctest julia> c = Channel(); julia> isready(c) # no tasks waiting to put! false julia> task = Task(() -> put!(c, 1)); julia> schedule(task); # schedule a put! task julia> isready(c) true ``` """ isready(c::Channel) = n_avail(c) > 0 isempty(c::Channel) = n_avail(c) == 0 function n_avail(c::Channel) # Lock-free equivalent to `length(c.data) + length(c.cond_put.waitq)` @atomic :monotonic c.n_avail_items end lock(c::Channel) = lock(c.cond_take) lock(f, c::Channel) = lock(f, c.cond_take) unlock(c::Channel) = unlock(c.cond_take) trylock(c::Channel) = trylock(c.cond_take) """ wait(c::Channel) Blocks until the `Channel` [`isready`](@ref). ```jldoctest julia> c = Channel(1); julia> isready(c) false julia> task = Task(() -> wait(c)); julia> schedule(task); julia> istaskdone(task) # task is blocked because channel is not ready false julia> put!(c, 1); julia> istaskdone(task) # task is now unblocked true ``` """ function wait(c::Channel) isready(c) && return lock(c) try while !isready(c) check_channel_state(c) wait(c.cond_wait) end finally unlock(c) end nothing end eltype(::Type{Channel{T}}) where {T} = T show(io::IO, c::Channel) = print(io, typeof(c), "(", c.sz_max, ")") function show(io::IO, ::MIME"text/plain", c::Channel) show(io, c) if !(get(io, :compact, false)::Bool) if !isopen(c) print(io, " (closed)") else n = n_avail(c) if n == 0 print(io, " (empty)") else s = n == 1 ? "" : "s" print(io, " (", n, " item$s available)") end end end end function iterate(c::Channel, state=nothing) if isopen(c) || isready(c) try return (take!(c), nothing) catch e if isa(e, InvalidStateException) && e.state === :closed return nothing else rethrow() end end else return nothing end end IteratorSize(::Type{<:Channel}) = SizeUnknown() L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/partr.jl6# This file is a part of Julia. License is MIT: https://julialang.org/license module Partr using ..Threads: SpinLock, maxthreadid, threadid # a task minheap mutable struct taskheap const lock::SpinLock const tasks::Vector{Task} @atomic ntasks::Int32 @atomic priority::UInt16 taskheap() = new(SpinLock(), Vector{Task}(undef, 256), zero(Int32), typemax(UInt16)) end # multiqueue minheap state const heap_d = UInt32(8) const heaps = [Vector{taskheap}(undef, 0), Vector{taskheap}(undef, 0)] const heaps_lock = [SpinLock(), SpinLock()] const cong_unbias = [typemax(UInt32), typemax(UInt32)] cong(max::UInt32, unbias::UInt32) = ccall(:jl_rand_ptls, UInt32, (UInt32, UInt32), max, unbias) + UInt32(1) function unbias_cong(max::UInt32) return typemax(UInt32) - ((typemax(UInt32) % max) + UInt32(1)) end function multiq_sift_up(heap::taskheap, idx::Int32) while idx > Int32(1) parent = (idx - Int32(2)) รท heap_d + Int32(1) if heap.tasks[idx].priority < heap.tasks[parent].priority t = heap.tasks[parent] heap.tasks[parent] = heap.tasks[idx] heap.tasks[idx] = t idx = parent else break end end end function multiq_sift_down(heap::taskheap, idx::Int32) if idx <= heap.ntasks for child = (heap_d * idx - heap_d + 2):(heap_d * idx + 1) child = Int(child) child > length(heap.tasks) && break if isassigned(heap.tasks, child) && heap.tasks[child].priority < heap.tasks[idx].priority t = heap.tasks[idx] heap.tasks[idx] = heap.tasks[child] heap.tasks[child] = t multiq_sift_down(heap, Int32(child)) end end end end function multiq_size(tpid::Int8) nt = UInt32(Threads._nthreads_in_pool(tpid)) tp = tpid + 1 tpheaps = heaps[tp] heap_c = UInt32(2) heap_p = UInt32(length(tpheaps)) if heap_c * nt <= heap_p return heap_p end @lock heaps_lock[tp] begin heap_p = UInt32(length(tpheaps)) nt = UInt32(Threads._nthreads_in_pool(tpid)) if heap_c * nt <= heap_p return heap_p end heap_p += heap_c * nt newheaps = Vector{taskheap}(undef, heap_p) copyto!(newheaps, tpheaps) for i = (1 + length(tpheaps)):heap_p newheaps[i] = taskheap() end heaps[tp] = newheaps cong_unbias[tp] = unbias_cong(heap_p) end return heap_p end function multiq_insert(task::Task, priority::UInt16) tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), task) @assert tpid > -1 heap_p = multiq_size(tpid) tp = tpid + 1 task.priority = priority rn = cong(heap_p, cong_unbias[tp]) tpheaps = heaps[tp] while !trylock(tpheaps[rn].lock) rn = cong(heap_p, cong_unbias[tp]) end heap = tpheaps[rn] if heap.ntasks >= length(heap.tasks) resize!(heap.tasks, length(heap.tasks) * 2) end ntasks = heap.ntasks + Int32(1) @atomic :monotonic heap.ntasks = ntasks heap.tasks[ntasks] = task multiq_sift_up(heap, ntasks) priority = heap.priority if task.priority < priority @atomic :monotonic heap.priority = task.priority end unlock(heap.lock) return true end function multiq_deletemin() local rn1, rn2 local prio1, prio2 tid = Threads.threadid() tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1 if tp == 0 # Foreign thread return nothing end tpheaps = heaps[tp] @label retry GC.safepoint() heap_p = UInt32(length(tpheaps)) for i = UInt32(0):heap_p if i == heap_p return nothing end rn1 = cong(heap_p, cong_unbias[tp]) rn2 = cong(heap_p, cong_unbias[tp]) prio1 = tpheaps[rn1].priority prio2 = tpheaps[rn2].priority if prio1 > prio2 prio1 = prio2 rn1 = rn2 elseif prio1 == prio2 && prio1 == typemax(UInt16) continue end if trylock(tpheaps[rn1].lock) if prio1 == tpheaps[rn1].priority break end unlock(tpheaps[rn1].lock) end end heap = tpheaps[rn1] task = heap.tasks[1] if ccall(:jl_set_task_tid, Cint, (Any, Cint), task, tid-1) == 0 unlock(heap.lock) @goto retry end ntasks = heap.ntasks @atomic :monotonic heap.ntasks = ntasks - Int32(1) heap.tasks[1] = heap.tasks[ntasks] Base._unsetindex!(heap.tasks, Int(ntasks)) prio1 = typemax(UInt16) if ntasks > 1 multiq_sift_down(heap, Int32(1)) prio1 = heap.tasks[1].priority end @atomic :monotonic heap.priority = prio1 unlock(heap.lock) return task end function multiq_check_empty() tid = Threads.threadid() tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1 if tp == 0 # Foreign thread return true end for i = UInt32(1):length(heaps[tp]) if heaps[tp][i].ntasks != 0 return false end end return true end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/task.jlษr# This file is a part of Julia. License is MIT: https://julialang.org/license ## basic task functions and TLS Core.Task(@nospecialize(f), reserved_stack::Int=0) = Core._Task(f, reserved_stack, ThreadSynchronizer()) # Container for a captured exception and its backtrace. Can be serialized. struct CapturedException <: Exception ex::Any processed_bt::Vector{Any} function CapturedException(ex, bt_raw::Vector) # bt_raw MUST be a vector that can be processed by StackTraces.stacktrace # Typically the result of a catch_backtrace() # Process bt_raw so that it can be safely serialized bt_lines = process_backtrace(bt_raw, 100) # Limiting this to 100 lines. CapturedException(ex, bt_lines) end CapturedException(ex, processed_bt::Vector{Any}) = new(ex, processed_bt) end function showerror(io::IO, ce::CapturedException) showerror(io, ce.ex, ce.processed_bt, backtrace=true) end """ capture_exception(ex, bt) -> Exception Returns an exception, possibly incorporating information from a backtrace `bt`. Defaults to returning [`CapturedException(ex, bt)`](@ref). Used in [`asyncmap`](@ref) and [`asyncmap!`](@ref) to capture exceptions thrown during the user-supplied function call. """ capture_exception(ex, bt) = CapturedException(ex, bt) """ CompositeException Wrap a `Vector` of exceptions thrown by a [`Task`](@ref) (e.g. generated from a remote worker over a channel or an asynchronously executing local I/O write or a remote worker under `pmap`) with information about the series of exceptions. For example, if a group of workers are executing several tasks, and multiple workers fail, the resulting `CompositeException` will contain a "bundle" of information from each worker indicating where and why the exception(s) occurred. """ struct CompositeException <: Exception exceptions::Vector{Any} CompositeException() = new(Any[]) CompositeException(exceptions) = new(exceptions) end length(c::CompositeException) = length(c.exceptions) push!(c::CompositeException, ex) = push!(c.exceptions, ex) pushfirst!(c::CompositeException, ex) = pushfirst!(c.exceptions, ex) isempty(c::CompositeException) = isempty(c.exceptions) iterate(c::CompositeException, state...) = iterate(c.exceptions, state...) eltype(::Type{CompositeException}) = Any function showerror(io::IO, ex::CompositeException) if !isempty(ex) showerror(io, ex.exceptions[1]) remaining = length(ex) - 1 if remaining > 0 print(io, "\n\n...and ", remaining, " more exception", remaining > 1 ? "s" : "", ".\n") end else print(io, "CompositeException()\n") end end """ TaskFailedException This exception is thrown by a [`wait(t)`](@ref) call when task `t` fails. `TaskFailedException` wraps the failed task `t`. """ struct TaskFailedException <: Exception task::Task end function showerror(io::IO, ex::TaskFailedException, bt = nothing; backtrace=true) print(io, "TaskFailedException") if bt !== nothing && backtrace show_backtrace(io, bt) end println(io) printstyled(io, "\n nested task error: ", color=error_color()) show_task_exception(io, ex.task) end function show_task_exception(io::IO, t::Task; indent = true) stack = current_exceptions(t) b = IOBuffer() if isempty(stack) # exception stack buffer not available; probably a serialized task showerror(IOContext(b, io), t.result) else show_exception_stack(IOContext(b, io), stack) end str = String(take!(b)) if indent str = replace(str, "\n" => "\n ") end print(io, str) end function show(io::IO, t::Task) print(io, "Task ($(t.state)) @0x$(string(convert(UInt, pointer_from_objref(t)), base = 16, pad = Sys.WORD_SIZE>>2))") end """ @task Wrap an expression in a [`Task`](@ref) without executing it, and return the [`Task`](@ref). This only creates a task, and does not run it. # Examples ```jldoctest julia> a1() = sum(i for i in 1:1000); julia> b = @task a1(); julia> istaskstarted(b) false julia> schedule(b); julia> yield(); julia> istaskdone(b) true ``` """ macro task(ex) thunk = Base.replace_linenums!(:(()->$(esc(ex))), __source__) :(Task($thunk)) end """ current_task() Get the currently running [`Task`](@ref). """ current_task() = ccall(:jl_get_current_task, Ref{Task}, ()) # task states const task_state_runnable = UInt8(0) const task_state_done = UInt8(1) const task_state_failed = UInt8(2) const _state_index = findfirst(==(:_state), fieldnames(Task)) @eval function load_state_acquire(t) # TODO: Replace this by proper atomic operations when available @GC.preserve t llvmcall($(""" %ptr = inttoptr i$(Sys.WORD_SIZE) %0 to i8* %rv = load atomic i8, i8* %ptr acquire, align 8 ret i8 %rv """), UInt8, Tuple{Ptr{UInt8}}, Ptr{UInt8}(pointer_from_objref(t) + fieldoffset(Task, _state_index))) end @inline function getproperty(t::Task, field::Symbol) if field === :state # TODO: this field name should be deprecated in 2.0 st = load_state_acquire(t) if st === task_state_runnable return :runnable elseif st === task_state_done return :done elseif st === task_state_failed return :failed else @assert false end elseif field === :backtrace # TODO: this field name should be deprecated in 2.0 return current_exceptions(t)[end][2] elseif field === :exception # TODO: this field name should be deprecated in 2.0 return t._isexception ? t.result : nothing else return getfield(t, field) end end """ istaskdone(t::Task) -> Bool Determine whether a task has exited. # Examples ```jldoctest julia> a2() = sum(i for i in 1:1000); julia> b = Task(a2); julia> istaskdone(b) false julia> schedule(b); julia> yield(); julia> istaskdone(b) true ``` """ istaskdone(t::Task) = load_state_acquire(t) !== task_state_runnable """ istaskstarted(t::Task) -> Bool Determine whether a task has started executing. # Examples ```jldoctest julia> a3() = sum(i for i in 1:1000); julia> b = Task(a3); julia> istaskstarted(b) false ``` """ istaskstarted(t::Task) = ccall(:jl_is_task_started, Cint, (Any,), t) != 0 """ istaskfailed(t::Task) -> Bool Determine whether a task has exited because an exception was thrown. # Examples ```jldoctest julia> a4() = error("task failed"); julia> b = Task(a4); julia> istaskfailed(b) false julia> schedule(b); julia> yield(); julia> istaskfailed(b) true ``` !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ istaskfailed(t::Task) = (load_state_acquire(t) === task_state_failed) Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) function Threads.threadpool(t::Task) tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), t) return Threads._tpid_to_sym(tpid) end task_result(t::Task) = t.result task_local_storage() = get_task_tls(current_task()) function get_task_tls(t::Task) if t.storage === nothing t.storage = IdDict() end return (t.storage)::IdDict{Any,Any} end """ task_local_storage(key) Look up the value of a key in the current task's task-local storage. """ task_local_storage(key) = task_local_storage()[key] """ task_local_storage(key, value) Assign a value to a key in the current task's task-local storage. """ task_local_storage(key, val) = (task_local_storage()[key] = val) """ task_local_storage(body, key, value) Call the function `body` with a modified task-local storage, in which `value` is assigned to `key`; the previous value of `key`, or lack thereof, is restored afterwards. Useful for emulating dynamic scoping. """ function task_local_storage(body::Function, key, val) tls = task_local_storage() hadkey = haskey(tls, key) old = get(tls, key, nothing) tls[key] = val try return body() finally hadkey ? (tls[key] = old) : delete!(tls, key) end end # just wait for a task to be done, no error propagation function _wait(t::Task) if !istaskdone(t) donenotify = t.donenotify::ThreadSynchronizer lock(donenotify) try while !istaskdone(t) wait(donenotify) end finally unlock(donenotify) end end nothing end # have `waiter` wait for `t` function _wait2(t::Task, waiter::Task) if !istaskdone(t) # since _wait2 is similar to schedule, we should observe the sticky # bit, even if we don't call `schedule` with early-return below if waiter.sticky && Threads.threadid(waiter) == 0 && !GC.in_finalizer() # Issue #41324 # t.sticky && tid == 0 is a task that needs to be co-scheduled with # the parent task. If the parent (current_task) is not sticky we must # set it to be sticky. # XXX: Ideally we would be able to unset this current_task().sticky = true tid = Threads.threadid() ccall(:jl_set_task_tid, Cint, (Any, Cint), waiter, tid-1) end donenotify = t.donenotify::ThreadSynchronizer lock(donenotify) if !istaskdone(t) push!(donenotify.waitq, waiter) unlock(donenotify) return nothing else unlock(donenotify) end end schedule(waiter) nothing end function wait(t::Task) t === current_task() && error("deadlock detected: cannot wait on current task") _wait(t) if istaskfailed(t) throw(TaskFailedException(t)) end nothing end """ fetch(x::Any) Return `x`. """ fetch(@nospecialize x) = x """ fetch(t::Task) Wait for a [`Task`](@ref) to finish, then return its result value. If the task fails with an exception, a [`TaskFailedException`](@ref) (which wraps the failed task) is thrown. """ function fetch(t::Task) wait(t) return task_result(t) end ## lexically-scoped waiting for multiple items struct ScheduledAfterSyncException <: Exception values::Vector{Any} end function showerror(io::IO, ex::ScheduledAfterSyncException) print(io, "ScheduledAfterSyncException: ") if isempty(ex.values) print(io, "(no values)") return end show(io, ex.values[1]) if length(ex.values) == 1 print(io, " is") elseif length(ex.values) == 2 print(io, " and one more ") print(io, nameof(typeof(ex.values[2]))) print(io, " are") else print(io, " and ", length(ex.values) - 1, " more objects are") end print(io, " registered after the end of a `@sync` block") end function sync_end(c::Channel{Any}) local c_ex while isready(c) r = take!(c) if isa(r, Task) _wait(r) if istaskfailed(r) if !@isdefined(c_ex) c_ex = CompositeException() end push!(c_ex, TaskFailedException(r)) end else try wait(r) catch e if !@isdefined(c_ex) c_ex = CompositeException() end push!(c_ex, e) end end end close(c) # Capture all waitable objects scheduled after the end of `@sync` and # include them in the exception. This way, the user can check what was # scheduled by examining at the exception object. if isready(c) local racy for r in c if !@isdefined(racy) racy = [] end push!(racy, r) end if @isdefined(racy) if !@isdefined(c_ex) c_ex = CompositeException() end # Since this is a clear programming error, show this exception first: pushfirst!(c_ex, ScheduledAfterSyncException(racy)) end end if @isdefined(c_ex) throw(c_ex) end nothing end const sync_varname = gensym(:sync) """ @sync Wait until all lexically-enclosed uses of [`@async`](@ref), [`@spawn`](@ref Threads.@spawn), `@spawnat` and `@distributed` are complete. All exceptions thrown by enclosed async operations are collected and thrown as a [`CompositeException`](@ref). # Examples ```julia-repl julia> Threads.nthreads() 4 julia> @sync begin Threads.@spawn println("Thread-id \$(Threads.threadid()), task 1") Threads.@spawn println("Thread-id \$(Threads.threadid()), task 2") end; Thread-id 3, task 1 Thread-id 1, task 2 ``` """ macro sync(block) var = esc(sync_varname) quote let $var = Channel(Inf) v = $(esc(block)) sync_end($var) v end end end # schedule an expression to run asynchronously """ @async Wrap an expression in a [`Task`](@ref) and add it to the local machine's scheduler queue. Values can be interpolated into `@async` via `\$`, which copies the value directly into the constructed underlying closure. This allows you to insert the _value_ of a variable, isolating the asynchronous code from changes to the variable's value in the current task. !!! warning It is strongly encouraged to favor `Threads.@spawn` over `@async` always **even when no parallelism is required** especially in publicly distributed libraries. This is because a use of `@async` disables the migration of the *parent* task across worker threads in the current implementation of Julia. Thus, seemingly innocent use of `@async` in a library function can have a large impact on the performance of very different parts of user applications. !!! compat "Julia 1.4" Interpolating values via `\$` is available as of Julia 1.4. """ macro async(expr) do_async_macro(expr, __source__) end # generate the code for @async, possibly wrapping the task in something before # pushing it to the wait queue. function do_async_macro(expr, linenums; wrap=identity) letargs = Base._lift_one_interp!(expr) thunk = Base.replace_linenums!(:(()->($(esc(expr)))), linenums) var = esc(sync_varname) quote let $(letargs...) local task = Task($thunk) if $(Expr(:islocal, var)) put!($var, $(wrap(:task))) end schedule(task) task end end end # task wrapper that doesn't create exceptions wrapped in TaskFailedException struct UnwrapTaskFailedException <: Exception task::Task end # common code for wait&fetch for UnwrapTaskFailedException function unwrap_task_failed(f::Function, t::UnwrapTaskFailedException) try f(t.task) catch ex if ex isa TaskFailedException throw(ex.task.exception) else rethrow() end end end # the unwrapping for above task wrapper (gets triggered in sync_end()) wait(t::UnwrapTaskFailedException) = unwrap_task_failed(wait, t) # same for fetching the tasks, for convenience fetch(t::UnwrapTaskFailedException) = unwrap_task_failed(fetch, t) # macro for running async code that doesn't throw wrapped exceptions macro async_unwrap(expr) do_async_macro(expr, __source__, wrap=task->:(Base.UnwrapTaskFailedException($task))) end """ errormonitor(t::Task) Print an error log to `stderr` if task `t` fails. # Examples ```julia-repl julia> Base._wait(errormonitor(Threads.@spawn error("task failed"))) Unhandled Task ERROR: task failed Stacktrace: [...] ``` """ function errormonitor(t::Task) t2 = Task() do if istaskfailed(t) local errs = stderr try # try to display the failure atomically errio = IOContext(PipeBuffer(), errs::IO) emphasize(errio, "Unhandled Task ") display_error(errio, scrub_repl_backtrace(current_exceptions(t))) write(errs, errio) catch try # try to display the secondary error atomically errio = IOContext(PipeBuffer(), errs::IO) print(errio, "\nSYSTEM: caught exception while trying to print a failed Task notice: ") display_error(errio, scrub_repl_backtrace(current_exceptions())) write(errs, errio) flush(errs) # and then the actual error, as best we can Core.print(Core.stderr, "while handling: ") Core.println(Core.stderr, current_exceptions(t)[end][1]) catch e # give up Core.print(Core.stderr, "\nSYSTEM: caught exception of type ", typeof(e).name.name, " while trying to print a failed Task notice; giving up\n") end end end nothing end t2.sticky = false _wait2(t, t2) return t end # Capture interpolated variables in $() and move them to let-block function _lift_one_interp!(e) letargs = Any[] # store the new gensymed arguments _lift_one_interp_helper(e, false, letargs) # Start out _not_ in a quote context (false) letargs end _lift_one_interp_helper(v, _, _) = v function _lift_one_interp_helper(expr::Expr, in_quote_context, letargs) if expr.head === :$ if in_quote_context # This $ is simply interpolating out of the quote # Now, we're out of the quote, so any _further_ $ is ours. in_quote_context = false else newarg = gensym() push!(letargs, :($(esc(newarg)) = $(esc(expr.args[1])))) return newarg # Don't recurse into the lifted $() exprs end elseif expr.head === :quote in_quote_context = true # Don't try to lift $ directly out of quotes elseif expr.head === :macrocall return expr # Don't recur into macro calls, since some other macros use $ end for (i,e) in enumerate(expr.args) expr.args[i] = _lift_one_interp_helper(e, in_quote_context, letargs) end expr end # add a wait-able object to the sync pool macro sync_add(expr) var = esc(sync_varname) quote local ref = $(esc(expr)) put!($var, ref) ref end end # runtime system hook called when a task finishes function task_done_hook(t::Task) # `finish_task` sets `sigatomic` before entering this function err = istaskfailed(t) result = task_result(t) handled = false donenotify = t.donenotify if isa(donenotify, ThreadSynchronizer) lock(donenotify) try if !isempty(donenotify.waitq) handled = true notify(donenotify) end finally unlock(donenotify) end end if err && !handled && Threads.threadid() == 1 if isa(result, InterruptException) && isdefined(Base, :active_repl_backend) && active_repl_backend.backend_task._state === task_state_runnable && isempty(Workqueue) && active_repl_backend.in_eval throwto(active_repl_backend.backend_task, result) # this terminates the task end end # Clear sigatomic before waiting sigatomic_end() try wait() # this will not return catch e # If an InterruptException happens while blocked in the event loop, try handing # the exception to the REPL task since the current task is done. # issue #19467 if Threads.threadid() == 1 && isa(e, InterruptException) && isdefined(Base, :active_repl_backend) && active_repl_backend.backend_task._state === task_state_runnable && isempty(Workqueue) && active_repl_backend.in_eval throwto(active_repl_backend.backend_task, e) else rethrow() end end end ## scheduler and work queue mutable struct IntrusiveLinkedListSynchronized{T} queue::IntrusiveLinkedList{T} lock::Threads.SpinLock IntrusiveLinkedListSynchronized{T}() where {T} = new(IntrusiveLinkedList{T}(), Threads.SpinLock()) end isempty(W::IntrusiveLinkedListSynchronized) = isempty(W.queue) length(W::IntrusiveLinkedListSynchronized) = length(W.queue) function push!(W::IntrusiveLinkedListSynchronized{T}, t::T) where T lock(W.lock) try push!(W.queue, t) finally unlock(W.lock) end return W end function pushfirst!(W::IntrusiveLinkedListSynchronized{T}, t::T) where T lock(W.lock) try pushfirst!(W.queue, t) finally unlock(W.lock) end return W end function pop!(W::IntrusiveLinkedListSynchronized) lock(W.lock) try return pop!(W.queue) finally unlock(W.lock) end end function popfirst!(W::IntrusiveLinkedListSynchronized) lock(W.lock) try return popfirst!(W.queue) finally unlock(W.lock) end end function list_deletefirst!(W::IntrusiveLinkedListSynchronized{T}, t::T) where T lock(W.lock) try list_deletefirst!(W.queue, t) finally unlock(W.lock) end return W end const StickyWorkqueue = IntrusiveLinkedListSynchronized{Task} global Workqueues::Vector{StickyWorkqueue} = [StickyWorkqueue()] const Workqueues_lock = Threads.SpinLock() const Workqueue = Workqueues[1] # default work queue is thread 1 // TODO: deprecate this variable function workqueue_for(tid::Int) qs = Workqueues if length(qs) >= tid && isassigned(qs, tid) return @inbounds qs[tid] end # slow path to allocate it l = Workqueues_lock @lock l begin qs = Workqueues if length(qs) < tid nt = Threads.maxthreadid() @assert tid <= nt global Workqueues = qs = copyto!(typeof(qs)(undef, length(qs) + nt - 1), qs) end if !isassigned(qs, tid) @inbounds qs[tid] = StickyWorkqueue() end return @inbounds qs[tid] end end function enq_work(t::Task) (t._state === task_state_runnable && t.queue === nothing) || error("schedule: Task not runnable") # Sticky tasks go into their thread's work queue. if t.sticky tid = Threads.threadid(t) if tid == 0 # The task is not yet stuck to a thread. Stick it to the current # thread and do the same to the parent task (the current task) so # that the tasks are correctly co-scheduled (issue #41324). # XXX: Ideally we would be able to unset this. if GC.in_finalizer() # The task was launched in a finalizer. There is no thread to sticky it # to, so just allow it to run anywhere as if it had been non-sticky. t.sticky = false @goto not_sticky else tid = Threads.threadid() ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) current_task().sticky = true end end push!(workqueue_for(tid), t) else @label not_sticky tp = Threads.threadpool(t) if tp === :foreign || Threads.threadpoolsize(tp) == 1 # There's only one thread in the task's assigned thread pool; # use its work queue. tid = (tp === :interactive) ? 1 : Threads.threadpoolsize(:interactive)+1 ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) push!(workqueue_for(tid), t) else # Otherwise, put the task in the multiqueue. Partr.multiq_insert(t, t.priority) tid = 0 end end ccall(:jl_wakeup_thread, Cvoid, (Int16,), (tid - 1) % Int16) return t end schedule(t::Task) = enq_work(t) """ schedule(t::Task, [val]; error=false) Add a [`Task`](@ref) to the scheduler's queue. This causes the task to run constantly when the system is otherwise idle, unless the task performs a blocking operation such as [`wait`](@ref). If a second argument `val` is provided, it will be passed to the task (via the return value of [`yieldto`](@ref)) when it runs again. If `error` is `true`, the value is raised as an exception in the woken task. !!! warning It is incorrect to use `schedule` on an arbitrary `Task` that has already been started. See [the API reference](@ref low-level-schedule-wait) for more information. # Examples ```jldoctest julia> a5() = sum(i for i in 1:1000); julia> b = Task(a5); julia> istaskstarted(b) false julia> schedule(b); julia> yield(); julia> istaskstarted(b) true julia> istaskdone(b) true ``` """ function schedule(t::Task, @nospecialize(arg); error=false) # schedule a task to be (re)started with the given value or exception t._state === task_state_runnable || Base.error("schedule: Task not runnable") if error t.queue === nothing || Base.list_deletefirst!(t.queue::IntrusiveLinkedList{Task}, t) setfield!(t, :result, arg) setfield!(t, :_isexception, true) else t.queue === nothing || Base.error("schedule: Task not runnable") setfield!(t, :result, arg) end enq_work(t) return t end """ yield() Switch to the scheduler to allow another scheduled task to run. A task that calls this function is still runnable, and will be restarted immediately if there are no other runnable tasks. """ function yield() ct = current_task() enq_work(ct) try wait() catch ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) rethrow() end end @inline set_next_task(t::Task) = ccall(:jl_set_next_task, Cvoid, (Any,), t) """ yield(t::Task, arg = nothing) A fast, unfair-scheduling version of `schedule(t, arg); yield()` which immediately yields to `t` before calling the scheduler. """ function yield(t::Task, @nospecialize(x=nothing)) (t._state === task_state_runnable && t.queue === nothing) || error("yield: Task not runnable") t.result = x enq_work(current_task()) set_next_task(t) return try_yieldto(ensure_rescheduled) end """ yieldto(t::Task, arg = nothing) Switch to the given task. The first time a task is switched to, the task's function is called with no arguments. On subsequent switches, `arg` is returned from the task's last call to `yieldto`. This is a low-level call that only switches tasks, not considering states or scheduling in any way. Its use is discouraged. """ function yieldto(t::Task, @nospecialize(x=nothing)) # TODO: these are legacy behaviors; these should perhaps be a scheduler # state error instead. if t._state === task_state_done return x elseif t._state === task_state_failed throw(t.result) end t.result = x set_next_task(t) return try_yieldto(identity) end function try_yieldto(undo) try ccall(:jl_switch, Cvoid, ()) catch undo(ccall(:jl_get_next_task, Ref{Task}, ())) rethrow() end ct = current_task() if ct._isexception exc = ct.result ct.result = nothing ct._isexception = false throw(exc) end result = ct.result ct.result = nothing return result end # yield to a task, throwing an exception in it function throwto(t::Task, @nospecialize exc) t.result = exc t._isexception = true set_next_task(t) return try_yieldto(identity) end function ensure_rescheduled(othertask::Task) ct = current_task() W = workqueue_for(Threads.threadid()) if ct !== othertask && othertask._state === task_state_runnable # we failed to yield to othertask # return it to the head of a queue to be retried later tid = Threads.threadid(othertask) Wother = tid == 0 ? W : workqueue_for(tid) pushfirst!(Wother, othertask) end # if the current task was queued, # also need to return it to the runnable state # before throwing an error list_deletefirst!(W, ct) nothing end function trypoptask(W::StickyWorkqueue) while !isempty(W) t = popfirst!(W) if t._state !== task_state_runnable # assume this somehow got queued twice, # probably broken now, but try discarding this switch and keep going # can't throw here, because it's probably not the fault of the caller to wait # and don't want to use print() here, because that may try to incur a task switch ccall(:jl_safe_printf, Cvoid, (Ptr{UInt8}, Int32...), "\nWARNING: Workqueue inconsistency detected: popfirst!(Workqueue).state !== :runnable\n") continue end return t end return Partr.multiq_deletemin() end checktaskempty = Partr.multiq_check_empty @noinline function poptask(W::StickyWorkqueue) task = trypoptask(W) if !(task isa Task) task = ccall(:jl_task_get_next, Ref{Task}, (Any, Any, Any), trypoptask, W, checktaskempty) end set_next_task(task) nothing end function wait() GC.safepoint() W = workqueue_for(Threads.threadid()) poptask(W) result = try_yieldto(ensure_rescheduled) process_events() # return when we come out of the queue return result end if Sys.iswindows() pause() = ccall(:Sleep, stdcall, Cvoid, (UInt32,), 0xffffffff) else pause() = ccall(:pause, Cvoid, ()) end X/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/threads_overloads.jll # This file is a part of Julia. License is MIT: https://julialang.org/license """ Threads.foreach(f, channel::Channel; schedule::Threads.AbstractSchedule=Threads.FairSchedule(), ntasks=Threads.threadpoolsize()) Similar to `foreach(f, channel)`, but iteration over `channel` and calls to `f` are split across `ntasks` tasks spawned by `Threads.@spawn`. This function will wait for all internally spawned tasks to complete before returning. If `schedule isa FairSchedule`, `Threads.foreach` will attempt to spawn tasks in a manner that enables Julia's scheduler to more freely load-balance work items across threads. This approach generally has higher per-item overhead, but may perform better than `StaticSchedule` in concurrence with other multithreaded workloads. If `schedule isa StaticSchedule`, `Threads.foreach` will spawn tasks in a manner that incurs lower per-item overhead than `FairSchedule`, but is less amenable to load-balancing. This approach thus may be more suitable for fine-grained, uniform workloads, but may perform worse than `FairSchedule` in concurrence with other multithreaded workloads. # Examples ```julia-repl julia> n = 20 julia> c = Channel{Int}(ch -> foreach(i -> put!(ch, i), 1:n), 1) julia> d = Channel{Int}(n) do ch f = i -> put!(ch, i^2) Threads.foreach(f, c) end julia> collect(d) collect(d) = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400] ``` !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ function Threads.foreach(f, channel::Channel; schedule::Threads.AbstractSchedule=Threads.FairSchedule(), ntasks=Threads.threadpoolsize()) apply = _apply_for_schedule(schedule) stop = Threads.Atomic{Bool}(false) @sync for _ in 1:ntasks Threads.@spawn try for item in channel $apply(f, item) # do `stop[] && break` after `f(item)` to avoid losing `item`. # this isn't super comprehensive since a task could still get # stuck on `take!` at `for item in channel`. We should think # about a more robust mechanism to avoid dropping items. See also # https://github.com/JuliaLang/julia/pull/34543#discussion_r422695217 stop[] && break end catch stop[] = true rethrow() end end return nothing end _apply_for_schedule(::Threads.StaticSchedule) = (f, x) -> f(x) _apply_for_schedule(::Threads.FairSchedule) = (f, x) -> wait(Threads.@spawn f(x)) R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/weakkeydict.jlG# This file is a part of Julia. License is MIT: https://julialang.org/license # weak key dictionaries """ WeakKeyDict([itr]) `WeakKeyDict()` constructs a hash table where the keys are weak references to objects which may be garbage collected even when referenced in a hash table. See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref), `WeakKeyDict` does not convert keys on insertion, as this would imply the key object was unreferenced anywhere before insertion. See also [`WeakRef`](@ref). """ mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V} ht::Dict{WeakRef,V} lock::ReentrantLock finalizer::Function dirty::Bool # Constructors mirror Dict's function WeakKeyDict{K,V}() where V where K t = new(Dict{WeakRef,V}(), ReentrantLock(), identity, 0) t.finalizer = k -> t.dirty = true return t end end function WeakKeyDict{K,V}(kv) where V where K h = WeakKeyDict{K,V}() for (k,v) in kv h[k] = v end return h end WeakKeyDict{K,V}(p::Pair) where V where K = setindex!(WeakKeyDict{K,V}(), p.second, p.first) function WeakKeyDict{K,V}(ps::Pair...) where V where K h = WeakKeyDict{K,V}() sizehint!(h, length(ps)) for p in ps h[p.first] = p.second end return h end WeakKeyDict() = WeakKeyDict{Any,Any}() WeakKeyDict(kv::Tuple{}) = WeakKeyDict() copy(d::WeakKeyDict) = WeakKeyDict(d) WeakKeyDict(ps::Pair{K,V}...) where {K,V} = WeakKeyDict{K,V}(ps) WeakKeyDict(ps::Pair{K}...) where {K} = WeakKeyDict{K,Any}(ps) WeakKeyDict(ps::(Pair{K,V} where K)...) where {V} = WeakKeyDict{Any,V}(ps) WeakKeyDict(ps::Pair...) = WeakKeyDict{Any,Any}(ps) function WeakKeyDict(kv) try Base.dict_with_eltype((K, V) -> WeakKeyDict{K, V}, kv, eltype(kv)) catch if !isiterable(typeof(kv)) || !all(x->isa(x,Union{Tuple,Pair}),kv) throw(ArgumentError("WeakKeyDict(kv): kv needs to be an iterator of tuples or pairs")) else rethrow() end end end function _cleanup_locked(h::WeakKeyDict) if h.dirty h.dirty = false idx = skip_deleted_floor!(h.ht) while idx != 0 if h.ht.keys[idx].value === nothing _delete!(h.ht, idx) end idx = skip_deleted(h.ht, idx + 1) end end return h end sizehint!(d::WeakKeyDict, newsz) = sizehint!(d.ht, newsz) empty(d::WeakKeyDict, ::Type{K}, ::Type{V}) where {K, V} = WeakKeyDict{K, V}() IteratorSize(::Type{<:WeakKeyDict}) = SizeUnknown() islocked(wkh::WeakKeyDict) = islocked(wkh.lock) lock(wkh::WeakKeyDict) = lock(wkh.lock) unlock(wkh::WeakKeyDict) = unlock(wkh.lock) lock(f, wkh::WeakKeyDict) = lock(f, wkh.lock) trylock(f, wkh::WeakKeyDict) = trylock(f, wkh.lock) function setindex!(wkh::WeakKeyDict{K}, v, key) where K !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) # 'nothing' is not valid both because 'finalizer' will reject it, # and because we therefore use it as a sentinel value key === nothing && throw(ArgumentError("`nothing` is not a valid WeakKeyDict key")) lock(wkh) do _cleanup_locked(wkh) k = getkey(wkh.ht, key, nothing) if k === nothing finalizer(wkh.finalizer, key) k = WeakRef(key) else k.value = key end wkh.ht[k] = v end return wkh end function get!(wkh::WeakKeyDict{K}, key, default) where {K} v = lock(wkh) do if key !== nothing && haskey(wkh.ht, key) wkh.ht[key] else wkh[key] = default end end return v end function get!(default::Callable, wkh::WeakKeyDict{K}, key) where {K} v = lock(wkh) do if key !== nothing && haskey(wkh.ht, key) wkh.ht[key] else wkh[key] = default() end end return v end function getkey(wkh::WeakKeyDict{K}, kk, default) where K k = lock(wkh) do local k = getkey(wkh.ht, kk, nothing) k === nothing && return nothing return k.value end return k === nothing ? default : k::K end map!(f, iter::ValueIterator{<:WeakKeyDict})= map!(f, values(iter.dict.ht)) function get(wkh::WeakKeyDict{K}, key, default) where {K} key === nothing && throw(KeyError(nothing)) lock(wkh) do return get(wkh.ht, key, default) end end function get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} key === nothing && throw(KeyError(nothing)) lock(wkh) do return get(default, wkh.ht, key) end end function pop!(wkh::WeakKeyDict{K}, key) where {K} key === nothing && throw(KeyError(nothing)) lock(wkh) do return pop!(wkh.ht, key) end end function pop!(wkh::WeakKeyDict{K}, key, default) where {K} key === nothing && return default lock(wkh) do return pop!(wkh.ht, key, default) end end function delete!(wkh::WeakKeyDict, key) key === nothing && return wkh lock(wkh) do delete!(wkh.ht, key) end return wkh end function empty!(wkh::WeakKeyDict) lock(wkh) do empty!(wkh.ht) end return wkh end function haskey(wkh::WeakKeyDict{K}, key) where {K} key === nothing && return false lock(wkh) do return haskey(wkh.ht, key) end end function getindex(wkh::WeakKeyDict{K}, key) where {K} key === nothing && throw(KeyError(nothing)) lock(wkh) do return getindex(wkh.ht, key) end end isempty(wkh::WeakKeyDict) = length(wkh) == 0 function length(t::WeakKeyDict) lock(t) do _cleanup_locked(t) return length(t.ht) end end function iterate(t::WeakKeyDict{K,V}, state...) where {K, V} return lock(t) do while true y = iterate(t.ht, state...) y === nothing && return nothing wkv, state = y k = wkv[1].value GC.safepoint() # ensure `k` is now gc-rooted k === nothing && continue # indicates `k` is scheduled for deletion kv = Pair{K,V}(k::K, wkv[2]) return (kv, state) end end end filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d) J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/env.jl?$# This file is a part of Julia. License is MIT: https://julialang.org/license if Sys.iswindows() const ERROR_ENVVAR_NOT_FOUND = UInt32(203) const env_dict = Dict{String, Vector{Cwchar_t}}() const env_lock = ReentrantLock() function memoized_env_lookup(str::AbstractString) # Windows environment variables have a different format from Linux / MacOS, and previously # incurred allocations because we had to convert a String to a Vector{Cwchar_t} each time # an environment variable was looked up. This function memoizes that lookup process, storing # the String => Vector{Cwchar_t} pairs in env_dict @lock env_lock begin var = get(env_dict, str, nothing) if isnothing(var) var = cwstring(str) env_dict[str] = var end return var end end _getenvlen(var::Vector{UInt16}) = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,C_NULL,0) _hasenv(s::Vector{UInt16}) = _getenvlen(s) != 0 || Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND _hasenv(s::AbstractString) = _hasenv(memoized_env_lookup(s)) function access_env(onError::Function, str::AbstractString) var = memoized_env_lookup(str) len = _getenvlen(var) if len == 0 return Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND ? "" : onError(str) end val = zeros(UInt16,len) ret = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,val,len) windowserror(:getenv, (ret == 0 && len != 1) || ret != len-1 || val[end] != 0) pop!(val) # NUL return transcode(String, val) end function _setenv(svar::AbstractString, sval::AbstractString, overwrite::Bool=true) var = memoized_env_lookup(svar) val = cwstring(sval) if overwrite || !_hasenv(var) ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,val) windowserror(:setenv, ret == 0) end end function _unsetenv(svar::AbstractString) var = memoized_env_lookup(svar) ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,C_NULL) windowserror(:setenv, ret == 0 && Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND) end else # !windows _getenv(var::AbstractString) = ccall(:getenv, Cstring, (Cstring,), var) _hasenv(s::AbstractString) = _getenv(s) != C_NULL function access_env(onError::Function, var::AbstractString) val = _getenv(var) val == C_NULL ? onError(var) : unsafe_string(val) end function _setenv(var::AbstractString, val::AbstractString, overwrite::Bool=true) ret = ccall(:setenv, Int32, (Cstring,Cstring,Int32), var, val, overwrite) systemerror(:setenv, ret != 0) end function _unsetenv(var::AbstractString) ret = ccall(:unsetenv, Int32, (Cstring,), var) systemerror(:unsetenv, ret != 0) end end # os test ## ENV: hash interface ## """ EnvDict() -> EnvDict A singleton of this type provides a hash table interface to environment variables. """ struct EnvDict <: AbstractDict{String,String}; end """ ENV Reference to the singleton `EnvDict`, providing a dictionary interface to system environment variables. (On Windows, system environment variables are case-insensitive, and `ENV` correspondingly converts all keys to uppercase for display, iteration, and copying. Portable code should not rely on the ability to distinguish variables by case, and should beware that setting an ostensibly lowercase variable may result in an uppercase `ENV` key.) !!! warning Mutating the environment is not thread-safe. # Examples ```julia-repl julia> ENV Base.EnvDict with "50" entries: "SECURITYSESSIONID" => "123" "USER" => "username" "MallocNanoZone" => "0" โ‹ฎ => โ‹ฎ julia> ENV["JULIA_EDITOR"] = "vim" "vim" julia> ENV["JULIA_EDITOR"] "vim" ``` See also: [`withenv`](@ref), [`addenv`](@ref). """ const ENV = EnvDict() const get_bool_env_truthy = ( "t", "T", "true", "True", "TRUE", "y", "Y", "yes", "Yes", "YES", "1") const get_bool_env_falsy = ( "f", "F", "false", "False", "FALSE", "n", "N", "no", "No", "NO", "0") """ Base.get_bool_env(name::String, default::Bool)::Union{Bool,Nothing} Evaluate whether the value of environnment variable `name` is a truthy or falsy string, and return `nothing` if it is not recognized as either. If the variable is not set, or is set to "", return `default`. Recognized values are the following, and their Capitalized and UPPERCASE forms: truthy: "t", "true", "y", "yes", "1" falsy: "f", "false", "n", "no", "0" """ function get_bool_env(name::String, default::Bool) haskey(ENV, name) || return default val = ENV[name] if isempty(val) return default elseif val in get_bool_env_truthy return true elseif val in get_bool_env_falsy return false else return nothing end end getindex(::EnvDict, k::AbstractString) = access_env(k->throw(KeyError(k)), k) get(::EnvDict, k::AbstractString, def) = access_env(Returns(def), k) get(f::Callable, ::EnvDict, k::AbstractString) = access_env(k->f(), k) function get!(default::Callable, ::EnvDict, k::AbstractString) haskey(ENV, k) && return ENV[k] ENV[k] = default() end in(k::AbstractString, ::KeySet{String, EnvDict}) = _hasenv(k) pop!(::EnvDict, k::AbstractString) = (v = ENV[k]; _unsetenv(k); v) pop!(::EnvDict, k::AbstractString, def) = haskey(ENV,k) ? pop!(ENV,k) : def delete!(::EnvDict, k::AbstractString) = (_unsetenv(k); ENV) setindex!(::EnvDict, v, k::AbstractString) = _setenv(k,string(v)) push!(::EnvDict, kv::Pair{<:AbstractString}) = setindex!(ENV, kv.second, kv.first) if Sys.iswindows() GESW() = (pos = ccall(:GetEnvironmentStringsW, stdcall, Ptr{UInt16}, ()); (pos, pos)) function winuppercase(s::AbstractString) isempty(s) && return s LOCALE_INVARIANT = 0x0000007f LCMAP_UPPERCASE = 0x00000200 ws = transcode(UInt16, String(s)) result = ccall(:LCMapStringW, stdcall, Cint, (UInt32, UInt32, Ptr{UInt16}, Cint, Ptr{UInt16}, Cint), LOCALE_INVARIANT, LCMAP_UPPERCASE, ws, length(ws), ws, length(ws)) systemerror(:LCMapStringW, iszero(result)) return transcode(String, ws) end function iterate(hash::EnvDict, block::Tuple{Ptr{UInt16},Ptr{UInt16}} = GESW()) while true if unsafe_load(block[1]) == 0 ccall(:FreeEnvironmentStringsW, stdcall, Int32, (Ptr{UInt16},), block[2]) return nothing end pos = block[1] blk = block[2] len = ccall(:wcslen, UInt, (Ptr{UInt16},), pos) buf = Vector{UInt16}(undef, len) GC.@preserve buf unsafe_copyto!(pointer(buf), pos, len) env = transcode(String, buf) pos += (len + 1) * 2 if !isempty(env) m = findnext('=', env, nextind(env, firstindex(env))) else m = nothing end if m === nothing @warn "malformed environment entry" env continue end return (Pair{String,String}(winuppercase(env[1:prevind(env, m)]), env[nextind(env, m):end]), (pos, blk)) end end else # !windows function iterate(::EnvDict, i=0) while true env = ccall(:jl_environ, Any, (Int32,), i) env === nothing && return nothing env = env::String m = findfirst('=', env) if m === nothing @warn "malformed environment entry" env continue end return (Pair{String,String}(env[1:prevind(env, m)], env[nextind(env, m):end]), i+1) end end end # os-test #TODO: Make these more efficient function length(::EnvDict) i = 0 for (k,v) in ENV i += 1 end return i end function show(io::IO, ::EnvDict) for (k,v) = ENV println(io, "$k=$v") end end """ withenv(f, kv::Pair...) Execute `f` in an environment that is temporarily modified (not replaced as in `setenv`) by zero or more `"var"=>val` arguments `kv`. `withenv` is generally used via the `withenv(kv...) do ... end` syntax. A value of `nothing` can be used to temporarily unset an environment variable (if it is set). When `withenv` returns, the original environment has been restored. !!! warning Changing the environment is not thread-safe. For running external commands with a different environment from the parent process, prefer using [`addenv`](@ref) over `withenv`. """ function withenv(f, keyvals::Pair{T}...) where T<:AbstractString old = Dict{T,Any}() for (key,val) in keyvals old[key] = get(ENV,key,nothing) val !== nothing ? (ENV[key]=val) : delete!(ENV, key) end try f() finally for (key,val) in old val !== nothing ? (ENV[key]=val) : delete!(ENV, key) end end end withenv(f) = f() # handle empty keyvals case; see #10853 L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/libuv.jl# This file is a part of Julia. License is MIT: https://julialang.org/license # Core definitions for interacting with the libuv library from Julia include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "uv_constants.jl")) # include($BUILDROOT/base/uv_constants.jl) # convert UV handle data to julia object, checking for null function uv_sizeof_handle(handle) if !(UV_UNKNOWN_HANDLE < handle < UV_HANDLE_TYPE_MAX) throw(DomainError(handle)) end return ccall(:uv_handle_size, Csize_t, (Int32,), handle) end function uv_sizeof_req(req) if !(UV_UNKNOWN_REQ < req < UV_REQ_TYPE_MAX) throw(DomainError(req)) end return ccall(:uv_req_size, Csize_t, (Int32,), req) end for h in uv_handle_types @eval const $(Symbol("_sizeof_", lowercase(string(h)))) = uv_sizeof_handle($h) end for r in uv_req_types @eval const $(Symbol("_sizeof_", lowercase(string(r)))) = uv_sizeof_req($r) end uv_handle_data(handle) = ccall(:jl_uv_handle_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) uv_req_data(handle) = ccall(:jl_uv_req_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) uv_req_set_data(req, data) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Any), req, data) uv_req_set_data(req, data::Ptr{Cvoid}) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), req, data) macro handle_as(hand, typ) return quote local data = uv_handle_data($(esc(hand))) data == C_NULL && return unsafe_pointer_to_objref(data)::($(esc(typ))) end end associate_julia_struct(handle::Ptr{Cvoid}, @nospecialize(jlobj)) = ccall(:jl_uv_associate_julia_struct, Cvoid, (Ptr{Cvoid}, Any), handle, jlobj) disassociate_julia_struct(uv) = disassociate_julia_struct(uv.handle) disassociate_julia_struct(handle::Ptr{Cvoid}) = handle != C_NULL && ccall(:jl_uv_disassociate_julia_struct, Cvoid, (Ptr{Cvoid},), handle) iolock_begin() = ccall(:jl_iolock_begin, Cvoid, ()) iolock_end() = ccall(:jl_iolock_end, Cvoid, ()) # A dict of all libuv handles that are being waited on somewhere in the system # and should thus not be garbage collected const uvhandles = IdDict() const preserve_handle_lock = Threads.SpinLock() function preserve_handle(x) lock(preserve_handle_lock) v = get(uvhandles, x, 0)::Int uvhandles[x] = v + 1 unlock(preserve_handle_lock) nothing end function unpreserve_handle(x) lock(preserve_handle_lock) v = get(uvhandles, x, 0)::Int if v == 0 unlock(preserve_handle_lock) error("unbalanced call to unpreserve_handle for $(typeof(x))") elseif v == 1 pop!(uvhandles, x) else uvhandles[x] = v - 1 end unlock(preserve_handle_lock) nothing end ## Libuv error handling ## struct IOError <: Exception msg::String code::Int32 IOError(msg::AbstractString, code::Integer) = new(msg, code) end showerror(io::IO, e::IOError) = print(io, "IOError: ", e.msg) function _UVError(pfx::AbstractString, code::Integer) code = Int32(code) IOError(string(pfx, ": ", struverror(code), " (", uverrorname(code), ")"), code) end function _UVError(pfx::AbstractString, code::Integer, sfxs::AbstractString...) code = Int32(code) IOError(string(pfx, ": ", struverror(code), " (", uverrorname(code), ")", " ", sfxs...), code) end struverror(err::Int32) = unsafe_string(ccall(:uv_strerror, Cstring, (Int32,), err)) uverrorname(err::Int32) = unsafe_string(ccall(:uv_err_name, Cstring, (Int32,), err)) uv_error(prefix::Symbol, c::Integer) = uv_error(string(prefix), c) uv_error(prefix::AbstractString, c::Integer) = c < 0 ? throw(_UVError(prefix, c)) : nothing ## event loop ## eventloop() = ccall(:jl_global_event_loop, Ptr{Cvoid}, ()) function uv_unref(h::Ptr{Cvoid}) iolock_begin() ccall(:uv_unref, Cvoid, (Ptr{Cvoid},), h) iolock_end() end function uv_ref(h::Ptr{Cvoid}) iolock_begin() ccall(:uv_ref, Cvoid, (Ptr{Cvoid},), h) iolock_end() end function process_events() return ccall(:jl_process_events, Int32, ()) end function uv_alloc_buf end function uv_readcb end function uv_writecb_task end function uv_shutdowncb_task end function uv_return_spawn end function uv_asynccb end function uv_timercb end function reinit_stdio() global stdin = init_stdio(ccall(:jl_stdin_stream, Ptr{Cvoid}, ())) global stdout = init_stdio(ccall(:jl_stdout_stream, Ptr{Cvoid}, ())) global stderr = init_stdio(ccall(:jl_stderr_stream, Ptr{Cvoid}, ())) opts = JLOptions() if opts.color != 0 have_color = (opts.color == 1) if !isa(stdout, TTY) global stdout = IOContext(stdout, :color => have_color) end if !isa(stderr, TTY) global stderr = IOContext(stderr, :color => have_color) end end nothing end """ stdin::IO Global variable referring to the standard input stream. """ :stdin """ stdout::IO Global variable referring to the standard out stream. """ :stdout """ stderr::IO Global variable referring to the standard error stream. """ :stderr U/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./uv_constants.jlู const uv_handle_types = [:UV_ASYNC, :UV_CHECK, :UV_FS_EVENT, :UV_FS_POLL, :UV_HANDLE, :UV_IDLE, :UV_NAMED_PIPE, :UV_POLL, :UV_PREPARE, :UV_PROCESS, :UV_STREAM, :UV_TCP, :UV_TIMER, :UV_TTY, :UV_UDP, :UV_SIGNAL, :UV_FILE] const uv_req_types = [:UV_REQ, :UV_CONNECT, :UV_WRITE, :UV_SHUTDOWN, :UV_UDP_SEND, :UV_FS, :UV_WORK, :UV_GETADDRINFO, :UV_GETNAMEINFO, :UV_RANDOM,] const uv_err_vals = [(:UV_E2BIG,(-(7))), (:UV_EACCES,(-(13))), (:UV_EADDRINUSE,(-(98))), (:UV_EADDRNOTAVAIL,(-(99))), (:UV_EAFNOSUPPORT,(-(97))), (:UV_EAGAIN,(-(11))), (:UV_EAI_ADDRFAMILY,(-3000)), (:UV_EAI_AGAIN,(-3001)), (:UV_EAI_BADFLAGS,(-3002)), (:UV_EAI_BADHINTS,(-3013)), (:UV_EAI_CANCELED,(-3003)), (:UV_EAI_FAIL,(-3004)), (:UV_EAI_FAMILY,(-3005)), (:UV_EAI_MEMORY,(-3006)), (:UV_EAI_NODATA,(-3007)), (:UV_EAI_NONAME,(-3008)), (:UV_EAI_OVERFLOW,(-3009)), (:UV_EAI_PROTOCOL,(-3014)), (:UV_EAI_SERVICE,(-3010)), (:UV_EAI_SOCKTYPE,(-3011)), (:UV_EALREADY,(-(114))), (:UV_EBADF,(-(9))), (:UV_EBUSY,(-(16))), (:UV_ECANCELED,(-(125))), (:UV_ECHARSET,(-4080)), (:UV_ECONNABORTED,(-(103))), (:UV_ECONNREFUSED,(-(111))), (:UV_ECONNRESET,(-(104))), (:UV_EDESTADDRREQ,(-(89))), (:UV_EEXIST,(-(17))), (:UV_EFAULT,(-(14))), (:UV_EFBIG,(-(27))), (:UV_EHOSTUNREACH,(-(113))), (:UV_EINTR,(-(4))), (:UV_EINVAL,(-(22))), (:UV_EIO,(-(5))), (:UV_EISCONN,(-(106))), (:UV_EISDIR,(-(21))), (:UV_ELOOP,(-(40))), (:UV_EMFILE,(-(24))), (:UV_EMSGSIZE,(-(90))), (:UV_ENAMETOOLONG,(-(36))), (:UV_ENETDOWN,(-(100))), (:UV_ENETUNREACH,(-(101))), (:UV_ENFILE,(-(23))), (:UV_ENOBUFS,(-(105))), (:UV_ENODEV,(-(19))), (:UV_ENOENT,(-(2))), (:UV_ENOMEM,(-(12))), (:UV_ENONET,(-(64))), (:UV_ENOPROTOOPT,(-(92))), (:UV_ENOSPC,(-(28))), (:UV_ENOSYS,(-(38))), (:UV_ENOTCONN,(-(107))), (:UV_ENOTDIR,(-(20))), (:UV_ENOTEMPTY,(-(39))), (:UV_ENOTSOCK,(-(88))), (:UV_ENOTSUP,(-(95))), (:UV_EOVERFLOW,(-(75))), (:UV_EPERM,(-(1))), (:UV_EPIPE,(-(32))), (:UV_EPROTO,(-(71))), (:UV_EPROTONOSUPPORT,(-(93))), (:UV_EPROTOTYPE,(-(91))), (:UV_ERANGE,(-(34))), (:UV_EROFS,(-(30))), (:UV_ESHUTDOWN,(-(108))), (:UV_ESPIPE,(-(29))), (:UV_ESRCH,(-(3))), (:UV_ETIMEDOUT,(-(110))), (:UV_ETXTBSY,(-(26))), (:UV_EXDEV,(-(18))), (:UV_UNKNOWN,(-4094)), (:UV_EOF,(-4095)), (:UV_ENXIO,(-(6))), (:UV_EMLINK,(-(31))), (:UV_EHOSTDOWN,(-(112))), (:UV_EREMOTEIO,(-(121))), (:UV_ENOTTY,(-(25))), (:UV_EFTYPE,(-4028)), (:UV_EILSEQ,(-(84))), (:UV_ESOCKTNOSUPPORT,(-(94))),] let handles = [:UV_UNKNOWN_HANDLE, uv_handle_types..., :UV_HANDLE_TYPE_MAX] reqs = [:UV_UNKNOWN_REQ, uv_req_types..., :UV_REQ_TYPE_PRIVATE, :UV_REQ_TYPE_MAX] for i in 1:length(handles) @eval const $(handles[i]) = $(i - 1) end for i in 1:length(reqs) @eval const $(reqs[i]) = $(i - 1) end for (v, val) in uv_err_vals @eval const $v = $val end end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/asyncevent.jlั)# This file is a part of Julia. License is MIT: https://julialang.org/license ## async event notifications """ AsyncCondition() Create a async condition that wakes up tasks waiting for it (by calling [`wait`](@ref) on the object) when notified from C by a call to `uv_async_send`. Waiting tasks are woken with an error when the object is closed (by [`close`](@ref)). Use [`isopen`](@ref) to check whether it is still active. This provides an implicit acquire & release memory ordering between the sending and waiting threads. """ mutable struct AsyncCondition @atomic handle::Ptr{Cvoid} cond::ThreadSynchronizer @atomic isopen::Bool @atomic set::Bool function AsyncCondition() this = new(Libc.malloc(_sizeof_uv_async), ThreadSynchronizer(), true, false) iolock_begin() associate_julia_struct(this.handle, this) err = ccall(:uv_async_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), this, @cfunction(uv_asynccb, Cvoid, (Ptr{Cvoid},))) if err != 0 #TODO: this codepath is currently not tested Libc.free(this.handle) this.handle = C_NULL throw(_UVError("uv_async_init", err)) end finalizer(uvfinalize, this) iolock_end() return this end end """ AsyncCondition(callback::Function) Create a async condition that calls the given `callback` function. The `callback` is passed one argument, the async condition object itself. """ function AsyncCondition(cb::Function) async = AsyncCondition() t = @task begin unpreserve_handle(async) while _trywait(async) cb(async) isopen(async) || return end end # here we are mimicking parts of _trywait, in coordination with task `t` preserve_handle(async) @lock async.cond begin if async.set schedule(t) else _wait2(async.cond, t) end end return async end ## timer-based notifications """ Timer(delay; interval = 0) Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object). Waiting tasks are woken after an initial delay of at least `delay` seconds, and then repeating after at least `interval` seconds again elapse. If `interval` is equal to `0`, the timer is only triggered once. When the timer is closed (by [`close`](@ref)) waiting tasks are woken with an error. Use [`isopen`](@ref) to check whether a timer is still active. !!! note `interval` is subject to accumulating time skew. If you need precise events at a particular absolute time, create a new timer at each expiration with the difference to the next time computed. !!! note A `Timer` requires yield points to update its state. For instance, `isopen(t::Timer)` cannot be used to timeout a non-yielding while loop. """ mutable struct Timer @atomic handle::Ptr{Cvoid} cond::ThreadSynchronizer @atomic isopen::Bool @atomic set::Bool function Timer(timeout::Real; interval::Real = 0.0) timeout โ‰ฅ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds")) interval โ‰ฅ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds")) # libuv has a tendency to timeout 1 ms early, so we need +1 on the timeout (in milliseconds), unless it is zero timeoutms = ceil(UInt64, timeout * 1000) + !iszero(timeout) intervalms = ceil(UInt64, interval * 1000) loop = eventloop() this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false) associate_julia_struct(this.handle, this) iolock_begin() err = ccall(:uv_timer_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), loop, this) @assert err == 0 finalizer(uvfinalize, this) ccall(:uv_update_time, Cvoid, (Ptr{Cvoid},), loop) err = ccall(:uv_timer_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64), this, @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)), timeoutms, intervalms) @assert err == 0 iolock_end() return this end end unsafe_convert(::Type{Ptr{Cvoid}}, t::Timer) = t.handle unsafe_convert(::Type{Ptr{Cvoid}}, async::AsyncCondition) = async.handle # if this returns true, the object has been signaled # if this returns false, the object is closed function _trywait(t::Union{Timer, AsyncCondition}) set = t.set if set # full barrier now for AsyncCondition t isa Timer || Core.Intrinsics.atomic_fence(:acquire_release) else if !isopen(t) close(t) # wait for the close to complete return false end iolock_begin() set = t.set if !set preserve_handle(t) lock(t.cond) try set = t.set if !set && t.handle != C_NULL # wait for set or handle, but not the isopen flag iolock_end() set = wait(t.cond) unlock(t.cond) iolock_begin() lock(t.cond) end finally unlock(t.cond) unpreserve_handle(t) end end iolock_end() end @atomic :monotonic t.set = false return set end function wait(t::Union{Timer, AsyncCondition}) _trywait(t) || throw(EOFError()) nothing end isopen(t::Union{Timer, AsyncCondition}) = t.isopen && t.handle != C_NULL function close(t::Union{Timer, AsyncCondition}) t.handle == C_NULL && return # short-circuit path iolock_begin() if t.handle != C_NULL if t.isopen @atomic :monotonic t.isopen = false ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) end # implement _trywait here without the auto-reset function, just waiting for the final close signal preserve_handle(t) lock(t.cond) try while t.handle != C_NULL iolock_end() wait(t.cond) unlock(t.cond) iolock_begin() lock(t.cond) end finally unlock(t.cond) unpreserve_handle(t) end end iolock_end() nothing end function uvfinalize(t::Union{Timer, AsyncCondition}) iolock_begin() lock(t.cond) try if t.handle != C_NULL disassociate_julia_struct(t.handle) # not going to call the usual close hooks anymore if t.isopen @atomic :monotonic t.isopen = false ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) end @atomic :monotonic t.handle = C_NULL notify(t.cond, false) end finally unlock(t.cond) end iolock_end() nothing end function _uv_hook_close(t::Union{Timer, AsyncCondition}) lock(t.cond) try @atomic :monotonic t.isopen = false Libc.free(@atomicswap :monotonic t.handle = C_NULL) notify(t.cond, false) finally unlock(t.cond) end nothing end function uv_asynccb(handle::Ptr{Cvoid}) async = @handle_as handle AsyncCondition lock(async.cond) # acquire barrier try @atomic :release async.set = true notify(async.cond, true) finally unlock(async.cond) end nothing end function uv_timercb(handle::Ptr{Cvoid}) t = @handle_as handle Timer lock(t.cond) try @atomic :monotonic t.set = true if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Cvoid},), t) == 0 # timer is stopped now if t.isopen @atomic :monotonic t.isopen = false ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) end end notify(t.cond, true) finally unlock(t.cond) end nothing end """ sleep(seconds) Block the current task for a specified number of seconds. The minimum sleep time is 1 millisecond or input of `0.001`. """ function sleep(sec::Real) sec โ‰ฅ 0 || throw(ArgumentError("cannot sleep for $sec seconds")) wait(Timer(sec)) nothing end # timer with repeated callback """ Timer(callback::Function, delay; interval = 0) Create a timer that runs the function `callback` at each timer expiration. Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` seconds, and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the callback is only run once. The function `callback` is called with a single argument, the timer itself. Stop a timer by calling `close`. The `callback` may still be run one final time, if the timer has already expired. # Examples Here the first number is printed after a delay of two seconds, then the following numbers are printed quickly. ```julia-repl julia> begin i = 0 cb(timer) = (global i += 1; println(i)) t = Timer(cb, 2, interval=0.2) wait(t) sleep(0.5) close(t) end 1 2 3 ``` """ function Timer(cb::Function, timeout::Real; interval::Real=0.0) timer = Timer(timeout, interval=interval) t = @task begin unpreserve_handle(timer) while _trywait(timer) try cb(timer) catch err write(stderr, "Error in Timer:\n") showerror(stderr, err, catch_backtrace()) return end isopen(timer) || return end end # here we are mimicking parts of _trywait, in coordination with task `t` preserve_handle(timer) @lock timer.cond begin if timer.set schedule(t) else _wait2(timer.cond, t) end end return timer end """ timedwait(testcb, timeout::Real; pollint::Real=0.1) Waits until `testcb()` returns `true` or `timeout` seconds have passed, whichever is earlier. The test function is polled every `pollint` seconds. The minimum value for `pollint` is 0.001 seconds, that is, 1 millisecond. Return `:ok` or `:timed_out`. """ function timedwait(testcb, timeout::Real; pollint::Real=0.1) pollint >= 1e-3 || throw(ArgumentError("pollint must be โ‰ฅ 1 millisecond")) start = time_ns() ns_timeout = 1e9 * timeout testcb() && return :ok t = Timer(pollint, interval=pollint) while _trywait(t) # stop if we ever get closed if testcb() close(t) return :ok elseif (time_ns() - start) > ns_timeout close(t) break end end return :timed_out end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/iostream.jlE# This file is a part of Julia. License is MIT: https://julialang.org/license ## IOStream const sizeof_ios_t = Int(ccall(:jl_sizeof_ios_t, Cint, ())) """ IOStream A buffered IO stream wrapping an OS file descriptor. Mostly used to represent files returned by [`open`](@ref). """ mutable struct IOStream <: IO handle::Ptr{Cvoid} ios::Array{UInt8,1} name::String mark::Int64 lock::ReentrantLock _dolock::Bool IOStream(name::AbstractString, buf::Array{UInt8,1}) = new(pointer(buf), buf, name, -1, ReentrantLock(), true) end function IOStream(name::AbstractString, finalize::Bool) buf = zeros(UInt8,sizeof_ios_t) x = IOStream(name, buf) if finalize finalizer(close, x) end return x end IOStream(name::AbstractString) = IOStream(name, true) unsafe_convert(T::Type{Ptr{Cvoid}}, s::IOStream) = convert(T, pointer(s.ios)) show(io::IO, s::IOStream) = print(io, "IOStream(", s.name, ")") macro _lock_ios(s, expr) s = esc(s) quote l = ($s)._dolock temp = ($s).lock l && lock(temp) val = $(esc(expr)) l && unlock(temp) val end end """ fd(stream) Return the file descriptor backing the stream or file. Note that this function only applies to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. """ fd(s::IOStream) = Int(ccall(:jl_ios_fd, Clong, (Ptr{Cvoid},), s.ios)) stat(s::IOStream) = stat(fd(s)) isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Cvoid},), s.ios) != 0 function close(s::IOStream) bad = @_lock_ios s ccall(:ios_close, Cint, (Ptr{Cvoid},), s.ios) != 0 systemerror("close", bad) end function flush(s::IOStream) sigatomic_begin() bad = @_lock_ios s ccall(:ios_flush, Cint, (Ptr{Cvoid},), s.ios) != 0 sigatomic_end() systemerror("flush", bad) end iswritable(s::IOStream) = ccall(:ios_get_writable, Cint, (Ptr{Cvoid},), s.ios)!=0 isreadable(s::IOStream) = ccall(:ios_get_readable, Cint, (Ptr{Cvoid},), s.ios)!=0 """ truncate(file, n) Resize the file or buffer given by the first argument to exactly `n` bytes, filling previously unallocated space with '\\0' if the file or buffer is grown. # Examples ```jldoctest julia> io = IOBuffer(); julia> write(io, "JuliaLang is a GitHub organization.") 35 julia> truncate(io, 15) IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=15, maxsize=Inf, ptr=16, mark=-1) julia> String(take!(io)) "JuliaLang is a " julia> io = IOBuffer(); julia> write(io, "JuliaLang is a GitHub organization."); julia> truncate(io, 40); julia> String(take!(io)) "JuliaLang is a GitHub organization.\\0\\0\\0\\0\\0" ``` """ function truncate(s::IOStream, n::Integer) err = @_lock_ios s ccall(:ios_trunc, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 systemerror("truncate", err) return s end """ seek(s, pos) Seek a stream to the given position. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> seek(io, 5); julia> read(io, Char) 'L': ASCII/Unicode U+004C (category Lu: Letter, uppercase) ``` """ function seek(s::IOStream, n::Integer) ret = @_lock_ios s ccall(:ios_seek, Int64, (Ptr{Cvoid}, Int64), s.ios, n) systemerror("seek", ret == -1) ret < -1 && error("seek failed") return s end """ seekstart(s) Seek a stream to its beginning. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> seek(io, 5); julia> read(io, Char) 'L': ASCII/Unicode U+004C (category Lu: Letter, uppercase) julia> seekstart(io); julia> read(io, Char) 'J': ASCII/Unicode U+004A (category Lu: Letter, uppercase) ``` """ seekstart(s::IO) = seek(s,0) """ seekend(s) Seek a stream to its end. """ function seekend(s::IOStream) err = @_lock_ios s ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0 systemerror("seekend", err) return s end """ skip(s, offset) Seek a stream relative to the current position. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> seek(io, 5); julia> skip(io, 10); julia> read(io, Char) 'G': ASCII/Unicode U+0047 (category Lu: Letter, uppercase) ``` """ function skip(s::IOStream, delta::Integer) ret = @_lock_ios s ccall(:ios_skip, Int64, (Ptr{Cvoid}, Int64), s.ios, delta) systemerror("skip", ret == -1) ret < -1 && error("skip failed") return s end """ position(s) Get the current position of a stream. # Examples ```jldoctest julia> io = IOBuffer("JuliaLang is a GitHub organization."); julia> seek(io, 5); julia> position(io) 5 julia> skip(io, 10); julia> position(io) 15 julia> seekend(io); julia> position(io) 35 ``` """ function position(s::IOStream) pos = @_lock_ios s ccall(:ios_pos, Int64, (Ptr{Cvoid},), s.ios) systemerror("position", pos == -1) return pos end function filesize(s::IOStream) sz = @_lock_ios s ccall(:ios_filesize, Int64, (Ptr{Cvoid},), s.ios) if sz == -1 err = Libc.errno() throw(IOError(string("filesize: ", Libc.strerror(err), " for ", s.name), err)) end return sz end _eof_nolock(s::IOStream) = ccall(:ios_eof_blocking, Cint, (Ptr{Cvoid},), s.ios) != 0 eof(s::IOStream) = @_lock_ios s _eof_nolock(s) ## constructing and opening streams ## # "own" means the descriptor will be closed with the IOStream """ fdio([name::AbstractString, ]fd::Integer[, own::Bool=false]) -> IOStream Create an [`IOStream`](@ref) object from an integer file descriptor. If `own` is `true`, closing this object will close the underlying descriptor. By default, an `IOStream` is closed when it is garbage collected. `name` allows you to associate the descriptor with a named file. """ function fdio(name::AbstractString, fd::Integer, own::Bool=false) s = IOStream(name) ccall(:ios_fd, Ptr{Cvoid}, (Ptr{Cvoid}, Clong, Cint, Cint), s.ios, fd, 0, own) return s end fdio(fd::Integer, own::Bool=false) = fdio(string(""), fd, own) """ open(filename::AbstractString; lock = true, keywords...) -> IOStream Open a file in a mode specified by five boolean keyword arguments: | Keyword | Description | Default | |:-----------|:-----------------------|:----------------------------------------| | `read` | open for reading | `!write` | | `write` | open for writing | `truncate \\| append` | | `create` | create if non-existent | `!read & write \\| truncate \\| append` | | `truncate` | truncate to zero size | `!read & write` | | `append` | seek to end | `false` | The default when no keywords are passed is to open files for reading only. Returns a stream for accessing the opened file. The `lock` keyword argument controls whether operations will be locked for safe multi-threaded access. !!! compat "Julia 1.5" The `lock` argument is available as of Julia 1.5. """ function open(fname::String; lock = true, read :: Union{Bool,Nothing} = nothing, write :: Union{Bool,Nothing} = nothing, create :: Union{Bool,Nothing} = nothing, truncate :: Union{Bool,Nothing} = nothing, append :: Union{Bool,Nothing} = nothing, ) flags = open_flags( read = read, write = write, create = create, truncate = truncate, append = append, ) s = IOStream(string("")) if !lock s._dolock = false end systemerror("opening file $(repr(fname))", ccall(:ios_file, Ptr{Cvoid}, (Ptr{UInt8}, Cstring, Cint, Cint, Cint, Cint), s.ios, fname, flags.read, flags.write, flags.create, flags.truncate) == C_NULL) if flags.append systemerror("seeking to end of file $fname", ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0) end return s end open(fname::AbstractString; kwargs...) = open(convert(String, fname)::String; kwargs...) """ open(filename::AbstractString, [mode::AbstractString]; lock = true) -> IOStream Alternate syntax for open, where a string-based mode specifier is used instead of the five booleans. The values of `mode` correspond to those from `fopen(3)` or Perl `open`, and are equivalent to setting the following boolean groups: | Mode | Description | Keywords | |:-----|:------------------------------|:------------------------------------| | `r` | read | none | | `w` | write, create, truncate | `write = true` | | `a` | write, create, append | `append = true` | | `r+` | read, write | `read = true, write = true` | | `w+` | read, write, create, truncate | `truncate = true, read = true` | | `a+` | read, write, create, append | `append = true, read = true` | The `lock` keyword argument controls whether operations will be locked for safe multi-threaded access. # Examples ```jldoctest julia> io = open("myfile.txt", "w"); julia> write(io, "Hello world!"); julia> close(io); julia> io = open("myfile.txt", "r"); julia> read(io, String) "Hello world!" julia> write(io, "This file is read only") ERROR: ArgumentError: write failed, IOStream is not writeable [...] julia> close(io) julia> io = open("myfile.txt", "a"); julia> write(io, "This stream is not read only") 28 julia> close(io) julia> rm("myfile.txt") ``` !!! compat "Julia 1.5" The `lock` argument is available as of Julia 1.5. """ function open(fname::AbstractString, mode::AbstractString; lock = true) mode == "r" ? open(fname, lock = lock, read = true) : mode == "r+" ? open(fname, lock = lock, read = true, write = true) : mode == "w" ? open(fname, lock = lock, truncate = true) : mode == "w+" ? open(fname, lock = lock, truncate = true, read = true) : mode == "a" ? open(fname, lock = lock, append = true) : mode == "a+" ? open(fname, lock = lock, append = true, read = true) : throw(ArgumentError("invalid open mode: $mode")) end ## low-level calls ## function write(s::IOStream, b::UInt8) iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) Int(@_lock_ios s ccall(:ios_putc, Cint, (Cint, Ptr{Cvoid}), b, s.ios)) end function unsafe_write(s::IOStream, p::Ptr{UInt8}, nb::UInt) iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) return Int(@_lock_ios s ccall(:ios_write, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, p, nb)) end # num bytes available without blocking bytesavailable(s::IOStream) = @_lock_ios s ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) function readavailable(s::IOStream) lock(s.lock) nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) if nb == 0 ccall(:ios_fillbuf, Cssize_t, (Ptr{Cvoid},), s.ios) nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) end a = Vector{UInt8}(undef, nb) nr = ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, a, nb) if nr != nb unlock(s.lock) throw(EOFError()) end unlock(s.lock) return a end function read(s::IOStream, ::Type{UInt8}) b = @_lock_ios s ccall(:ios_getc, Cint, (Ptr{Cvoid},), s.ios) if b == -1 throw(EOFError()) end return b % UInt8 end if ENDIAN_BOM == 0x04030201 function read(s::IOStream, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64}}) n = sizeof(T) l = s._dolock _lock = s.lock l && lock(_lock) if ccall(:jl_ios_buffer_n, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 l && unlock(_lock) throw(EOFError()) end x = ccall(:jl_ios_get_nbyte_int, UInt64, (Ptr{Cvoid}, Csize_t), s.ios, n) % T l && unlock(_lock) return x end read(s::IOStream, ::Type{Float16}) = reinterpret(Float16, read(s, Int16)) read(s::IOStream, ::Type{Float32}) = reinterpret(Float32, read(s, Int32)) read(s::IOStream, ::Type{Float64}) = reinterpret(Float64, read(s, Int64)) end function unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt) nr = @_lock_ios s ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, p, nb) if nr != nb throw(EOFError()) end nothing end ## text I/O ## take!(s::IOStream) = @_lock_ios s ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Cvoid},), s.ios) function readuntil(s::IOStream, delim::UInt8; keep::Bool=false) @_lock_ios s ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 0, !keep) end # like readuntil, above, but returns a String without requiring a copy function readuntil_string(s::IOStream, delim::UInt8, keep::Bool) @_lock_ios s ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 1, !keep) end function readline(s::IOStream; keep::Bool=false) @_lock_ios s ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, '\n', 1, keep ? 0 : 2) end function readbytes_all!(s::IOStream, b::Union{Array{UInt8}, FastContiguousSubArray{UInt8,<:Any,<:Array{UInt8}}}, nb::Integer) olb = lb = length(b) nr = 0 let l = s._dolock, slock = s.lock l && lock(slock) GC.@preserve b while nr < nb if lb < nr+1 try lb = max(65536, (nr+1) * 2) resize!(b, lb) catch l && unlock(slock) rethrow() end end thisr = Int(ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) nr += thisr (nr == nb || thisr == 0 || _eof_nolock(s)) && break end l && unlock(slock) end if lb > olb && lb > nr resize!(b, max(olb, nr)) # shrink to just contain input data if was resized end return nr end function readbytes_some!(s::IOStream, b::Union{Array{UInt8}, FastContiguousSubArray{UInt8,<:Any,<:Array{UInt8}}}, nb::Integer) olb = length(b) if nb > olb resize!(b, nb) end local nr @_lock_ios s begin nr = GC.@preserve b Int(ccall(:ios_read, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, pointer(b), nb)) end lb = length(b) if lb > olb && lb > nr resize!(b, max(olb, nr)) # shrink to just contain input data if was resized end return nr end """ readbytes!(stream::IOStream, b::AbstractVector{UInt8}, nb=length(b); all::Bool=true) Read at most `nb` bytes from `stream` into `b`, returning the number of bytes read. The size of `b` will be increased if needed (i.e. if `nb` is greater than `length(b)` and enough bytes could be read), but it will never be decreased. If `all` is `true` (the default), this function will block repeatedly trying to read all requested bytes, until an error or end-of-file occurs. If `all` is `false`, at most one `read` call is performed, and the amount of data returned is device-dependent. Note that not all stream types support the `all` option. """ function readbytes!(s::IOStream, b::Union{Array{UInt8}, FastContiguousSubArray{UInt8,<:Any,<:Array{UInt8}}}, nb=length(b); all::Bool=true) return all ? readbytes_all!(s, b, nb) : readbytes_some!(s, b, nb) end function read(s::IOStream) # First we try to fill the buffer. If that gives us the whole file, # copy it out and return. Otherwise look at the file size and use it # to prealloate space. Determining the size requires extra syscalls, # which we want to avoid for small files. @_lock_ios s begin nb = ccall(:ios_fillbuf, Cssize_t, (Ptr{Cvoid},), s.ios) if nb != -1 b = StringVector(nb) readbytes_all!(s, b, nb) else sz = try # filesize is just a hint, so ignore if it fails filesize(s) catch ex ex isa IOError || rethrow() Int64(-1) end if sz > 0 pos = position(s) if pos > 0 sz -= pos end end b = StringVector(sz < 0 ? 1024 : sz) nr = readbytes_all!(s, b, sz < 0 ? typemax(Int) : sz) resize!(b, nr) end end return b end """ read(s::IOStream, nb::Integer; all=true) Read at most `nb` bytes from `s`, returning a `Vector{UInt8}` of the bytes read. If `all` is `true` (the default), this function will block repeatedly trying to read all requested bytes, until an error or end-of-file occurs. If `all` is `false`, at most one `read` call is performed, and the amount of data returned is device-dependent. Note that not all stream types support the `all` option. """ function read(s::IOStream, nb::Integer; all::Bool=true) # When all=false we have to allocate a buffer of the requested size upfront # since a single call will be made b = Vector{UInt8}(undef, all && nb == typemax(Int) ? 1024 : nb) nr = readbytes!(s, b, nb, all=all) resize!(b, nr) return b end ## peek ## function peek(s::IOStream, ::Type{UInt8}) b = @_lock_ios s ccall(:ios_peekc, Cint, (Ptr{Cvoid},), s.ios) if b == -1 throw(EOFError()) end return b % UInt8 end M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/stream.jlภป# This file is a part of Julia. License is MIT: https://julialang.org/license import .Libc: RawFD, dup if Sys.iswindows() import .Libc: WindowsRawSocket const OS_HANDLE = WindowsRawSocket const INVALID_OS_HANDLE = WindowsRawSocket(Ptr{Cvoid}(-1)) else const OS_HANDLE = RawFD const INVALID_OS_HANDLE = RawFD(-1) end ## types ## abstract type IOServer end """ LibuvServer An abstract type for IOServers handled by libuv. If `server isa LibuvServer`, it must obey the following interface: - `server.handle` must be a `Ptr{Cvoid}` - `server.status` must be an `Int` - `server.cond` must be a `GenericCondition` """ abstract type LibuvServer <: IOServer end function getproperty(server::LibuvServer, name::Symbol) if name === :handle return getfield(server, :handle)::Ptr{Cvoid} elseif name === :status return getfield(server, :status)::Int elseif name === :cond return getfield(server, :cond)::GenericCondition else return getfield(server, name) end end """ LibuvStream An abstract type for IO streams handled by libuv. If `stream isa LibuvStream`, it must obey the following interface: - `stream.handle`, if present, must be a `Ptr{Cvoid}` - `stream.status`, if present, must be an `Int` - `stream.buffer`, if present, must be an `IOBuffer` - `stream.sendbuf`, if present, must be a `Union{Nothing,IOBuffer}` - `stream.cond`, if present, must be a `GenericCondition` - `stream.lock`, if present, must be an `AbstractLock` - `stream.throttle`, if present, must be an `Int` """ abstract type LibuvStream <: IO end function getproperty(stream::LibuvStream, name::Symbol) if name === :handle return getfield(stream, :handle)::Ptr{Cvoid} elseif name === :status return getfield(stream, :status)::Int elseif name === :buffer return getfield(stream, :buffer)::IOBuffer elseif name === :sendbuf return getfield(stream, :sendbuf)::Union{Nothing,IOBuffer} elseif name === :cond return getfield(stream, :cond)::GenericCondition elseif name === :lock return getfield(stream, :lock)::AbstractLock elseif name === :throttle return getfield(stream, :throttle)::Int else return getfield(stream, name) end end # IO # +- GenericIOBuffer{T<:AbstractArray{UInt8,1}} (not exported) # +- AbstractPipe (not exported) # . +- Pipe # . +- Process (not exported) # . +- ProcessChain (not exported) # +- DevNull (not exported) # +- Filesystem.File # +- LibuvStream (not exported) # . +- PipeEndpoint (not exported) # . +- TCPSocket # . +- TTY (not exported) # . +- UDPSocket # . +- BufferStream (FIXME: 2.0) # +- IOBuffer = Base.GenericIOBuffer{Array{UInt8,1}} # +- IOStream # IOServer # +- LibuvServer # . +- PipeServer # . +- TCPServer # Redirectable = Union{IO, FileRedirect, Libc.RawFD} (not exported) bytesavailable(s::LibuvStream) = bytesavailable(s.buffer) function eof(s::LibuvStream) bytesavailable(s) > 0 && return false wait_readnb(s, 1) # This function is race-y if used from multiple threads, but we guarantee # it to never return true until the stream is definitively exhausted # and that we won't return true if there's a readerror pending (it'll instead get thrown). # This requires some careful ordering here (TODO: atomic loads) bytesavailable(s) > 0 && return false open = isreadable(s) # must precede readerror check s.readerror === nothing || throw(s.readerror) return !open end # Limit our default maximum read and buffer size, # to avoid DoS-ing ourself into an OOM situation const DEFAULT_READ_BUFFER_SZ = 10485760 # 10 MB # manually limit our write size, if the OS doesn't support full-size writes if Sys.iswindows() const MAX_OS_WRITE = UInt(0x1FF0_0000) # 511 MB (determined semi-empirically, limited to 31 MB on XP) else const MAX_OS_WRITE = UInt(typemax(Csize_t)) end const StatusUninit = 0 # handle is allocated, but not initialized const StatusInit = 1 # handle is valid, but not connected/active const StatusConnecting = 2 # handle is in process of connecting const StatusOpen = 3 # handle is usable const StatusActive = 4 # handle is listening for read/write/connect events const StatusClosing = 5 # handle is closing / being closed const StatusClosed = 6 # handle is closed const StatusEOF = 7 # handle is a TTY that has seen an EOF event (pretends to be closed until reseteof is called) const StatusPaused = 8 # handle is Active, but not consuming events, and will transition to Open if it receives an event function uv_status_string(x) s = x.status if x.handle == C_NULL if s == StatusClosed return "closed" elseif s == StatusUninit return "null" end return "invalid status" elseif s == StatusUninit return "uninit" elseif s == StatusInit return "init" elseif s == StatusConnecting return "connecting" elseif s == StatusOpen return "open" elseif s == StatusActive return "active" elseif s == StatusPaused return "paused" elseif s == StatusClosing return "closing" elseif s == StatusClosed return "closed" elseif s == StatusEOF return "eof" end return "invalid status" end mutable struct PipeEndpoint <: LibuvStream handle::Ptr{Cvoid} status::Int buffer::IOBuffer cond::ThreadSynchronizer readerror::Any sendbuf::Union{IOBuffer, Nothing} lock::ReentrantLock # advisory lock throttle::Int function PipeEndpoint(handle::Ptr{Cvoid}, status) p = new(handle, status, PipeBuffer(), ThreadSynchronizer(), nothing, nothing, ReentrantLock(), DEFAULT_READ_BUFFER_SZ) associate_julia_struct(handle, p) finalizer(uvfinalize, p) return p end end function PipeEndpoint() pipe = PipeEndpoint(Libc.malloc(_sizeof_uv_named_pipe), StatusUninit) iolock_begin() err = ccall(:uv_pipe_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), eventloop(), pipe.handle, 0) uv_error("failed to create pipe endpoint", err) pipe.status = StatusInit iolock_end() return pipe end function PipeEndpoint(fd::OS_HANDLE) pipe = PipeEndpoint() iolock_begin() err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), pipe.handle, fd) uv_error("pipe_open", err) pipe.status = StatusOpen iolock_end() return pipe end if OS_HANDLE != RawFD PipeEndpoint(fd::RawFD) = PipeEndpoint(Libc._get_osfhandle(fd)) end mutable struct TTY <: LibuvStream handle::Ptr{Cvoid} status::Int buffer::IOBuffer cond::ThreadSynchronizer readerror::Any sendbuf::Union{IOBuffer, Nothing} lock::ReentrantLock # advisory lock throttle::Int @static if Sys.iswindows(); ispty::Bool; end function TTY(handle::Ptr{Cvoid}, status) tty = new( handle, status, PipeBuffer(), ThreadSynchronizer(), nothing, nothing, ReentrantLock(), DEFAULT_READ_BUFFER_SZ) associate_julia_struct(handle, tty) finalizer(uvfinalize, tty) @static if Sys.iswindows() tty.ispty = ccall(:jl_ispty, Cint, (Ptr{Cvoid},), handle) != 0 end return tty end end function TTY(fd::OS_HANDLE) tty = TTY(Libc.malloc(_sizeof_uv_tty), StatusUninit) iolock_begin() err = ccall(:uv_tty_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Int32), eventloop(), tty.handle, fd, 0) uv_error("TTY", err) tty.status = StatusOpen iolock_end() return tty end if OS_HANDLE != RawFD TTY(fd::RawFD) = TTY(Libc._get_osfhandle(fd)) end show(io::IO, stream::LibuvServer) = print(io, typeof(stream), "(", _fd(stream), " ", uv_status_string(stream), ")") show(io::IO, stream::LibuvStream) = print(io, typeof(stream), "(", _fd(stream), " ", uv_status_string(stream), ", ", bytesavailable(stream.buffer), " bytes waiting)") # Shared LibuvStream object interface function isreadable(io::LibuvStream) bytesavailable(io) > 0 && return true isopen(io) || return false io.status == StatusEOF && return false return ccall(:uv_is_readable, Cint, (Ptr{Cvoid},), io.handle) != 0 end function iswritable(io::LibuvStream) isopen(io) || return false io.status == StatusClosing && return false return ccall(:uv_is_writable, Cint, (Ptr{Cvoid},), io.handle) != 0 end lock(s::LibuvStream) = lock(s.lock) unlock(s::LibuvStream) = unlock(s.lock) setup_stdio(stream::LibuvStream, ::Bool) = (stream, false) rawhandle(stream::LibuvStream) = stream.handle unsafe_convert(::Type{Ptr{Cvoid}}, s::Union{LibuvStream, LibuvServer}) = s.handle function init_stdio(handle::Ptr{Cvoid}) iolock_begin() t = ccall(:jl_uv_handle_type, Int32, (Ptr{Cvoid},), handle) local io if t == UV_FILE fd = ccall(:jl_uv_file_handle, OS_HANDLE, (Ptr{Cvoid},), handle) # TODO: Replace ios.c file with libuv fs? # return File(fd) @static if Sys.iswindows() # TODO: Get ios.c to understand native handles fd = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), fd, 0) end # TODO: Get fdio to work natively with file descriptors instead of integers io = fdio(cconvert(Cint, fd)) elseif t == UV_TTY io = TTY(handle, StatusOpen) elseif t == UV_TCP Sockets = require(PkgId(UUID((0x6462fe0b_24de_5631, 0x8697_dd941f90decc)), "Sockets")) io = Sockets.TCPSocket(handle, StatusOpen) elseif t == UV_NAMED_PIPE io = PipeEndpoint(handle, StatusOpen) else throw(ArgumentError("invalid stdio type: $t")) end iolock_end() return io end """ open(fd::OS_HANDLE) -> IO Take a raw file descriptor wrap it in a Julia-aware IO type, and take ownership of the fd handle. Call `open(Libc.dup(fd))` to avoid the ownership capture of the original handle. !!! warning Do not call this on a handle that's already owned by some other part of the system. """ function open(h::OS_HANDLE) iolock_begin() t = ccall(:uv_guess_handle, Cint, (OS_HANDLE,), h) local io if t == UV_FILE @static if Sys.iswindows() # TODO: Get ios.c to understand native handles h = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), h, 0) end # TODO: Get fdio to work natively with file descriptors instead of integers io = fdio(cconvert(Cint, h)) elseif t == UV_TTY io = TTY(h) elseif t == UV_TCP Sockets = require(PkgId(UUID((0x6462fe0b_24de_5631, 0x8697_dd941f90decc)), "Sockets")) io = Sockets.TCPSocket(h) elseif t == UV_NAMED_PIPE io = PipeEndpoint(h) @static if Sys.iswindows() if ccall(:jl_ispty, Cint, (Ptr{Cvoid},), io.handle) != 0 # replace the Julia `PipeEndpoint` type with a `TTY` type, # if we detect that this is a cygwin pty object pipe_handle, pipe_status = io.handle, io.status io.status = StatusClosed io.handle = C_NULL io = TTY(pipe_handle, pipe_status) end end else throw(ArgumentError("invalid stdio type: $t")) end iolock_end() return io end if OS_HANDLE != RawFD function open(fd::RawFD) h = Libc.dup(Libc._get_osfhandle(fd)) # make a dup to steal ownership away from msvcrt try io = open(h) ccall(:_close, Cint, (RawFD,), fd) # on success, destroy the old libc handle return io catch ex ccall(:CloseHandle, stdcall, Cint, (OS_HANDLE,), h) # on failure, destroy the new nt handle rethrow(ex) end end end function isopen(x::Union{LibuvStream, LibuvServer}) if x.status == StatusUninit || x.status == StatusInit || x.handle === C_NULL throw(ArgumentError("$x is not initialized")) end return x.status != StatusClosed end function check_open(x::Union{LibuvStream, LibuvServer}) if !isopen(x) || x.status == StatusClosing throw(IOError("stream is closed or unusable", 0)) end end function wait_readnb(x::LibuvStream, nb::Int) # fast path before iolock acquire bytesavailable(x.buffer) >= nb && return open = isopen(x) && x.status != StatusEOF # must precede readerror check x.readerror === nothing || throw(x.readerror) open || return iolock_begin() # repeat fast path after iolock acquire, before other expensive work bytesavailable(x.buffer) >= nb && (iolock_end(); return) open = isopen(x) && x.status != StatusEOF x.readerror === nothing || throw(x.readerror) open || (iolock_end(); return) # now do the "real" work oldthrottle = x.throttle preserve_handle(x) lock(x.cond) try while bytesavailable(x.buffer) < nb x.readerror === nothing || throw(x.readerror) isopen(x) || break x.status == StatusEOF && break x.throttle = max(nb, x.throttle) start_reading(x) # ensure we are reading iolock_end() wait(x.cond) unlock(x.cond) iolock_begin() lock(x.cond) end finally if isempty(x.cond) stop_reading(x) # stop reading iff there are currently no other read clients of the stream end if oldthrottle <= x.throttle <= nb # if we're interleaving readers, we might not get back to the "original" throttle # but we consider that an acceptable "risk", since we can't be quite sure what the intended value is now x.throttle = oldthrottle end unpreserve_handle(x) unlock(x.cond) end iolock_end() nothing end function closewrite(s::LibuvStream) iolock_begin() check_open(s) req = Libc.malloc(_sizeof_uv_shutdown) uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call err = ccall(:uv_shutdown, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), req, s, @cfunction(uv_shutdowncb_task, Cvoid, (Ptr{Cvoid}, Cint))) if err < 0 Libc.free(req) uv_error("shutdown", err) end ct = current_task() preserve_handle(ct) sigatomic_begin() uv_req_set_data(req, ct) iolock_end() status = try sigatomic_end() wait()::Cint finally # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we won't get spurious notifications later uv_req_set_data(req, C_NULL) else # done with req Libc.free(req) end iolock_end() unpreserve_handle(ct) end if isopen(s) if status < 0 || ccall(:uv_is_readable, Cint, (Ptr{Cvoid},), s.handle) == 0 close(s) end end if status < 0 throw(_UVError("shutdown", status)) end nothing end function wait_close(x::Union{LibuvStream, LibuvServer}) preserve_handle(x) lock(x.cond) try while isopen(x) wait(x.cond) end finally unlock(x.cond) unpreserve_handle(x) end nothing end function close(stream::Union{LibuvStream, LibuvServer}) iolock_begin() if stream.status == StatusInit ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing elseif isopen(stream) if stream.status != StatusClosing ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing end end iolock_end() wait_close(stream) nothing end function uvfinalize(uv::Union{LibuvStream, LibuvServer}) iolock_begin() if uv.handle != C_NULL disassociate_julia_struct(uv.handle) # not going to call the usual close hooks (so preserve_handle is not needed) if uv.status == StatusUninit Libc.free(uv.handle) elseif uv.status == StatusInit ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), uv.handle) elseif isopen(uv) if uv.status != StatusClosing ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), uv.handle) end elseif uv.status == StatusClosed Libc.free(uv.handle) end uv.handle = C_NULL uv.status = StatusClosed end iolock_end() nothing end if Sys.iswindows() ispty(s::TTY) = s.ispty ispty(s::IO) = false end """ displaysize([io::IO]) -> (lines, columns) Return the nominal size of the screen that may be used for rendering output to this `IO` object. If no input is provided, the environment variables `LINES` and `COLUMNS` are read. If those are not set, a default size of `(24, 80)` is returned. # Examples ```jldoctest julia> withenv("LINES" => 30, "COLUMNS" => 100) do displaysize() end (30, 100) ``` To get your TTY size, ```julia-repl julia> displaysize(stdout) (34, 147) ``` """ displaysize(io::IO) = displaysize() displaysize() = (parse(Int, get(ENV, "LINES", "24")), parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int} function displaysize(io::TTY) check_open(io) local h::Int, w::Int default_size = displaysize() @static if Sys.iswindows() if ispty(io) # io is actually a libuv pipe but a cygwin/msys2 pty try h, w = parse.(Int, split(read(open(Base.Cmd(String["stty", "size"]), "r", io).out, String))) h > 0 || (h = default_size[1]) w > 0 || (w = default_size[2]) return h, w catch return default_size end end end s1 = Ref{Int32}(0) s2 = Ref{Int32}(0) iolock_begin() check_open(io) Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize, Int32, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}), io, s1, s2) != 0) iolock_end() w, h = s1[], s2[] h > 0 || (h = default_size[1]) w > 0 || (w = default_size[2]) return h, w end ### Libuv callbacks ### ## BUFFER ## ## Allocate space in buffer (for immediate use) function alloc_request(buffer::IOBuffer, recommended_size::UInt) ensureroom(buffer, Int(recommended_size)) ptr = buffer.append ? buffer.size + 1 : buffer.ptr nb = min(length(buffer.data), buffer.maxsize) - ptr + 1 return (Ptr{Cvoid}(pointer(buffer.data, ptr)), nb) end notify_filled(buffer::IOBuffer, nread::Int, base::Ptr{Cvoid}, len::UInt) = notify_filled(buffer, nread) function notify_filled(buffer::IOBuffer, nread::Int) if buffer.append buffer.size += nread else buffer.ptr += nread end nothing end function alloc_buf_hook(stream::LibuvStream, size::UInt) throttle = UInt(stream.throttle) return alloc_request(stream.buffer, (size > throttle) ? throttle : size) end function uv_alloc_buf(handle::Ptr{Cvoid}, size::Csize_t, buf::Ptr{Cvoid}) hd = uv_handle_data(handle) if hd == C_NULL ccall(:jl_uv_buf_set_len, Cvoid, (Ptr{Cvoid}, Csize_t), buf, 0) return nothing end stream = unsafe_pointer_to_objref(hd)::LibuvStream local data::Ptr{Cvoid}, newsize::Csize_t if stream.status != StatusActive data = C_NULL newsize = 0 else (data, newsize) = alloc_buf_hook(stream, UInt(size)) if data == C_NULL newsize = 0 end # avoid aliasing of `nread` with `errno` in uv_readcb # or exceeding the Win32 maximum uv_buf_t len maxsize = @static Sys.iswindows() ? typemax(Cint) : typemax(Cssize_t) newsize > maxsize && (newsize = maxsize) end ccall(:jl_uv_buf_set_base, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), buf, data) ccall(:jl_uv_buf_set_len, Cvoid, (Ptr{Cvoid}, Csize_t), buf, newsize) nothing end function uv_readcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid}) stream_unknown_type = @handle_as handle LibuvStream nrequested = ccall(:jl_uv_buf_len, Csize_t, (Ptr{Cvoid},), buf) function readcb_specialized(stream::LibuvStream, nread::Int, nrequested::UInt) lock(stream.cond) if nread < 0 if nread == UV_ENOBUFS && nrequested == 0 # remind the client that stream.buffer is full notify(stream.cond) elseif nread == UV_EOF # libuv called uv_stop_reading already if stream.status != StatusClosing stream.status = StatusEOF notify(stream.cond) if stream isa TTY # stream can still be used by reseteof (or possibly write) elseif !(stream isa PipeEndpoint) && ccall(:uv_is_writable, Cint, (Ptr{Cvoid},), stream.handle) != 0 # stream can still be used by write else # underlying stream is no longer useful: begin finalization ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing end end else stream.readerror = _UVError("read", nread) notify(stream.cond) # This is a fatal connection error ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) stream.status = StatusClosing end else notify_filled(stream.buffer, nread) notify(stream.cond) end unlock(stream.cond) # Stop background reading when # 1) there's nobody paying attention to the data we are reading # 2) we have accumulated a lot of unread data OR # 3) we have an alternate buffer that has reached its limit. if stream.status == StatusPaused || (stream.status == StatusActive && ((bytesavailable(stream.buffer) >= stream.throttle) || (bytesavailable(stream.buffer) >= stream.buffer.maxsize))) # save cycles by stopping kernel notifications from arriving ccall(:uv_read_stop, Cint, (Ptr{Cvoid},), stream) stream.status = StatusOpen end nothing end readcb_specialized(stream_unknown_type, Int(nread), UInt(nrequested)) nothing end function reseteof(x::TTY) iolock_begin() if x.status == StatusEOF x.status = StatusOpen end iolock_end() nothing end function _uv_hook_close(uv::Union{LibuvStream, LibuvServer}) lock(uv.cond) try uv.status = StatusClosed # notify any listeners that exist on this libuv stream type notify(uv.cond) finally unlock(uv.cond) end nothing end ########################################## # Pipe Abstraction # (composed of two half-pipes: .in and .out) ########################################## mutable struct Pipe <: AbstractPipe in::PipeEndpoint # writable out::PipeEndpoint # readable end """ Construct an uninitialized Pipe object. The appropriate end of the pipe will be automatically initialized if the object is used in process spawning. This can be useful to easily obtain references in process pipelines, e.g.: ``` julia> err = Pipe() # After this `err` will be initialized and you may read `foo`'s # stderr from the `err` pipe. julia> run(pipeline(pipeline(`foo`, stderr=err), `cat`), wait=false) ``` """ Pipe() = Pipe(PipeEndpoint(), PipeEndpoint()) pipe_reader(p::Pipe) = p.out pipe_writer(p::Pipe) = p.in function link_pipe!(pipe::Pipe; reader_supports_async = false, writer_supports_async = false) link_pipe!(pipe.out, reader_supports_async, pipe.in, writer_supports_async) return pipe end show(io::IO, stream::Pipe) = print(io, "Pipe(", _fd(stream.in), " ", uv_status_string(stream.in), " => ", _fd(stream.out), " ", uv_status_string(stream.out), ", ", bytesavailable(stream), " bytes waiting)") ## Functions for PipeEndpoint and PipeServer ## function open_pipe!(p::PipeEndpoint, handle::OS_HANDLE) iolock_begin() if p.status != StatusInit error("pipe is already in use or has been closed") end err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), p.handle, handle) uv_error("pipe_open", err) p.status = StatusOpen iolock_end() return p end function link_pipe!(read_end::PipeEndpoint, reader_supports_async::Bool, write_end::PipeEndpoint, writer_supports_async::Bool) rd, wr = link_pipe(reader_supports_async, writer_supports_async) try try open_pipe!(read_end, rd) catch close_pipe_sync(rd) rethrow() end open_pipe!(write_end, wr) catch close_pipe_sync(wr) rethrow() end nothing end function link_pipe(reader_supports_async::Bool, writer_supports_async::Bool) UV_NONBLOCK_PIPE = 0x40 fildes = Ref{Pair{OS_HANDLE, OS_HANDLE}}(INVALID_OS_HANDLE => INVALID_OS_HANDLE) # read (in) => write (out) err = ccall(:uv_pipe, Int32, (Ptr{Pair{OS_HANDLE, OS_HANDLE}}, Cint, Cint), fildes, reader_supports_async * UV_NONBLOCK_PIPE, writer_supports_async * UV_NONBLOCK_PIPE) uv_error("pipe", err) return fildes[] end if Sys.iswindows() function close_pipe_sync(handle::WindowsRawSocket) ccall(:CloseHandle, stdcall, Cint, (WindowsRawSocket,), handle) nothing end else function close_pipe_sync(handle::RawFD) ccall(:close, Cint, (RawFD,), handle) nothing end end ## Functions for any LibuvStream ## # flow control function start_reading(stream::LibuvStream) iolock_begin() if stream.status == StatusOpen if !isreadable(stream) error("tried to read a stream that is not readable") end # libuv may call the alloc callback immediately # for a TTY on Windows, so ensure the status is set first stream.status = StatusActive ret = ccall(:uv_read_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), stream, @cfunction(uv_alloc_buf, Cvoid, (Ptr{Cvoid}, Csize_t, Ptr{Cvoid})), @cfunction(uv_readcb, Cvoid, (Ptr{Cvoid}, Cssize_t, Ptr{Cvoid}))) elseif stream.status == StatusPaused stream.status = StatusActive ret = Int32(0) elseif stream.status == StatusActive ret = Int32(0) else ret = Int32(-1) end iolock_end() return ret end if Sys.iswindows() # the low performance version of stop_reading is required # on Windows due to a NT kernel bug that we can't use a blocking # stream for non-blocking (overlapped) calls, # and a ReadFile call blocking on one thread # causes all other operations on that stream to lockup function stop_reading(stream::LibuvStream) iolock_begin() if stream.status == StatusActive stream.status = StatusOpen ccall(:uv_read_stop, Cint, (Ptr{Cvoid},), stream) end iolock_end() nothing end else function stop_reading(stream::LibuvStream) iolock_begin() if stream.status == StatusActive stream.status = StatusPaused end iolock_end() nothing end end # bulk read / write readbytes!(s::LibuvStream, a::Vector{UInt8}, nb = length(a)) = readbytes!(s, a, Int(nb)) function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) iolock_begin() sbuf = s.buffer @assert sbuf.seekable == false @assert sbuf.maxsize >= nb function wait_locked(s, buf, nb) while bytesavailable(buf) < nb s.readerror === nothing || throw(s.readerror) isopen(s) || break s.status != StatusEOF || break iolock_end() wait_readnb(s, nb) iolock_begin() end end if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer wait_locked(s, sbuf, nb) end if bytesavailable(sbuf) >= nb nread = readbytes!(sbuf, a, nb) else newbuf = PipeBuffer(a, maxsize=nb) newbuf.size = 0 # reset the write pointer to the beginning nread = try s.buffer = newbuf write(newbuf, sbuf) wait_locked(s, newbuf, nb) bytesavailable(newbuf) finally s.buffer = sbuf end compact(newbuf) end iolock_end() return nread end function read(stream::LibuvStream) wait_readnb(stream, typemax(Int)) iolock_begin() bytes = take!(stream.buffer) iolock_end() return bytes end function unsafe_read(s::LibuvStream, p::Ptr{UInt8}, nb::UInt) iolock_begin() sbuf = s.buffer @assert sbuf.seekable == false @assert sbuf.maxsize >= nb function wait_locked(s, buf, nb) while bytesavailable(buf) < nb s.readerror === nothing || throw(s.readerror) isopen(s) || throw(EOFError()) s.status != StatusEOF || throw(EOFError()) iolock_end() wait_readnb(s, nb) iolock_begin() end end if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer wait_locked(s, sbuf, Int(nb)) end if bytesavailable(sbuf) >= nb unsafe_read(sbuf, p, nb) else newbuf = PipeBuffer(unsafe_wrap(Array, p, nb), maxsize=Int(nb)) newbuf.size = 0 # reset the write pointer to the beginning try s.buffer = newbuf write(newbuf, sbuf) wait_locked(s, newbuf, Int(nb)) finally s.buffer = sbuf end end iolock_end() nothing end function read(this::LibuvStream, ::Type{UInt8}) iolock_begin() sbuf = this.buffer @assert sbuf.seekable == false while bytesavailable(sbuf) < 1 iolock_end() eof(this) && throw(EOFError()) iolock_begin() end c = read(sbuf, UInt8) iolock_end() return c end function readavailable(this::LibuvStream) wait_readnb(this, 1) # unlike the other `read` family of functions, this one doesn't guarantee error reporting iolock_begin() buf = this.buffer @assert buf.seekable == false bytes = take!(buf) iolock_end() return bytes end function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) iolock_begin() buf = x.buffer @assert buf.seekable == false if !occursin(c, buf) # fast path checks first x.readerror === nothing || throw(x.readerror) if isopen(x) && x.status != StatusEOF preserve_handle(x) lock(x.cond) try while !occursin(c, x.buffer) x.readerror === nothing || throw(x.readerror) isopen(x) || break x.status != StatusEOF || break start_reading(x) # ensure we are reading iolock_end() wait(x.cond) unlock(x.cond) iolock_begin() lock(x.cond) end finally if isempty(x.cond) stop_reading(x) # stop reading iff there are currently no other read clients of the stream end unlock(x.cond) unpreserve_handle(x) end end end bytes = readuntil(buf, c, keep=keep) iolock_end() return bytes end uv_write(s::LibuvStream, p::Vector{UInt8}) = GC.@preserve p uv_write(s, pointer(p), UInt(sizeof(p))) # caller must have acquired the iolock function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) uvw = uv_write_async(s, p, n) ct = current_task() preserve_handle(ct) sigatomic_begin() uv_req_set_data(uvw, ct) iolock_end() status = try sigatomic_end() # wait for the last chunk to complete (or error) # assume that any errors would be sticky, # (so we don't need to monitor the error status of the intermediate writes) wait()::Cint finally # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end sigatomic_end() iolock_begin() ct.queue === nothing || list_deletefirst!(ct.queue::IntrusiveLinkedList{Task}, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later uv_req_set_data(uvw, C_NULL) else # done with uvw Libc.free(uvw) end iolock_end() unpreserve_handle(ct) end if status < 0 throw(_UVError("write", status)) end return Int(n) end # helper function for uv_write that returns the uv_write_t struct for the write # rather than waiting on it, caller must hold the iolock function uv_write_async(s::LibuvStream, p::Ptr{UInt8}, n::UInt) check_open(s) while true uvw = Libc.malloc(_sizeof_uv_write) uv_req_set_data(uvw, C_NULL) # in case we get interrupted before arriving at the wait call nwrite = min(n, MAX_OS_WRITE) # split up the write into chunks the OS can handle. # TODO: use writev instead of a loop err = ccall(:jl_uv_write, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, UInt, Ptr{Cvoid}, Ptr{Cvoid}), s, p, nwrite, uvw, @cfunction(uv_writecb_task, Cvoid, (Ptr{Cvoid}, Cint))) if err < 0 Libc.free(uvw) uv_error("write", err) end n -= nwrite p += nwrite if n == 0 return uvw end end end # Optimized send # - smaller writes are buffered, final uv write on flush or when buffer full # - large isbits arrays are unbuffered and written directly function unsafe_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) while true # try to add to the send buffer iolock_begin() buf = s.sendbuf buf === nothing && break totb = bytesavailable(buf) + n if totb < buf.maxsize nb = unsafe_write(buf, p, n) iolock_end() return nb end bytesavailable(buf) == 0 && break # perform flush(s) arr = take!(buf) uv_write(s, arr) end # perform the output to the kernel return uv_write(s, p, n) end function flush(s::LibuvStream) iolock_begin() buf = s.sendbuf if buf !== nothing if bytesavailable(buf) > 0 arr = take!(buf) uv_write(s, arr) return end end uv_write(s, Ptr{UInt8}(Base.eventloop()), UInt(0)) # zero write from a random pointer to flush current queue return end function buffer_writes(s::LibuvStream, bufsize) sendbuf = PipeBuffer(bufsize) iolock_begin() s.sendbuf = sendbuf iolock_end() return s end ## low-level calls to libuv ## function write(s::LibuvStream, b::UInt8) buf = s.sendbuf if buf !== nothing iolock_begin() if bytesavailable(buf) + 1 < buf.maxsize n = write(buf, b) iolock_end() return n end iolock_end() end return write(s, Ref{UInt8}(b)) end function uv_writecb_task(req::Ptr{Cvoid}, status::Cint) d = uv_req_data(req) if d != C_NULL uv_req_set_data(req, C_NULL) # let the Task know we got the writecb t = unsafe_pointer_to_objref(d)::Task schedule(t, status) else # no owner for this req, safe to just free it Libc.free(req) end nothing end function uv_shutdowncb_task(req::Ptr{Cvoid}, status::Cint) d = uv_req_data(req) if d != C_NULL uv_req_set_data(req, C_NULL) # let the Task know we got the shutdowncb t = unsafe_pointer_to_objref(d)::Task schedule(t, status) else # no owner for this req, safe to just free it Libc.free(req) end nothing end _fd(x::IOStream) = RawFD(fd(x)) _fd(x::Union{OS_HANDLE, RawFD}) = x function _fd(x::Union{LibuvStream, LibuvServer}) fd = Ref{OS_HANDLE}(INVALID_OS_HANDLE) if x.status != StatusUninit && x.status != StatusClosed && x.handle != C_NULL err = ccall(:uv_fileno, Int32, (Ptr{Cvoid}, Ptr{OS_HANDLE}), x.handle, fd) # handle errors by returning INVALID_OS_HANDLE end return fd[] end struct RedirectStdStream <: Function unix_fd::Int writable::Bool end for (f, writable, unix_fd) in ((:redirect_stdin, false, 0), (:redirect_stdout, true, 1), (:redirect_stderr, true, 2)) @eval const ($f) = RedirectStdStream($unix_fd, $writable) end function _redirect_io_libc(stream, unix_fd::Int) posix_fd = _fd(stream) @static if Sys.iswindows() if 0 <= unix_fd <= 2 ccall(:SetStdHandle, stdcall, Int32, (Int32, OS_HANDLE), -10 - unix_fd, Libc._get_osfhandle(posix_fd)) end end dup(posix_fd, RawFD(unix_fd)) nothing end function _redirect_io_global(io, unix_fd::Int) unix_fd == 0 && (global stdin = io) unix_fd == 1 && (global stdout = io) unix_fd == 2 && (global stderr = io) nothing end function (f::RedirectStdStream)(handle::Union{LibuvStream, IOStream}) _redirect_io_libc(handle, f.unix_fd) c_sym = f.unix_fd == 0 ? cglobal(:jl_uv_stdin, Ptr{Cvoid}) : f.unix_fd == 1 ? cglobal(:jl_uv_stdout, Ptr{Cvoid}) : f.unix_fd == 2 ? cglobal(:jl_uv_stderr, Ptr{Cvoid}) : C_NULL c_sym == C_NULL || unsafe_store!(c_sym, handle.handle) _redirect_io_global(handle, f.unix_fd) return handle end function (f::RedirectStdStream)(::DevNull) nulldev = @static Sys.iswindows() ? "NUL" : "/dev/null" handle = open(nulldev, write=f.writable) _redirect_io_libc(handle, f.unix_fd) close(handle) # handle has been dup'ed in _redirect_io_libc _redirect_io_global(devnull, f.unix_fd) return devnull end function (f::RedirectStdStream)(io::AbstractPipe) io2 = (f.writable ? pipe_writer : pipe_reader)(io) f(io2) _redirect_io_global(io, f.unix_fd) return io end function (f::RedirectStdStream)(p::Pipe) if p.in.status == StatusInit && p.out.status == StatusInit link_pipe!(p) end io2 = getfield(p, f.writable ? :in : :out) f(io2) return p end (f::RedirectStdStream)() = f(Pipe()) # Deprecate these in v2 (RedirectStdStream support) iterate(p::Pipe) = (p.out, 1) iterate(p::Pipe, i::Int) = i == 1 ? (p.in, 2) : nothing getindex(p::Pipe, key::Int) = key == 1 ? p.out : key == 2 ? p.in : throw(KeyError(key)) """ redirect_stdout([stream]) -> stream Create a pipe to which all C and Julia level [`stdout`](@ref) output will be redirected. Return a stream representing the pipe ends. Data written to [`stdout`](@ref) may now be read from the `rd` end of the pipe. !!! note `stream` must be a compatible objects, such as an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. See also [`redirect_stdio`](@ref). """ redirect_stdout """ redirect_stderr([stream]) -> stream Like [`redirect_stdout`](@ref), but for [`stderr`](@ref). !!! note `stream` must be a compatible objects, such as an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. See also [`redirect_stdio`](@ref). """ redirect_stderr """ redirect_stdin([stream]) -> stream Like [`redirect_stdout`](@ref), but for [`stdin`](@ref). Note that the direction of the stream is reversed. !!! note `stream` must be a compatible objects, such as an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. See also [`redirect_stdio`](@ref). """ redirect_stdin """ redirect_stdio(;stdin=stdin, stderr=stderr, stdout=stdout) Redirect a subset of the streams `stdin`, `stderr`, `stdout`. Each argument must be an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. !!! compat "Julia 1.7" `redirect_stdio` requires Julia 1.7 or later. """ function redirect_stdio(;stdin=nothing, stderr=nothing, stdout=nothing) stdin === nothing || redirect_stdin(stdin) stderr === nothing || redirect_stderr(stderr) stdout === nothing || redirect_stdout(stdout) end """ redirect_stdio(f; stdin=nothing, stderr=nothing, stdout=nothing) Redirect a subset of the streams `stdin`, `stderr`, `stdout`, call `f()` and restore each stream. Possible values for each stream are: * `nothing` indicating the stream should not be redirected. * `path::AbstractString` redirecting the stream to the file at `path`. * `io` an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. # Examples ```julia-repl julia> redirect_stdio(stdout="stdout.txt", stderr="stderr.txt") do print("hello stdout") print(stderr, "hello stderr") end julia> read("stdout.txt", String) "hello stdout" julia> read("stderr.txt", String) "hello stderr" ``` # Edge cases It is possible to pass the same argument to `stdout` and `stderr`: ```julia-repl julia> redirect_stdio(stdout="log.txt", stderr="log.txt", stdin=devnull) do ... end ``` However it is not supported to pass two distinct descriptors of the same file. ```julia-repl julia> io1 = open("same/path", "w") julia> io2 = open("same/path", "w") julia> redirect_stdio(f, stdout=io1, stderr=io2) # not supported ``` Also the `stdin` argument may not be the same descriptor as `stdout` or `stderr`. ```julia-repl julia> io = open(...) julia> redirect_stdio(f, stdout=io, stdin=io) # not supported ``` !!! compat "Julia 1.7" `redirect_stdio` requires Julia 1.7 or later. """ function redirect_stdio(f; stdin=nothing, stderr=nothing, stdout=nothing) function resolve(new::Nothing, oldstream, mode) (new=nothing, close=false, old=nothing) end function resolve(path::AbstractString, oldstream,mode) (new=open(path, mode), close=true, old=oldstream) end function resolve(new, oldstream, mode) (new=new, close=false, old=oldstream) end same_path(x, y) = false function same_path(x::AbstractString, y::AbstractString) # if x = y = "does_not_yet_exist.txt" then samefile will return false (abspath(x) == abspath(y)) || samefile(x,y) end if same_path(stderr, stdin) throw(ArgumentError("stdin and stderr cannot be the same path")) end if same_path(stdout, stdin) throw(ArgumentError("stdin and stdout cannot be the same path")) end new_in , close_in , old_in = resolve(stdin , Base.stdin , "r") new_out, close_out, old_out = resolve(stdout, Base.stdout, "w") if same_path(stderr, stdout) # make sure that in case stderr = stdout = "same/path" # only a single io is used instead of opening the same file twice new_err, close_err, old_err = new_out, false, Base.stderr else new_err, close_err, old_err = resolve(stderr, Base.stderr, "w") end redirect_stdio(; stderr=new_err, stdin=new_in, stdout=new_out) try return f() finally redirect_stdio(;stderr=old_err, stdin=old_in, stdout=old_out) close_err && close(new_err) close_in && close(new_in ) close_out && close(new_out) end end function (f::RedirectStdStream)(thunk::Function, stream) stdold = f.unix_fd == 0 ? stdin : f.unix_fd == 1 ? stdout : f.unix_fd == 2 ? stderr : throw(ArgumentError("Not implemented to get old handle of fd except for stdio")) f(stream) try return thunk() finally f(stdold) end end """ redirect_stdout(f::Function, stream) Run the function `f` while redirecting [`stdout`](@ref) to `stream`. Upon completion, [`stdout`](@ref) is restored to its prior setting. """ redirect_stdout(f::Function, stream) """ redirect_stderr(f::Function, stream) Run the function `f` while redirecting [`stderr`](@ref) to `stream`. Upon completion, [`stderr`](@ref) is restored to its prior setting. """ redirect_stderr(f::Function, stream) """ redirect_stdin(f::Function, stream) Run the function `f` while redirecting [`stdin`](@ref) to `stream`. Upon completion, [`stdin`](@ref) is restored to its prior setting. """ redirect_stdin(f::Function, stream) mark(x::LibuvStream) = mark(x.buffer) unmark(x::LibuvStream) = unmark(x.buffer) reset(x::LibuvStream) = reset(x.buffer) ismarked(x::LibuvStream) = ismarked(x.buffer) function peek(s::LibuvStream, ::Type{T}) where T mark(s) try read(s, T) finally reset(s) end end # BufferStream's are non-OS streams, backed by a regular IOBuffer mutable struct BufferStream <: LibuvStream buffer::IOBuffer cond::Threads.Condition readerror::Any buffer_writes::Bool lock::ReentrantLock # advisory lock status::Int BufferStream() = new(PipeBuffer(), Threads.Condition(), nothing, false, ReentrantLock(), StatusActive) end isopen(s::BufferStream) = s.status != StatusClosed closewrite(s::BufferStream) = close(s) function close(s::BufferStream) lock(s.cond) do s.status = StatusClosed notify(s.cond) nothing end end uvfinalize(s::BufferStream) = nothing setup_stdio(stream::BufferStream, child_readable::Bool) = invoke(setup_stdio, Tuple{IO, Bool}, stream, child_readable) function read(s::BufferStream, ::Type{UInt8}) nread = lock(s.cond) do wait_readnb(s, 1) read(s.buffer, UInt8) end return nread end function unsafe_read(s::BufferStream, a::Ptr{UInt8}, nb::UInt) lock(s.cond) do wait_readnb(s, Int(nb)) unsafe_read(s.buffer, a, nb) nothing end end bytesavailable(s::BufferStream) = bytesavailable(s.buffer) isreadable(s::BufferStream) = (isopen(s) || bytesavailable(s) > 0) && s.buffer.readable iswritable(s::BufferStream) = isopen(s) && s.buffer.writable function wait_readnb(s::BufferStream, nb::Int) lock(s.cond) do while isopen(s) && bytesavailable(s.buffer) < nb wait(s.cond) end end end show(io::IO, s::BufferStream) = print(io, "BufferStream(bytes waiting=", bytesavailable(s.buffer), ", isopen=", isopen(s), ")") function readuntil(s::BufferStream, c::UInt8; keep::Bool=false) bytes = lock(s.cond) do while isopen(s) && !occursin(c, s.buffer) wait(s.cond) end readuntil(s.buffer, c, keep=keep) end return bytes end function wait_close(s::BufferStream) lock(s.cond) do while isopen(s) wait(s.cond) end end end start_reading(s::BufferStream) = Int32(0) stop_reading(s::BufferStream) = nothing write(s::BufferStream, b::UInt8) = write(s, Ref{UInt8}(b)) function unsafe_write(s::BufferStream, p::Ptr{UInt8}, nb::UInt) nwrite = lock(s.cond) do rv = unsafe_write(s.buffer, p, nb) s.buffer_writes || notify(s.cond) rv end return nwrite end function eof(s::BufferStream) bytesavailable(s) > 0 && return false iseof = lock(s.cond) do wait_readnb(s, 1) return !isopen(s) && bytesavailable(s) <= 0 end return iseof end # If buffer_writes is called, it will delay notifying waiters till a flush is called. buffer_writes(s::BufferStream, bufsize=0) = (s.buffer_writes = true; s) function flush(s::BufferStream) lock(s.cond) do notify(s.cond) nothing end end skip(s::BufferStream, n) = skip(s.buffer, n) Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/filesystem.jl"# This file is a part of Julia. License is MIT: https://julialang.org/license ## File Operations (Libuv-based) ## module Filesystem const S_IFDIR = 0o040000 # directory const S_IFCHR = 0o020000 # character device const S_IFBLK = 0o060000 # block device const S_IFREG = 0o100000 # regular file const S_IFIFO = 0o010000 # fifo (named pipe) const S_IFLNK = 0o120000 # symbolic link const S_IFSOCK = 0o140000 # socket file const S_IFMT = 0o170000 const S_ISUID = 0o4000 # set UID bit const S_ISGID = 0o2000 # set GID bit const S_ENFMT = S_ISGID # file locking enforcement const S_ISVTX = 0o1000 # sticky bit const S_IRUSR = 0o0400 # read by owner const S_IWUSR = 0o0200 # write by owner const S_IXUSR = 0o0100 # execute by owner const S_IRWXU = 0o0700 # mask for owner permissions const S_IRGRP = 0o0040 # read by group const S_IWGRP = 0o0020 # write by group const S_IXGRP = 0o0010 # execute by group const S_IRWXG = 0o0070 # mask for group permissions const S_IROTH = 0o0004 # read by other const S_IWOTH = 0o0002 # write by other const S_IXOTH = 0o0001 # execute by other const S_IRWXO = 0o0007 # mask for other permissions export File, StatStruct, # open, futime, write, JL_O_WRONLY, JL_O_RDONLY, JL_O_RDWR, JL_O_APPEND, JL_O_CREAT, JL_O_EXCL, JL_O_TRUNC, JL_O_TEMPORARY, JL_O_SHORT_LIVED, JL_O_SEQUENTIAL, JL_O_RANDOM, JL_O_NOCTTY, JL_O_NOCTTY, JL_O_NONBLOCK, JL_O_NDELAY, JL_O_SYNC, JL_O_FSYNC, JL_O_ASYNC, JL_O_LARGEFILE, JL_O_DIRECTORY, JL_O_NOFOLLOW, JL_O_CLOEXEC, JL_O_DIRECT, JL_O_NOATIME, JL_O_PATH, JL_O_TMPFILE, JL_O_DSYNC, JL_O_RSYNC, S_IRUSR, S_IWUSR, S_IXUSR, S_IRWXU, S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXG, S_IROTH, S_IWOTH, S_IXOTH, S_IRWXO import .Base: IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen, bytesavailable, position, read, read!, readavailable, seek, seekend, show, skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error, setup_stdio, rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror, filesize import .Base.RefValue if Sys.iswindows() import .Base: cwstring end # Average buffer size including null terminator for several filesystem operations. # On Windows we use the MAX_PATH = 260 value on Win32. const AVG_PATH = Sys.iswindows() ? 260 : 512 # helper function to clean up libuv request uv_fs_req_cleanup(req) = ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) include("path.jl") include("stat.jl") include("file.jl") include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "file_constants.jl")) # include($BUILDROOT/base/file_constants.jl) ## Operations with File (fd) objects ## abstract type AbstractFile <: IO end mutable struct File <: AbstractFile open::Bool handle::OS_HANDLE File(fd::OS_HANDLE) = new(true, fd) end if OS_HANDLE !== RawFD File(fd::RawFD) = File(Libc._get_osfhandle(fd)) # TODO: calling close would now destroy the wrong handle end rawhandle(file::File) = file.handle setup_stdio(file::File, ::Bool) = (file, false) # Filesystem.open, not Base.open function open(path::AbstractString, flags::Integer, mode::Integer=0) req = Libc.malloc(_sizeof_uv_fs) local handle try ret = ccall(:uv_fs_open, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32, Int32, Ptr{Cvoid}), C_NULL, req, path, flags, mode, C_NULL) handle = ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req) uv_fs_req_cleanup(req) ret < 0 && uv_error("open($(repr(path)), $flags, $mode)", ret) finally # conversion to Cstring could cause an exception Libc.free(req) end return File(OS_HANDLE(@static Sys.iswindows() ? Ptr{Cvoid}(handle) : Cint(handle))) end isopen(f::File) = f.open function check_open(f::File) if !isopen(f) throw(ArgumentError("file is closed")) end end function close(f::File) if isopen(f) f.open = false err = ccall(:jl_fs_close, Int32, (OS_HANDLE,), f.handle) f.handle = INVALID_OS_HANDLE uv_error("close", err) end nothing end # sendfile is the most efficient way to copy from a file descriptor function sendfile(dst::File, src::File, src_offset::Int64, bytes::Int) check_open(dst) check_open(src) while true result = ccall(:jl_fs_sendfile, Int32, (OS_HANDLE, OS_HANDLE, Int64, Csize_t), src.handle, dst.handle, src_offset, bytes) uv_error("sendfile", result) nsent = result bytes -= nsent src_offset += nsent bytes <= 0 && break end nothing end function unsafe_write(f::File, buf::Ptr{UInt8}, len::UInt, offset::Int64=Int64(-1)) check_open(f) err = ccall(:jl_fs_write, Int32, (OS_HANDLE, Ptr{UInt8}, Csize_t, Int64), f.handle, buf, len, offset) uv_error("write", err) return len end write(f::File, c::UInt8) = write(f, Ref{UInt8}(c)) function truncate(f::File, n::Integer) check_open(f) req = Libc.malloc(_sizeof_uv_fs) err = ccall(:uv_fs_ftruncate, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Int64, Ptr{Cvoid}), C_NULL, req, f.handle, n, C_NULL) Libc.free(req) uv_error("ftruncate", err) return f end function futime(f::File, atime::Float64, mtime::Float64) check_open(f) req = Libc.malloc(_sizeof_uv_fs) err = ccall(:uv_fs_futime, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Float64, Float64, Ptr{Cvoid}), C_NULL, req, f.handle, atime, mtime, C_NULL) Libc.free(req) uv_error("futime", err) return f end function read(f::File, ::Type{UInt8}) check_open(f) ret = ccall(:jl_fs_read_byte, Int32, (OS_HANDLE,), f.handle) uv_error("read", ret) return ret % UInt8 end function read(f::File, ::Type{Char}) b0 = read(f, UInt8) l = 0x08 * (0x04 - UInt8(leading_ones(b0))) c = UInt32(b0) << 24 if l โ‰ค 0x10 s = 16 while s โ‰ฅ l && !eof(f) # this works around lack of peek(::File) p = position(f) b = read(f, UInt8) if b & 0xc0 != 0x80 seek(f, p) break end c |= UInt32(b) << s s -= 8 end end return reinterpret(Char, c) end read(f::File, ::Type{T}) where {T<:AbstractChar} = T(read(f, Char)) # fallback function unsafe_read(f::File, p::Ptr{UInt8}, nel::UInt) check_open(f) ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t), f.handle, p, nel) uv_error("read", ret) ret == nel || throw(EOFError()) nothing end bytesavailable(f::File) = max(0, filesize(f) - position(f)) # position can be > filesize eof(f::File) = bytesavailable(f) == 0 function readbytes!(f::File, b::Array{UInt8}, nb=length(b)) nr = min(nb, bytesavailable(f)) if length(b) < nr resize!(b, nr) end ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t), f.handle, b, nr) uv_error("read", ret) return ret end read(io::File) = read!(io, Base.StringVector(bytesavailable(io))) readavailable(io::File) = read(io) read(io::File, nb::Integer) = read!(io, Base.StringVector(min(nb, bytesavailable(io)))) const SEEK_SET = Int32(0) const SEEK_CUR = Int32(1) const SEEK_END = Int32(2) function seek(f::File, n::Integer) ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_SET) ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("seek") return f end function seekend(f::File) ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_END) ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("seekend") return f end function skip(f::File, n::Integer) ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_CUR) ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("skip") return f end function position(f::File) check_open(f) ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_CUR) ret == -1 && (@static Sys.iswindows() ? windowserror : systemerror)("lseek") return ret end fd(f::File) = f.handle stat(f::File) = stat(f.handle) function touch(f::File) @static if Sys.isunix() ret = ccall(:futimes, Cint, (Cint, Ptr{Cvoid}), fd(f), C_NULL) systemerror(:futimes, ret != 0) else t = time() futime(f, t, t) end f end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/path.jl.E# This file is a part of Julia. License is MIT: https://julialang.org/license export abspath, basename, dirname, expanduser, contractuser, homedir, isabspath, isdirpath, joinpath, normpath, realpath, relpath, splitdir, splitdrive, splitext, splitpath if Sys.isunix() const path_separator = "/" const path_separator_re = r"/+"sa const path_directory_re = r"(?:^|/)\.{0,2}$"sa const path_dir_splitter = r"^(.*?)(/+)([^/]*)$"sa const path_ext_splitter = r"^((?:.*/)?(?:\.|[^/\.])[^/]*?)(\.[^/\.]*|)$"sa splitdrive(path::String) = ("",path) elseif Sys.iswindows() const path_separator = "\\" const path_separator_re = r"[/\\]+"sa const path_absolute_re = r"^(?:[A-Za-z]+:)?[/\\]"sa const path_directory_re = r"(?:^|[/\\])\.{0,2}$"sa const path_dir_splitter = r"^(.*?)([/\\]+)([^/\\]*)$"sa const path_ext_splitter = r"^((?:.*[/\\])?(?:\.|[^/\\\.])[^/\\]*?)(\.[^/\\\.]*|)$"sa function splitdrive(path::String) m = match(r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$"sa, path)::AbstractMatch String(something(m.captures[1])), String(something(m.captures[2])) end else error("path primitives for this OS need to be defined") end """ splitdrive(path::AbstractString) -> (AbstractString, AbstractString) On Windows, split a path into the drive letter part and the path part. On Unix systems, the first component is always the empty string. """ splitdrive(path::AbstractString) """ homedir() -> String Return the current user's home directory. !!! note `homedir` determines the home directory via `libuv`'s `uv_os_homedir`. For details (for example on how to specify the home directory via environment variables), see the [`uv_os_homedir` documentation](http://docs.libuv.org/en/v1.x/misc.html#c.uv_os_homedir). """ function homedir() buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null while true rc = ccall(:uv_os_homedir, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) if rc == 0 resize!(buf, sz[]) return String(buf) elseif rc == Base.UV_ENOBUFS resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector else uv_error("homedir()", rc) end end end if Sys.iswindows() isabspath(path::AbstractString) = occursin(path_absolute_re, path) else isabspath(path::AbstractString) = startswith(path, '/') end """ isabspath(path::AbstractString) -> Bool Determine whether a path is absolute (begins at the root directory). # Examples ```jldoctest julia> isabspath("/home") true julia> isabspath("home") false ``` """ isabspath(path::AbstractString) """ isdirpath(path::AbstractString) -> Bool Determine whether a path refers to a directory (for example, ends with a path separator). # Examples ```jldoctest julia> isdirpath("/home") false julia> isdirpath("/home/") true ``` """ isdirpath(path::String) = occursin(path_directory_re, splitdrive(path)[2]) """ splitdir(path::AbstractString) -> (AbstractString, AbstractString) Split a path into a tuple of the directory name and file name. # Examples ```jldoctest julia> splitdir("/home/myuser") ("/home", "myuser") ``` """ function splitdir(path::String) a, b = splitdrive(path) _splitdir_nodrive(a,b) end # Common splitdir functionality without splitdrive, needed for splitpath. _splitdir_nodrive(path::String) = _splitdir_nodrive("", path) function _splitdir_nodrive(a::String, b::String) m = match(path_dir_splitter,b) m === nothing && return (a,b) cs = m.captures getcapture(cs, i) = cs[i]::AbstractString c1, c2, c3 = getcapture(cs, 1), getcapture(cs, 2), getcapture(cs, 3) a = string(a, isempty(c1) ? c2[1] : c1) a, String(c3) end """ dirname(path::AbstractString) -> String Get the directory part of a path. Trailing characters ('/' or '\\') in the path are counted as part of the path. # Examples ```jldoctest julia> dirname("/home/myuser") "/home" julia> dirname("/home/myuser/") "/home/myuser" ``` See also [`basename`](@ref). """ dirname(path::AbstractString) = splitdir(path)[1] """ basename(path::AbstractString) -> String Get the file name part of a path. !!! note This function differs slightly from the Unix `basename` program, where trailing slashes are ignored, i.e. `\$ basename /foo/bar/` returns `bar`, whereas `basename` in Julia returns an empty string `""`. # Examples ```jldoctest julia> basename("/home/myuser/example.jl") "example.jl" julia> basename("/home/myuser/") "" ``` See also [`dirname`](@ref). """ basename(path::AbstractString) = splitdir(path)[2] """ splitext(path::AbstractString) -> (String, String) If the last component of a path contains one or more dots, split the path into everything before the last dot and everything including and after the dot. Otherwise, return a tuple of the argument unmodified and the empty string. "splitext" is short for "split extension". # Examples ```jldoctest julia> splitext("/home/myuser/example.jl") ("/home/myuser/example", ".jl") julia> splitext("/home/myuser/example.tar.gz") ("/home/myuser/example.tar", ".gz") julia> splitext("/home/my.user/example") ("/home/my.user/example", "") ``` """ function splitext(path::String) a, b = splitdrive(path) m = match(path_ext_splitter, b) m === nothing && return (path,"") (a*something(m.captures[1])), String(something(m.captures[2])) end # NOTE: deprecated in 1.4 pathsep() = path_separator """ splitpath(path::AbstractString) -> Vector{String} Split a file path into all its path components. This is the opposite of `joinpath`. Returns an array of substrings, one for each directory or file in the path, including the root directory if present. !!! compat "Julia 1.1" This function requires at least Julia 1.1. # Examples ```jldoctest julia> splitpath("/home/myuser/example.jl") 4-element Vector{String}: "/" "home" "myuser" "example.jl" ``` """ splitpath(p::AbstractString) = splitpath(String(p)) function splitpath(p::String) drive, p = splitdrive(p) out = String[] isempty(p) && (pushfirst!(out,p)) # "" means the current directory. while !isempty(p) dir, base = _splitdir_nodrive(p) dir == p && (pushfirst!(out, dir); break) # Reached root node. if !isempty(base) # Skip trailing '/' in basename pushfirst!(out, base) end p = dir end if !isempty(drive) # Tack the drive back on to the first element. out[1] = drive*out[1] # Note that length(out) is always >= 1. end return out end if Sys.iswindows() function joinpath(paths::Union{Tuple, AbstractVector})::String assertstring(x) = x isa AbstractString || throw(ArgumentError("path component is not a string: $(repr(x))")) isempty(paths) && throw(ArgumentError("collection of path components must be non-empty")) assertstring(paths[1]) result_drive, result_path = splitdrive(paths[1]) p_path = "" for i in firstindex(paths)+1:lastindex(paths) assertstring(paths[i]) p_drive, p_path = splitdrive(paths[i]) if startswith(p_path, ('\\', '/')) # second path is absolute if !isempty(p_drive) || !isempty(result_drive) result_drive = p_drive end result_path = p_path continue elseif !isempty(p_drive) && p_drive != result_drive if lowercase(p_drive) != lowercase(result_drive) # different drives, ignore the first path entirely result_drive = p_drive result_path = p_path continue end end # second path is relative to the first if !isempty(result_path) && result_path[end] โˆ‰ ('\\', '/') result_path *= "\\" end result_path = result_path * p_path end # add separator between UNC and non-absolute path if !isempty(p_path) && result_path[1] โˆ‰ ('\\', '/') && !isempty(result_drive) && result_drive[end] != ':' return result_drive * "\\" * result_path end return result_drive * result_path end else function joinpath(paths::Union{Tuple, AbstractVector})::String assertstring(x) = x isa AbstractString || throw(ArgumentError("path component is not a string: $(repr(x))")) isempty(paths) && throw(ArgumentError("collection of path components must be non-empty")) assertstring(paths[1]) path = paths[1] for i in firstindex(paths)+1:lastindex(paths) p = paths[i] assertstring(p) if isabspath(p) path = p elseif isempty(path) || path[end] == '/' path *= p else path *= "/" * p end end return path end end # os-test joinpath(paths::AbstractString...)::String = joinpath(paths) """ joinpath(parts::AbstractString...) -> String joinpath(parts::Vector{AbstractString}) -> String joinpath(parts::Tuple{AbstractString}) -> String Join path components into a full path. If some argument is an absolute path or (on Windows) has a drive specification that doesn't match the drive computed for the join of the preceding paths, then prior components are dropped. Note on Windows since there is a current directory for each drive, `joinpath("c:", "foo")` represents a path relative to the current directory on drive "c:" so this is equal to "c:foo", not "c:\\foo". Furthermore, `joinpath` treats this as a non-absolute path and ignores the drive letter casing, hence `joinpath("C:\\A","c:b") = "C:\\A\\b"`. # Examples ```jldoctest julia> joinpath("/home/myuser", "example.jl") "/home/myuser/example.jl" ``` ```jldoctest julia> joinpath(["/home/myuser", "example.jl"]) "/home/myuser/example.jl" ``` """ joinpath """ normpath(path::AbstractString) -> String Normalize a path, removing "." and ".." entries and changing "/" to the canonical path separator for the system. # Examples ```jldoctest julia> normpath("/home/myuser/../example.jl") "/home/example.jl" julia> normpath("Documents/Julia") == joinpath("Documents", "Julia") true ``` """ function normpath(path::String) isabs = isabspath(path) isdir = isdirpath(path) drive, path = splitdrive(path) parts = split(path, path_separator_re; keepempty=false) filter!(!=("."), parts) while true clean = true for j = 1:length(parts)-1 if parts[j] != ".." && parts[j+1] == ".." deleteat!(parts, j:j+1) clean = false break end end clean && break end if isabs while !isempty(parts) && parts[1] == ".." popfirst!(parts) end elseif isempty(parts) push!(parts, ".") end path = join(parts, path_separator) if isabs path = path_separator*path end if isdir && !isdirpath(path) path *= path_separator end string(drive,path) end """ normpath(path::AbstractString, paths::AbstractString...) -> String Convert a set of paths to a normalized path by joining them together and removing "." and ".." entries. Equivalent to `normpath(joinpath(path, paths...))`. """ normpath(a::AbstractString, b::AbstractString...) = normpath(joinpath(a,b...)) """ abspath(path::AbstractString) -> String Convert a path to an absolute path by adding the current directory if necessary. Also normalizes the path as in [`normpath`](@ref). # Example If you are in a directory called `JuliaExample` and the data you are using is two levels up relative to the `JuliaExample` directory, you could write: abspath("../../data") Which gives a path like `"/home/JuliaUser/data/"`. See also [`joinpath`](@ref), [`pwd`](@ref), [`expanduser`](@ref). """ function abspath(a::String)::String if !isabspath(a) cwd = pwd() a_drive, a_nodrive = splitdrive(a) if a_drive != "" && lowercase(splitdrive(cwd)[1]) != lowercase(a_drive) cwd = a_drive * path_separator a = joinpath(cwd, a_nodrive) else a = joinpath(cwd, a) end end return normpath(a) end """ abspath(path::AbstractString, paths::AbstractString...) -> String Convert a set of paths to an absolute path by joining them together and adding the current directory if necessary. Equivalent to `abspath(joinpath(path, paths...))`. """ abspath(a::AbstractString, b::AbstractString...) = abspath(joinpath(a,b...)) if Sys.iswindows() function longpath(path::AbstractString) p = cwstring(path) buf = zeros(UInt16, length(p)) while true n = ccall((:GetLongPathNameW, "kernel32"), stdcall, UInt32, (Ptr{UInt16}, Ptr{UInt16}, UInt32), p, buf, length(buf)) windowserror(:longpath, n == 0) x = n < length(buf) # is the buffer big enough? resize!(buf, n) # shrink if x, grow if !x x && return transcode(String, buf) end end end # os-test """ realpath(path::AbstractString) -> String Canonicalize a path by expanding symbolic links and removing "." and ".." entries. On case-insensitive case-preserving filesystems (typically Mac and Windows), the filesystem's stored case for the path is returned. (This function throws an exception if `path` does not exist in the filesystem.) """ function realpath(path::AbstractString) req = Libc.malloc(_sizeof_uv_fs) try ret = ccall(:uv_fs_realpath, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL) if ret < 0 uv_fs_req_cleanup(req) uv_error("realpath($(repr(path)))", ret) end path = unsafe_string(ccall(:jl_uv_fs_t_ptr, Cstring, (Ptr{Cvoid},), req)) uv_fs_req_cleanup(req) return path finally Libc.free(req) end end if Sys.iswindows() # on windows, ~ means "temporary file" expanduser(path::AbstractString) = path contractuser(path::AbstractString) = path else function expanduser(path::AbstractString) y = iterate(path) y === nothing && return path c, i = y::Tuple{eltype(path),Int} c != '~' && return path y = iterate(path, i) y === nothing && return homedir() y[1]::eltype(path) == '/' && return homedir() * path[i:end] throw(ArgumentError("~user tilde expansion not yet implemented")) end function contractuser(path::AbstractString) home = homedir() if path == home return "~" elseif startswith(path, home) return joinpath("~", relpath(path, home)) else return path end end end """ expanduser(path::AbstractString) -> AbstractString On Unix systems, replace a tilde character at the start of a path with the current user's home directory. See also: [`contractuser`](@ref). """ expanduser(path::AbstractString) """ contractuser(path::AbstractString) -> AbstractString On Unix systems, if the path starts with `homedir()`, replace it with a tilde character. See also: [`expanduser`](@ref). """ contractuser(path::AbstractString) """ relpath(path::AbstractString, startpath::AbstractString = ".") -> String Return a relative filepath to `path` either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of `path` or `startpath`. On Windows, case sensitivity is applied to every part of the path except drive letters. If `path` and `startpath` refer to different drives, the absolute path of `path` is returned. """ function relpath(path::String, startpath::String = ".") isempty(path) && throw(ArgumentError("`path` must be non-empty")) isempty(startpath) && throw(ArgumentError("`startpath` must be non-empty")) curdir = "." pardir = ".." path == startpath && return curdir if Sys.iswindows() path_drive, path_without_drive = splitdrive(path) startpath_drive, startpath_without_drive = splitdrive(startpath) isempty(startpath_drive) && (startpath_drive = path_drive) # by default assume same as path drive uppercase(path_drive) == uppercase(startpath_drive) || return abspath(path) # if drives differ return first path path_arr = split(abspath(path_drive * path_without_drive), path_separator_re) start_arr = split(abspath(path_drive * startpath_without_drive), path_separator_re) else path_arr = split(abspath(path), path_separator_re) start_arr = split(abspath(startpath), path_separator_re) end i = 0 while i < min(length(path_arr), length(start_arr)) i += 1 if path_arr[i] != start_arr[i] i -= 1 break end end pathpart = join(path_arr[i+1:something(findlast(x -> !isempty(x), path_arr), 0)], path_separator) prefix_num = something(findlast(x -> !isempty(x), start_arr), 0) - i - 1 if prefix_num >= 0 prefix = pardir * path_separator relpath_ = isempty(pathpart) ? (prefix^prefix_num) * pardir : (prefix^prefix_num) * pardir * path_separator * pathpart else relpath_ = pathpart end return isempty(relpath_) ? curdir : relpath_ end relpath(path::AbstractString, startpath::AbstractString) = relpath(String(path), String(startpath)) for f in (:isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath) @eval $f(path::AbstractString) = $f(String(path)) end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/stat.jl]3# This file is a part of Julia. License is MIT: https://julialang.org/license # filesystem operations export ctime, filemode, filesize, gperm, isblockdev, ischardev, isdir, isfifo, isfile, islink, ismount, ispath, issetgid, issetuid, issocket, issticky, lstat, mtime, operm, stat, uperm struct StatStruct desc :: Union{String, OS_HANDLE} # for show method, not included in equality or hash device :: UInt inode :: UInt mode :: UInt nlink :: Int uid :: UInt gid :: UInt rdev :: UInt size :: Int64 blksize :: Int64 blocks :: Int64 mtime :: Float64 ctime :: Float64 end @eval function Base.:(==)(x::StatStruct, y::StatStruct) # do not include `desc` in equality or hash $(let ex = true for fld in fieldnames(StatStruct)[2:end] ex = :(getfield(x, $(QuoteNode(fld))) === getfield(y, $(QuoteNode(fld))) && $ex) end Expr(:return, ex) end) end @eval function Base.hash(obj::StatStruct, h::UInt) $(quote $(Any[:(h = hash(getfield(obj, $(QuoteNode(fld))), h)) for fld in fieldnames(StatStruct)[2:end]]...) return h end) end StatStruct() = StatStruct("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) StatStruct(buf::Union{Vector{UInt8},Ptr{UInt8}}) = StatStruct("", buf) StatStruct(desc::Union{AbstractString, OS_HANDLE}, buf::Union{Vector{UInt8},Ptr{UInt8}}) = StatStruct( desc isa OS_HANDLE ? desc : String(desc), ccall(:jl_stat_dev, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_ino, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_mode, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_nlink, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_uid, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_gid, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_rdev, UInt32, (Ptr{UInt8},), buf), ccall(:jl_stat_size, UInt64, (Ptr{UInt8},), buf), ccall(:jl_stat_blksize, UInt64, (Ptr{UInt8},), buf), ccall(:jl_stat_blocks, UInt64, (Ptr{UInt8},), buf), ccall(:jl_stat_mtime, Float64, (Ptr{UInt8},), buf), ccall(:jl_stat_ctime, Float64, (Ptr{UInt8},), buf), ) function iso_datetime_with_relative(t, tnow) str = Libc.strftime("%FT%T%z", t) secdiff = t - tnow for (d, name) in ((24*60*60, "day"), (60*60, "hour"), (60, "minute"), (1, "second")) tdiff = round(Int, div(abs(secdiff), d)) if tdiff != 0 # find first unit difference plural = tdiff == 1 ? "" : "s" when = secdiff < 0 ? "ago" : "in the future" return "$str ($tdiff $name$plural $when)" end end return "$str (just now)" end function getusername(uid::Unsigned) pwd = Libc.getpwuid(uid, false) pwd === nothing && return isempty(pwd.username) && return return pwd.username end function getgroupname(gid::Unsigned) gp = Libc.getgrgid(gid, false) gp === nothing && return isempty(gp.groupname) && return return gp.groupname end function show_statstruct(io::IO, st::StatStruct, oneline::Bool) print(io, oneline ? "StatStruct(" : "StatStruct for ") show(io, st.desc) oneline || print(io, "\n ") print(io, " size: ", st.size, " bytes") oneline || print(io, "\n") print(io, " device: ", st.device) oneline || print(io, "\n ") print(io, " inode: ", st.inode) oneline || print(io, "\n ") print(io, " mode: 0o", string(filemode(st), base = 8, pad = 6), " (", filemode_string(st), ")") oneline || print(io, "\n ") print(io, " nlink: ", st.nlink) oneline || print(io, "\n ") print(io, " uid: $(st.uid)") username = getusername(st.uid) username === nothing || print(io, " (", username, ")") oneline || print(io, "\n ") print(io, " gid: ", st.gid) groupname = getgroupname(st.gid) groupname === nothing || print(io, " (", groupname, ")") oneline || print(io, "\n ") print(io, " rdev: ", st.rdev) oneline || print(io, "\n ") print(io, " blksz: ", st.blksize) oneline || print(io, "\n") print(io, " blocks: ", st.blocks) tnow = round(UInt, time()) oneline || print(io, "\n ") print(io, " mtime: ", iso_datetime_with_relative(st.mtime, tnow)) oneline || print(io, "\n ") print(io, " ctime: ", iso_datetime_with_relative(st.ctime, tnow)) oneline && print(io, ")") return nothing end show(io::IO, st::StatStruct) = show_statstruct(io, st, true) show(io::IO, ::MIME"text/plain", st::StatStruct) = show_statstruct(io, st, false) # stat & lstat functions macro stat_call(sym, arg1type, arg) return quote stat_buf = zeros(UInt8, Int(ccall(:jl_sizeof_stat, Int32, ()))) r = ccall($(Expr(:quote, sym)), Int32, ($(esc(arg1type)), Ptr{UInt8}), $(esc(arg)), stat_buf) if !(r in (0, Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL)) uv_error(string("stat(", repr($(esc(arg))), ")"), r) end st = StatStruct($(esc(arg)), stat_buf) if ispath(st) != (r == 0) error("stat returned zero type for a valid path") end return st end end stat(fd::OS_HANDLE) = @stat_call jl_fstat OS_HANDLE fd stat(path::AbstractString) = @stat_call jl_stat Cstring path lstat(path::AbstractString) = @stat_call jl_lstat Cstring path if RawFD !== OS_HANDLE global stat(fd::RawFD) = stat(Libc._get_osfhandle(fd)) end stat(fd::Integer) = stat(RawFD(fd)) """ stat(file) Return a structure whose fields contain information about the file. The fields of the structure are: | Name | Description | |:--------|:-------------------------------------------------------------------| | desc | The path or OS file descriptor | | size | The size (in bytes) of the file | | device | ID of the device that contains the file | | inode | The inode number of the file | | mode | The protection mode of the file | | nlink | The number of hard links to the file | | uid | The user id of the owner of the file | | gid | The group id of the file owner | | rdev | If this file refers to a device, the ID of the device it refers to | | blksize | The file-system preferred block size for the file | | blocks | The number of such blocks allocated | | mtime | Unix timestamp of when the file was last modified | | ctime | Unix timestamp of when the file's metadata was changed | """ stat(path...) = stat(joinpath(path...)) """ lstat(file) Like [`stat`](@ref), but for symbolic links gets the info for the link itself rather than the file it refers to. This function must be called on a file path rather than a file object or a file descriptor. """ lstat(path...) = lstat(joinpath(path...)) # some convenience functions const filemode_table = ( [ (S_IFLNK, "l"), (S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR (S_IFREG, "-"), (S_IFBLK, "b"), (S_IFDIR, "d"), (S_IFCHR, "c"), (S_IFIFO, "p") ], [ (S_IRUSR, "r"), ], [ (S_IWUSR, "w"), ], [ (S_IXUSR|S_ISUID, "s"), (S_ISUID, "S"), (S_IXUSR, "x") ], [ (S_IRGRP, "r"), ], [ (S_IWGRP, "w"), ], [ (S_IXGRP|S_ISGID, "s"), (S_ISGID, "S"), (S_IXGRP, "x") ], [ (S_IROTH, "r"), ], [ (S_IWOTH, "w"), ], [ (S_IXOTH|S_ISVTX, "t"), (S_ISVTX, "T"), (S_IXOTH, "x") ] ) """ filemode(file) Equivalent to `stat(file).mode`. """ filemode(st::StatStruct) = st.mode filemode_string(st::StatStruct) = filemode_string(st.mode) function filemode_string(mode) str = IOBuffer() for table in filemode_table complete = true for (bit, char) in table if mode & bit == bit write(str, char) complete = false break end end complete && write(str, "-") end return String(take!(str)) end """ filesize(path...) Equivalent to `stat(file).size`. """ filesize(st::StatStruct) = st.size """ mtime(file) Equivalent to `stat(file).mtime`. """ mtime(st::StatStruct) = st.mtime """ ctime(file) Equivalent to `stat(file).ctime`. """ ctime(st::StatStruct) = st.ctime # mode type predicates """ ispath(path) -> Bool Return `true` if a valid filesystem entity exists at `path`, otherwise returns `false`. This is the generalization of [`isfile`](@ref), [`isdir`](@ref) etc. """ ispath(st::StatStruct) = filemode(st) & 0xf000 != 0x0000 """ isfifo(path) -> Bool Return `true` if `path` is a FIFO, `false` otherwise. """ isfifo(st::StatStruct) = filemode(st) & 0xf000 == 0x1000 """ ischardev(path) -> Bool Return `true` if `path` is a character device, `false` otherwise. """ ischardev(st::StatStruct) = filemode(st) & 0xf000 == 0x2000 """ isdir(path) -> Bool Return `true` if `path` is a directory, `false` otherwise. # Examples ```jldoctest julia> isdir(homedir()) true julia> isdir("not/a/directory") false ``` See also [`isfile`](@ref) and [`ispath`](@ref). """ isdir(st::StatStruct) = filemode(st) & 0xf000 == 0x4000 """ isblockdev(path) -> Bool Return `true` if `path` is a block device, `false` otherwise. """ isblockdev(st::StatStruct) = filemode(st) & 0xf000 == 0x6000 """ isfile(path) -> Bool Return `true` if `path` is a regular file, `false` otherwise. # Examples ```jldoctest julia> isfile(homedir()) false julia> filename = "test_file.txt"; julia> write(filename, "Hello world!"); julia> isfile(filename) true julia> rm(filename); julia> isfile(filename) false ``` See also [`isdir`](@ref) and [`ispath`](@ref). """ isfile(st::StatStruct) = filemode(st) & 0xf000 == 0x8000 """ islink(path) -> Bool Return `true` if `path` is a symbolic link, `false` otherwise. """ islink(st::StatStruct) = filemode(st) & 0xf000 == 0xa000 """ issocket(path) -> Bool Return `true` if `path` is a socket, `false` otherwise. """ issocket(st::StatStruct) = filemode(st) & 0xf000 == 0xc000 # mode permission predicates """ issetuid(path) -> Bool Return `true` if `path` has the setuid flag set, `false` otherwise. """ issetuid(st::StatStruct) = (filemode(st) & 0o4000) > 0 """ issetgid(path) -> Bool Return `true` if `path` has the setgid flag set, `false` otherwise. """ issetgid(st::StatStruct) = (filemode(st) & 0o2000) > 0 """ issticky(path) -> Bool Return `true` if `path` has the sticky bit set, `false` otherwise. """ issticky(st::StatStruct) = (filemode(st) & 0o1000) > 0 """ uperm(file) Get the permissions of the owner of the file as a bitfield of | Value | Description | |:------|:-------------------| | 01 | Execute Permission | | 02 | Write Permission | | 04 | Read Permission | For allowed arguments, see [`stat`](@ref). """ uperm(st::StatStruct) = UInt8((filemode(st) >> 6) & 0x7) """ gperm(file) Like [`uperm`](@ref) but gets the permissions of the group owning the file. """ gperm(st::StatStruct) = UInt8((filemode(st) >> 3) & 0x7) """ operm(file) Like [`uperm`](@ref) but gets the permissions for people who neither own the file nor are a member of the group owning the file """ operm(st::StatStruct) = UInt8((filemode(st) ) & 0x7) # mode predicate methods for file names for f in Symbol[ :ispath, :isfifo, :ischardev, :isdir, :isblockdev, :isfile, :issocket, :issetuid, :issetgid, :issticky, :uperm, :gperm, :operm, :filemode, :filesize, :mtime, :ctime, ] @eval ($f)(path...) = ($f)(stat(path...)) end islink(path...) = islink(lstat(path...)) # samefile can be used for files and directories: #11145#issuecomment-99511194 function samefile(a::StatStruct, b::StatStruct) ispath(a) && ispath(b) && a.device == b.device && a.inode == b.inode end """ samefile(path_a::AbstractString, path_b::AbstractString) Check if the paths `path_a` and `path_b` refer to the same existing file or directory. """ samefile(a::AbstractString, b::AbstractString) = samefile(stat(a), stat(b)) """ ismount(path) -> Bool Return `true` if `path` is a mount point, `false` otherwise. """ function ismount(path...) path = joinpath(path...) isdir(path) || return false s1 = lstat(path) # Symbolic links cannot be mount points islink(s1) && return false parent_path = joinpath(path, "..") s2 = lstat(parent_path) # If a directory and its parent are on different devices, then the # directory must be a mount point (s1.device != s2.device) && return true (s1.inode == s2.inode) && return true false end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/file.jlฝ•# This file is a part of Julia. License is MIT: https://julialang.org/license # Operations with the file system (paths) ## export cd, chmod, chown, cp, cptree, diskstat, hardlink, mkdir, mkpath, mktemp, mktempdir, mv, pwd, rename, readlink, readdir, rm, samefile, sendfile, symlink, tempdir, tempname, touch, unlink, walkdir # get and set current directory """ pwd() -> String Get the current working directory. See also: [`cd`](@ref), [`tempdir`](@ref). # Examples ```julia-repl julia> pwd() "/home/JuliaUser" julia> cd("/home/JuliaUser/Projects/julia") julia> pwd() "/home/JuliaUser/Projects/julia" ``` """ function pwd() buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null while true rc = ccall(:uv_cwd, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) if rc == 0 resize!(buf, sz[]) return String(buf) elseif rc == Base.UV_ENOBUFS resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector else uv_error("pwd()", rc) end end end """ cd(dir::AbstractString=homedir()) Set the current working directory. See also: [`pwd`](@ref), [`mkdir`](@ref), [`mkpath`](@ref), [`mktempdir`](@ref). # Examples ```julia-repl julia> cd("/home/JuliaUser/Projects/julia") julia> pwd() "/home/JuliaUser/Projects/julia" julia> cd() julia> pwd() "/home/JuliaUser" ``` """ function cd(dir::AbstractString) err = ccall(:uv_chdir, Cint, (Cstring,), dir) err < 0 && uv_error("cd($(repr(dir)))", err) return nothing end cd() = cd(homedir()) if Sys.iswindows() function cd(f::Function, dir::AbstractString) old = pwd() try cd(dir) f() finally cd(old) end end else function cd(f::Function, dir::AbstractString) fd = ccall(:open, Int32, (Cstring, Int32, UInt32...), :., 0) systemerror(:open, fd == -1) try cd(dir) f() finally systemerror(:fchdir, ccall(:fchdir, Int32, (Int32,), fd) != 0) systemerror(:close, ccall(:close, Int32, (Int32,), fd) != 0) end end end """ cd(f::Function, dir::AbstractString=homedir()) Temporarily change the current working directory to `dir`, apply function `f` and finally return to the original directory. # Examples ```julia-repl julia> pwd() "/home/JuliaUser" julia> cd(readdir, "/home/JuliaUser/Projects/julia") 34-element Array{String,1}: ".circleci" ".freebsdci.sh" ".git" ".gitattributes" ".github" โ‹ฎ "test" "ui" "usr" "usr-staging" julia> pwd() "/home/JuliaUser" ``` """ cd(f::Function) = cd(f, homedir()) function checkmode(mode::Integer) if !(0 <= mode <= 511) throw(ArgumentError("Mode must be between 0 and 511 = 0o777")) end mode end """ mkdir(path::AbstractString; mode::Unsigned = 0o777) Make a new directory with name `path` and permissions `mode`. `mode` defaults to `0o777`, modified by the current file creation mask. This function never creates more than one directory. If the directory already exists, or some intermediate directories do not exist, this function throws an error. See [`mkpath`](@ref) for a function which creates all required intermediate directories. Return `path`. # Examples ```julia-repl julia> mkdir("testingdir") "testingdir" julia> cd("testingdir") julia> pwd() "/home/JuliaUser/testingdir" ``` """ function mkdir(path::AbstractString; mode::Integer = 0o777) req = Libc.malloc(_sizeof_uv_fs) try ret = ccall(:uv_fs_mkdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Cint, Ptr{Cvoid}), C_NULL, req, path, checkmode(mode), C_NULL) if ret < 0 uv_fs_req_cleanup(req) uv_error("mkdir($(repr(path)); mode=0o$(string(mode,base=8)))", ret) end uv_fs_req_cleanup(req) return path finally Libc.free(req) end end """ mkpath(path::AbstractString; mode::Unsigned = 0o777) Create all intermediate directories in the `path` as required. Directories are created with the permissions `mode` which defaults to `0o777` and is modified by the current file creation mask. Unlike [`mkdir`](@ref), `mkpath` does not error if `path` (or parts of it) already exists. However, an error will be thrown if `path` (or parts of it) points to an existing file. Return `path`. If `path` includes a filename you will probably want to use `mkpath(dirname(path))` to avoid creating a directory using the filename. # Examples ```julia-repl julia> cd(mktempdir()) julia> mkpath("my/test/dir") # creates three directories "my/test/dir" julia> readdir() 1-element Array{String,1}: "my" julia> cd("my") julia> readdir() 1-element Array{String,1}: "test" julia> readdir("test") 1-element Array{String,1}: "dir" julia> mkpath("intermediate_dir/actually_a_directory.txt") # creates two directories "intermediate_dir/actually_a_directory.txt" julia> isdir("intermediate_dir/actually_a_directory.txt") true ``` """ function mkpath(path::AbstractString; mode::Integer = 0o777) isdirpath(path) && (path = dirname(path)) dir = dirname(path) (path == dir || isdir(path)) && return path mkpath(dir, mode = checkmode(mode)) try mkdir(path, mode = mode) catch err # If there is a problem with making the directory, but the directory # does in fact exist, then ignore the error. Else re-throw it. if !isa(err, IOError) || !isdir(path) rethrow() end end path end """ rm(path::AbstractString; force::Bool=false, recursive::Bool=false) Delete the file, link, or empty directory at the given path. If `force=true` is passed, a non-existing path is not treated as error. If `recursive=true` is passed and the path is a directory, then all contents are removed recursively. # Examples ```jldoctest julia> mkpath("my/test/dir"); julia> rm("my", recursive=true) julia> rm("this_file_does_not_exist", force=true) julia> rm("this_file_does_not_exist") ERROR: IOError: unlink("this_file_does_not_exist"): no such file or directory (ENOENT) Stacktrace: [...] ``` """ function rm(path::AbstractString; force::Bool=false, recursive::Bool=false) if islink(path) || !isdir(path) try @static if Sys.iswindows() # is writable on windows actually means "is deletable" st = lstat(path) if ispath(st) && (filemode(st) & 0o222) == 0 chmod(path, 0o777) end end unlink(path) catch err if force && isa(err, IOError) && err.code==Base.UV_ENOENT return end rethrow() end else if recursive try for p in readdir(path) rm(joinpath(path, p), force=force, recursive=true) end catch err if !(isa(err, IOError) && err.code==Base.UV_EACCES) rethrow(err) end end end req = Libc.malloc(_sizeof_uv_fs) try ret = ccall(:uv_fs_rmdir, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL) uv_fs_req_cleanup(req) if ret < 0 && !(force && ret == Base.UV_ENOENT) uv_error("rm($(repr(path)))", ret) end nothing finally Libc.free(req) end end end # The following use Unix command line facilities function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::AbstractString; force::Bool=false) if ispath(dst) if force # Check for issue when: (src == dst) or when one is a link to the other # https://github.com/JuliaLang/julia/pull/11172#issuecomment-100391076 if Base.samefile(src, dst) abs_src = islink(src) ? abspath(readlink(src)) : abspath(src) abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst) throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir. ", "This is not supported.\n ", "`src` refers to: $(abs_src)\n ", "`dst` refers to: $(abs_dst)\n"))) end rm(dst; recursive=true, force=true) else throw(ArgumentError(string("'$dst' exists. `force=true` ", "is required to remove '$dst' before $(txt)."))) end end end function cptree(src::String, dst::String; force::Bool=false, follow_symlinks::Bool=false) isdir(src) || throw(ArgumentError("'$src' is not a directory. Use `cp(src, dst)`")) checkfor_mv_cp_cptree(src, dst, "copying"; force=force) mkdir(dst) for name in readdir(src) srcname = joinpath(src, name) if !follow_symlinks && islink(srcname) symlink(readlink(srcname), joinpath(dst, name)) elseif isdir(srcname) cptree(srcname, joinpath(dst, name); force=force, follow_symlinks=follow_symlinks) else sendfile(srcname, joinpath(dst, name)) end end end cptree(src::AbstractString, dst::AbstractString; kwargs...) = cptree(String(src)::String, String(dst)::String; kwargs...) """ cp(src::AbstractString, dst::AbstractString; force::Bool=false, follow_symlinks::Bool=false) Copy the file, link, or directory from `src` to `dst`. `force=true` will first remove an existing `dst`. If `follow_symlinks=false`, and `src` is a symbolic link, `dst` will be created as a symbolic link. If `follow_symlinks=true` and `src` is a symbolic link, `dst` will be a copy of the file or directory `src` refers to. Return `dst`. !!! note The `cp` function is different from the `cp` command. The `cp` function always operates on the assumption that `dst` is a file, while the command does different things depending on whether `dst` is a directory or a file. Using `force=true` when `dst` is a directory will result in loss of all the contents present in the `dst` directory, and `dst` will become a file that has the contents of `src` instead. """ function cp(src::AbstractString, dst::AbstractString; force::Bool=false, follow_symlinks::Bool=false) checkfor_mv_cp_cptree(src, dst, "copying"; force=force) if !follow_symlinks && islink(src) symlink(readlink(src), dst) elseif isdir(src) cptree(src, dst; force=force, follow_symlinks=follow_symlinks) else sendfile(src, dst) end dst end """ mv(src::AbstractString, dst::AbstractString; force::Bool=false) Move the file, link, or directory from `src` to `dst`. `force=true` will first remove an existing `dst`. Return `dst`. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> write("hello.txt", "world"); julia> mv("hello.txt", "goodbye.txt") "goodbye.txt" julia> "hello.txt" in readdir() false julia> readline("goodbye.txt") "world" julia> write("hello.txt", "world2"); julia> mv("hello.txt", "goodbye.txt") ERROR: ArgumentError: 'goodbye.txt' exists. `force=true` is required to remove 'goodbye.txt' before moving. Stacktrace: [1] #checkfor_mv_cp_cptree#10(::Bool, ::Function, ::String, ::String, ::String) at ./file.jl:293 [...] julia> mv("hello.txt", "goodbye.txt", force=true) "goodbye.txt" julia> rm("goodbye.txt"); ``` """ function mv(src::AbstractString, dst::AbstractString; force::Bool=false) checkfor_mv_cp_cptree(src, dst, "moving"; force=force) rename(src, dst) dst end """ touch(path::AbstractString) touch(fd::File) Update the last-modified timestamp on a file to the current time. If the file does not exist a new file is created. Return `path`. # Examples ```julia-repl julia> write("my_little_file", 2); julia> mtime("my_little_file") 1.5273815391135583e9 julia> touch("my_little_file"); julia> mtime("my_little_file") 1.527381559163435e9 ``` We can see the [`mtime`](@ref) has been modified by `touch`. """ function touch(path::AbstractString) f = open(path, JL_O_WRONLY | JL_O_CREAT, 0o0666) try touch(f) finally close(f) end path end """ tempdir() Gets the path of the temporary directory. On Windows, `tempdir()` uses the first environment variable found in the ordered list `TMP`, `TEMP`, `USERPROFILE`. On all other operating systems, `tempdir()` uses the first environment variable found in the ordered list `TMPDIR`, `TMP`, `TEMP`, and `TEMPDIR`. If none of these are found, the path `"/tmp"` is used. """ function tempdir() buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null while true rc = ccall(:uv_os_tmpdir, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) if rc == 0 resize!(buf, sz[]) return String(buf) elseif rc == Base.UV_ENOBUFS resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector else uv_error("tempdir()", rc) end end end """ prepare_for_deletion(path::AbstractString) Prepares the given `path` for deletion by ensuring that all directories within that `path` have write permissions, so that files can be removed from them. This is automatically invoked by methods such as `mktempdir()` to ensure that no matter what weird permissions a user may have created directories with within the temporary prefix, it will always be deleted. """ function prepare_for_deletion(path::AbstractString) # Nothing to do for non-directories if !isdir(path) return end try chmod(path, filemode(path) | 0o333) catch; end for (root, dirs, files) in walkdir(path; onerror=x->()) for dir in dirs dpath = joinpath(root, dir) try chmod(dpath, filemode(dpath) | 0o333) catch; end end end end const TEMP_CLEANUP_MIN = Ref(1024) const TEMP_CLEANUP_MAX = Ref(1024) const TEMP_CLEANUP = Dict{String,Bool}() const TEMP_CLEANUP_LOCK = ReentrantLock() function temp_cleanup_later(path::AbstractString; asap::Bool=false) lock(TEMP_CLEANUP_LOCK) # each path should only be inserted here once, but if there # is a collision, let !asap win over asap: if any user might # still be using the path, don't delete it until process exit TEMP_CLEANUP[path] = get(TEMP_CLEANUP, path, true) & asap if length(TEMP_CLEANUP) > TEMP_CLEANUP_MAX[] temp_cleanup_purge() TEMP_CLEANUP_MAX[] = max(TEMP_CLEANUP_MIN[], 2*length(TEMP_CLEANUP)) end unlock(TEMP_CLEANUP_LOCK) return nothing end function temp_cleanup_purge(; force::Bool=false) need_gc = Sys.iswindows() for (path, asap) in TEMP_CLEANUP try if (force || asap) && ispath(path) need_gc && GC.gc(true) need_gc = false prepare_for_deletion(path) rm(path, recursive=true, force=true) end !ispath(path) && delete!(TEMP_CLEANUP, path) catch ex @warn """ Failed to clean up temporary path $(repr(path)) $ex """ _group=:file end end end const temp_prefix = "jl_" # Use `Libc.rand()` to generate random strings function _rand_filename(len = 10) slug = Base.StringVector(len) chars = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" for i = 1:len slug[i] = chars[(Libc.rand() % length(chars)) + 1] end return String(slug) end # Obtain a temporary filename. function tempname(parent::AbstractString=tempdir(); max_tries::Int = 100, cleanup::Bool=true) isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) prefix = joinpath(parent, temp_prefix) filename = nothing for i in 1:max_tries filename = string(prefix, _rand_filename()) if ispath(filename) filename = nothing else break end end if filename === nothing error("tempname: max_tries exhausted") end cleanup && temp_cleanup_later(filename) return filename end if Sys.iswindows() # While this isn't a true analog of `mkstemp`, it _does_ create an # empty file for us, ensuring that other simultaneous calls to # `_win_mkstemp()` won't collide, so it's a better name for the # function than `tempname()`. function _win_mkstemp(temppath::AbstractString) tempp = cwstring(temppath) temppfx = cwstring(temp_prefix) tname = Vector{UInt16}(undef, 32767) uunique = ccall(:GetTempFileNameW, stdcall, UInt32, (Ptr{UInt16}, Ptr{UInt16}, UInt32, Ptr{UInt16}), tempp, temppfx, UInt32(0), tname) windowserror("GetTempFileName", uunique == 0) lentname = something(findfirst(iszero, tname)) @assert lentname > 0 resize!(tname, lentname - 1) return transcode(String, tname) end function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) filename = _win_mkstemp(parent) cleanup && temp_cleanup_later(filename) return (filename, Base.open(filename, "r+")) end else # !windows # Create and return the name of a temporary file along with an IOStream function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) b = joinpath(parent, temp_prefix * "XXXXXX") p = ccall(:mkstemp, Int32, (Cstring,), b) # modifies b systemerror(:mktemp, p == -1) cleanup && temp_cleanup_later(b) return (b, fdio(p, true)) end end # os-test """ tempname(parent=tempdir(); cleanup=true) -> String Generate a temporary file path. This function only returns a path; no file is created. The path is likely to be unique, but this cannot be guaranteed due to the very remote possibility of two simultaneous calls to `tempname` generating the same file name. The name is guaranteed to differ from all files already existing at the time of the call to `tempname`. When called with no arguments, the temporary name will be an absolute path to a temporary name in the system temporary directory as given by `tempdir()`. If a `parent` directory argument is given, the temporary path will be in that directory instead. The `cleanup` option controls whether the process attempts to delete the returned path automatically when the process exits. Note that the `tempname` function does not create any file or directory at the returned location, so there is nothing to cleanup unless you create a file or directory there. If you do and `clean` is `true` it will be deleted upon process termination. !!! compat "Julia 1.4" The `parent` and `cleanup` arguments were added in 1.4. Prior to Julia 1.4 the path `tempname` would never be cleaned up at process termination. !!! warning This can lead to security holes if another process obtains the same file name and creates the file before you are able to. Open the file with `JL_O_EXCL` if this is a concern. Using [`mktemp()`](@ref) is also recommended instead. """ tempname() """ mktemp(parent=tempdir(); cleanup=true) -> (path, io) Return `(path, io)`, where `path` is the path of a new temporary file in `parent` and `io` is an open file object for this path. The `cleanup` option controls whether the temporary file is automatically deleted when the process exits. !!! compat "Julia 1.3" The `cleanup` keyword argument was added in Julia 1.3. Relatedly, starting from 1.3, Julia will remove the temporary paths created by `mktemp` when the Julia process exits, unless `cleanup` is explicitly set to `false`. """ mktemp(parent) """ mktempdir(parent=tempdir(); prefix=$(repr(temp_prefix)), cleanup=true) -> path Create a temporary directory in the `parent` directory with a name constructed from the given `prefix` and a random suffix, and return its path. Additionally, on some platforms, any trailing `'X'` characters in `prefix` may be replaced with random characters. If `parent` does not exist, throw an error. The `cleanup` option controls whether the temporary directory is automatically deleted when the process exits. !!! compat "Julia 1.2" The `prefix` keyword argument was added in Julia 1.2. !!! compat "Julia 1.3" The `cleanup` keyword argument was added in Julia 1.3. Relatedly, starting from 1.3, Julia will remove the temporary paths created by `mktempdir` when the Julia process exits, unless `cleanup` is explicitly set to `false`. See also: [`mktemp`](@ref), [`mkdir`](@ref). """ function mktempdir(parent::AbstractString=tempdir(); prefix::AbstractString=temp_prefix, cleanup::Bool=true) if isempty(parent) || occursin(path_separator_re, parent[end:end]) # append a path_separator only if parent didn't already have one tpath = "$(parent)$(prefix)XXXXXX" else tpath = "$(parent)$(path_separator)$(prefix)XXXXXX" end req = Libc.malloc(_sizeof_uv_fs) try ret = ccall(:uv_fs_mkdtemp, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, tpath, C_NULL) if ret < 0 uv_fs_req_cleanup(req) uv_error("mktempdir($(repr(parent)))", ret) end path = unsafe_string(ccall(:jl_uv_fs_t_path, Cstring, (Ptr{Cvoid},), req)) uv_fs_req_cleanup(req) cleanup && temp_cleanup_later(path) return path finally Libc.free(req) end end """ mktemp(f::Function, parent=tempdir()) Apply the function `f` to the result of [`mktemp(parent)`](@ref) and remove the temporary file upon completion. See also: [`mktempdir`](@ref). """ function mktemp(fn::Function, parent::AbstractString=tempdir()) (tmp_path, tmp_io) = mktemp(parent, cleanup=false) try fn(tmp_path, tmp_io) finally try close(tmp_io) ispath(tmp_path) && rm(tmp_path) catch ex @error "mktemp cleanup" _group=:file exception=(ex, catch_backtrace()) # might be possible to remove later temp_cleanup_later(tmp_path, asap=true) end end end """ mktempdir(f::Function, parent=tempdir(); prefix=$(repr(temp_prefix))) Apply the function `f` to the result of [`mktempdir(parent; prefix)`](@ref) and remove the temporary directory all of its contents upon completion. See also: [`mktemp`](@ref), [`mkdir`](@ref). !!! compat "Julia 1.2" The `prefix` keyword argument was added in Julia 1.2. """ function mktempdir(fn::Function, parent::AbstractString=tempdir(); prefix::AbstractString=temp_prefix) tmpdir = mktempdir(parent; prefix=prefix, cleanup=false) try fn(tmpdir) finally try if ispath(tmpdir) prepare_for_deletion(tmpdir) rm(tmpdir, recursive=true) end catch ex @error "mktempdir cleanup" _group=:file exception=(ex, catch_backtrace()) # might be possible to remove later temp_cleanup_later(tmpdir, asap=true) end end end struct uv_dirent_t name::Ptr{UInt8} typ::Cint end """ readdir(dir::AbstractString=pwd(); join::Bool = false, sort::Bool = true, ) -> Vector{String} Return the names in the directory `dir` or the current working directory if not given. When `join` is false, `readdir` returns just the names in the directory as is; when `join` is true, it returns `joinpath(dir, name)` for each `name` so that the returned strings are full paths. If you want to get absolute paths back, call `readdir` with an absolute directory path and `join` set to true. By default, `readdir` sorts the list of names it returns. If you want to skip sorting the names and get them in the order that the file system lists them, you can use `readdir(dir, sort=false)` to opt out of sorting. See also: [`walkdir`](@ref). !!! compat "Julia 1.4" The `join` and `sort` keyword arguments require at least Julia 1.4. # Examples ```julia-repl julia> cd("/home/JuliaUser/dev/julia") julia> readdir() 30-element Array{String,1}: ".appveyor.yml" ".git" ".gitattributes" โ‹ฎ "ui" "usr" "usr-staging" julia> readdir(join=true) 30-element Array{String,1}: "/home/JuliaUser/dev/julia/.appveyor.yml" "/home/JuliaUser/dev/julia/.git" "/home/JuliaUser/dev/julia/.gitattributes" โ‹ฎ "/home/JuliaUser/dev/julia/ui" "/home/JuliaUser/dev/julia/usr" "/home/JuliaUser/dev/julia/usr-staging" julia> readdir("base") 145-element Array{String,1}: ".gitignore" "Base.jl" "Enums.jl" โ‹ฎ "version_git.sh" "views.jl" "weakkeydict.jl" julia> readdir("base", join=true) 145-element Array{String,1}: "base/.gitignore" "base/Base.jl" "base/Enums.jl" โ‹ฎ "base/version_git.sh" "base/views.jl" "base/weakkeydict.jl" julia> readdir(abspath("base"), join=true) 145-element Array{String,1}: "/home/JuliaUser/dev/julia/base/.gitignore" "/home/JuliaUser/dev/julia/base/Base.jl" "/home/JuliaUser/dev/julia/base/Enums.jl" โ‹ฎ "/home/JuliaUser/dev/julia/base/version_git.sh" "/home/JuliaUser/dev/julia/base/views.jl" "/home/JuliaUser/dev/julia/base/weakkeydict.jl" ``` """ function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true) # Allocate space for uv_fs_t struct req = Libc.malloc(_sizeof_uv_fs) try # defined in sys.c, to call uv_fs_readdir, which sets errno on error. err = ccall(:uv_fs_scandir, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Cint, Ptr{Cvoid}), C_NULL, req, dir, 0, C_NULL) err < 0 && uv_error("readdir($(repr(dir)))", err) # iterate the listing into entries entries = String[] ent = Ref{uv_dirent_t}() while Base.UV_EOF != ccall(:uv_fs_scandir_next, Cint, (Ptr{Cvoid}, Ptr{uv_dirent_t}), req, ent) name = unsafe_string(ent[].name) push!(entries, join ? joinpath(dir, name) : name) end # Clean up the request string uv_fs_req_cleanup(req) # sort entries unless opted out sort && sort!(entries) return entries finally Libc.free(req) end end readdir(; join::Bool=false, sort::Bool=true) = readdir(join ? pwd() : ".", join=join, sort=sort) """ walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) Return an iterator that walks the directory tree of a directory. The iterator returns a tuple containing `(rootpath, dirs, files)`. The directory tree can be traversed top-down or bottom-up. If `walkdir` or `stat` encounters a `IOError` it will rethrow the error by default. A custom error handling function can be provided through `onerror` keyword argument. `onerror` is called with a `IOError` as argument. See also: [`readdir`](@ref). # Examples ```julia for (root, dirs, files) in walkdir(".") println("Directories in \$root") for dir in dirs println(joinpath(root, dir)) # path to directories end println("Files in \$root") for file in files println(joinpath(root, file)) # path to files end end ``` ```julia-repl julia> mkpath("my/test/dir"); julia> itr = walkdir("my"); julia> (root, dirs, files) = first(itr) ("my", ["test"], String[]) julia> (root, dirs, files) = first(itr) ("my/test", ["dir"], String[]) julia> (root, dirs, files) = first(itr) ("my/test/dir", String[], String[]) ``` """ function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) function _walkdir(chnl, root) tryf(f, p) = try f(p) catch err isa(err, IOError) || rethrow() try onerror(err) catch err2 close(chnl, err2) end return end content = tryf(readdir, root) content === nothing && return dirs = Vector{eltype(content)}() files = Vector{eltype(content)}() for name in content path = joinpath(root, name) # If we're not following symlinks, then treat all symlinks as files if (!follow_symlinks && something(tryf(islink, path), true)) || !something(tryf(isdir, path), false) push!(files, name) else push!(dirs, name) end end if topdown push!(chnl, (root, dirs, files)) end for dir in dirs _walkdir(chnl, joinpath(root, dir)) end if !topdown push!(chnl, (root, dirs, files)) end nothing end return Channel{Tuple{String,Vector{String},Vector{String}}}(chnl -> _walkdir(chnl, root)) end function unlink(p::AbstractString) err = ccall(:jl_fs_unlink, Int32, (Cstring,), p) err < 0 && uv_error("unlink($(repr(p)))", err) nothing end # For move command function rename(src::AbstractString, dst::AbstractString; force::Bool=false) err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), src, dst) # on error, default to cp && rm if err < 0 cp(src, dst; force=force, follow_symlinks=false) rm(src; recursive=true) end nothing end function sendfile(src::AbstractString, dst::AbstractString) src_open = false dst_open = false local src_file, dst_file try src_file = open(src, JL_O_RDONLY) src_open = true dst_file = open(dst, JL_O_CREAT | JL_O_TRUNC | JL_O_WRONLY, filemode(src_file)) dst_open = true bytes = filesize(stat(src_file)) sendfile(dst_file, src_file, Int64(0), Int(bytes)) finally if src_open && isopen(src_file) close(src_file) end if dst_open && isopen(dst_file) close(dst_file) end end end if Sys.iswindows() const UV_FS_SYMLINK_DIR = 0x0001 const UV_FS_SYMLINK_JUNCTION = 0x0002 const UV__EPERM = -4048 end """ hardlink(src::AbstractString, dst::AbstractString) Creates a hard link to an existing source file `src` with the name `dst`. The destination, `dst`, must not exist. See also: [`symlink`](@ref). !!! compat "Julia 1.8" This method was added in Julia 1.8. """ function hardlink(src::AbstractString, dst::AbstractString) err = ccall(:jl_fs_hardlink, Int32, (Cstring, Cstring), src, dst) if err < 0 msg = "hardlink($(repr(src)), $(repr(dst)))" uv_error(msg, err) end return nothing end """ symlink(target::AbstractString, link::AbstractString; dir_target = false) Creates a symbolic link to `target` with the name `link`. On Windows, symlinks must be explicitly declared as referring to a directory or not. If `target` already exists, by default the type of `link` will be auto- detected, however if `target` does not exist, this function defaults to creating a file symlink unless `dir_target` is set to `true`. Note that if the user sets `dir_target` but `target` exists and is a file, a directory symlink will still be created, but dereferencing the symlink will fail, just as if the user creates a file symlink (by calling `symlink()` with `dir_target` set to `false` before the directory is created) and tries to dereference it to a directory. Additionally, there are two methods of making a link on Windows; symbolic links and junction points. Junction points are slightly more efficient, but do not support relative paths, so if a relative directory symlink is requested (as denoted by `isabspath(target)` returning `false`) a symlink will be used, else a junction point will be used. Best practice for creating symlinks on Windows is to create them only after the files/directories they reference are already created. See also: [`hardlink`](@ref). !!! note This function raises an error under operating systems that do not support soft symbolic links, such as Windows XP. !!! compat "Julia 1.6" The `dir_target` keyword argument was added in Julia 1.6. Prior to this, symlinks to nonexistent paths on windows would always be file symlinks, and relative symlinks to directories were not supported. """ function symlink(target::AbstractString, link::AbstractString; dir_target::Bool = false) @static if Sys.iswindows() if Sys.windows_version() < Sys.WINDOWS_VISTA_VER error("Windows XP does not support soft symlinks") end end flags = 0 @static if Sys.iswindows() # If we're going to create a directory link, we need to know beforehand. # First, if `target` is not an absolute path, let's immediately resolve # it so that we can peek and see if it's a directory. resolved_target = target if !isabspath(target) resolved_target = joinpath(dirname(link), target) end # If it is a directory (or `dir_target` is set), we'll need to add one # of `UV_FS_SYMLINK_{DIR,JUNCTION}` to the flags, depending on whether # `target` is an absolute path or not. if (ispath(resolved_target) && isdir(resolved_target)) || dir_target if isabspath(target) flags |= UV_FS_SYMLINK_JUNCTION else flags |= UV_FS_SYMLINK_DIR end end end err = ccall(:jl_fs_symlink, Int32, (Cstring, Cstring, Cint), target, link, flags) if err < 0 msg = "symlink($(repr(target)), $(repr(link)))" @static if Sys.iswindows() # creating file/directory symlinks requires Administrator privileges # while junction points apparently do not if flags & UV_FS_SYMLINK_JUNCTION == 0 && err == UV__EPERM msg = "On Windows, creating symlinks requires Administrator privileges.\n$msg" end end uv_error(msg, err) end return nothing end """ readlink(path::AbstractString) -> String Return the target location a symbolic link `path` points to. """ function readlink(path::AbstractString) req = Libc.malloc(_sizeof_uv_fs) try ret = ccall(:uv_fs_readlink, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL) if ret < 0 uv_fs_req_cleanup(req) uv_error("readlink($(repr(path)))", ret) @assert false end tgt = unsafe_string(ccall(:jl_uv_fs_t_ptr, Cstring, (Ptr{Cvoid},), req)) uv_fs_req_cleanup(req) return tgt finally Libc.free(req) end end """ chmod(path::AbstractString, mode::Integer; recursive::Bool=false) Change the permissions mode of `path` to `mode`. Only integer `mode`s (e.g. `0o777`) are currently supported. If `recursive=true` and the path is a directory all permissions in that directory will be recursively changed. Return `path`. !!! note Prior to Julia 1.6, this did not correctly manipulate filesystem ACLs on Windows, therefore it would only set read-only bits on files. It now is able to manipulate ACLs. """ function chmod(path::AbstractString, mode::Integer; recursive::Bool=false) err = ccall(:jl_fs_chmod, Int32, (Cstring, Cint), path, mode) err < 0 && uv_error("chmod($(repr(path)), 0o$(string(mode, base=8)))", err) if recursive && isdir(path) for p in readdir(path) if !islink(joinpath(path, p)) chmod(joinpath(path, p), mode, recursive=true) end end end path end """ chown(path::AbstractString, owner::Integer, group::Integer=-1) Change the owner and/or group of `path` to `owner` and/or `group`. If the value entered for `owner` or `group` is `-1` the corresponding ID will not change. Only integer `owner`s and `group`s are currently supported. Return `path`. """ function chown(path::AbstractString, owner::Integer, group::Integer=-1) err = ccall(:jl_fs_chown, Int32, (Cstring, Cint, Cint), path, owner, group) err < 0 && uv_error("chown($(repr(path)), $owner, $group)", err) path end # - http://docs.libuv.org/en/v1.x/fs.html#c.uv_fs_statfs (libuv function docs) # - http://docs.libuv.org/en/v1.x/fs.html#c.uv_statfs_t (libuv docs of the returned struct) """ DiskStat Stores information about the disk in bytes. Populate by calling `diskstat`. """ struct DiskStat ftype::UInt64 bsize::UInt64 blocks::UInt64 bfree::UInt64 bavail::UInt64 files::UInt64 ffree::UInt64 fspare::NTuple{4, UInt64} # reserved end function Base.getproperty(stats::DiskStat, field::Symbol) total = Int64(getfield(stats, :bsize) * getfield(stats, :blocks)) available = Int64(getfield(stats, :bsize) * getfield(stats, :bavail)) field === :total && return total field === :available && return available field === :used && return total - available return getfield(stats, field) end @eval Base.propertynames(stats::DiskStat) = $((fieldnames(DiskStat)[1:end-1]..., :available, :total, :used)) Base.show(io::IO, x::DiskStat) = print(io, "DiskStat(total=$(x.total), used=$(x.used), available=$(x.available))") """ diskstat(path=pwd()) Returns statistics in bytes about the disk that contains the file or directory pointed at by `path`. If no argument is passed, statistics about the disk that contains the current working directory are returned. !!! compat "Julia 1.8" This method was added in Julia 1.8. """ function diskstat(path::AbstractString=pwd()) req = zeros(UInt8, _sizeof_uv_fs) err = ccall(:uv_fs_statfs, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), C_NULL, req, path, C_NULL) err < 0 && uv_error("diskstat($(repr(path)))", err) statfs_ptr = ccall(:jl_uv_fs_t_ptr, Ptr{Nothing}, (Ptr{Cvoid},), req) return unsafe_load(reinterpret(Ptr{DiskStat}, statfs_ptr)) end W/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/./file_constants.jlconst JL_O_WRONLY = 0o01 const JL_O_RDONLY = 0o00 const JL_O_RDWR = 0o02 const JL_O_APPEND = 0o02000 const JL_O_CREAT = 0o0100 const JL_O_EXCL = 0o0200 const JL_O_TRUNC = 0o01000 const JL_O_TEMPORARY = 0x0000 const JL_O_SHORT_LIVED = 0x0000 const JL_O_SEQUENTIAL = 0x0000 const JL_O_RANDOM = 0x0000 const JL_O_NOCTTY = 0o0400 const JL_O_NONBLOCK = 0o04000 const JL_O_NDELAY = 0o04000 const JL_O_SYNC = 0o04010000 const JL_O_FSYNC = 0o04010000 const JL_O_ASYNC = 0o020000 const JL_O_LARGEFILE = 0x0000 const JL_O_DIRECTORY = 0o0200000 const JL_O_NOFOLLOW = 0o0400000 const JL_O_CLOEXEC = 0o02000000 const JL_O_DIRECT = 0x0000 const JL_O_NOATIME = 0x0000 const JL_O_PATH = 0x0000 const JL_O_TMPFILE = 0x0000 const JL_O_DSYNC = 0o010000 const JL_O_RSYNC = 0o04010000 J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/cmd.jl~F# This file is a part of Julia. License is MIT: https://julialang.org/license abstract type AbstractCmd end # libuv process option flags const UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = UInt8(1 << 2) const UV_PROCESS_DETACHED = UInt8(1 << 3) const UV_PROCESS_WINDOWS_HIDE = UInt8(1 << 4) struct Cmd <: AbstractCmd exec::Vector{String} ignorestatus::Bool flags::UInt32 # libuv process flags env::Union{Vector{String},Nothing} dir::String cpus::Union{Nothing,Vector{UInt16}} Cmd(exec::Vector{String}) = new(exec, false, 0x00, nothing, "", nothing) Cmd(cmd::Cmd, ignorestatus, flags, env, dir, cpus = nothing) = new(cmd.exec, ignorestatus, flags, env, dir === cmd.dir ? dir : cstr(dir), cpus) function Cmd(cmd::Cmd; ignorestatus::Bool=cmd.ignorestatus, env=cmd.env, dir::AbstractString=cmd.dir, cpus::Union{Nothing,Vector{UInt16}} = cmd.cpus, detach::Bool = 0 != cmd.flags & UV_PROCESS_DETACHED, windows_verbatim::Bool = 0 != cmd.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, windows_hide::Bool = 0 != cmd.flags & UV_PROCESS_WINDOWS_HIDE) flags = detach * UV_PROCESS_DETACHED | windows_verbatim * UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | windows_hide * UV_PROCESS_WINDOWS_HIDE new(cmd.exec, ignorestatus, flags, byteenv(env), dir === cmd.dir ? dir : cstr(dir), cpus) end end has_nondefault_cmd_flags(c::Cmd) = c.ignorestatus || c.flags != 0x00 || c.env !== nothing || c.dir !== "" || c.cpus !== nothing """ Cmd(cmd::Cmd; ignorestatus, detach, windows_verbatim, windows_hide, env, dir) Cmd(exec::Vector{String}) Construct a new `Cmd` object, representing an external program and arguments, from `cmd`, while changing the settings of the optional keyword arguments: * `ignorestatus::Bool`: If `true` (defaults to `false`), then the `Cmd` will not throw an error if the return code is nonzero. * `detach::Bool`: If `true` (defaults to `false`), then the `Cmd` will be run in a new process group, allowing it to outlive the `julia` process and not have Ctrl-C passed to it. * `windows_verbatim::Bool`: If `true` (defaults to `false`), then on Windows the `Cmd` will send a command-line string to the process with no quoting or escaping of arguments, even arguments containing spaces. (On Windows, arguments are sent to a program as a single "command-line" string, and programs are responsible for parsing it into arguments. By default, empty arguments and arguments with spaces or tabs are quoted with double quotes `"` in the command line, and `\\` or `"` are preceded by backslashes. `windows_verbatim=true` is useful for launching programs that parse their command line in nonstandard ways.) Has no effect on non-Windows systems. * `windows_hide::Bool`: If `true` (defaults to `false`), then on Windows no new console window is displayed when the `Cmd` is executed. This has no effect if a console is already open or on non-Windows systems. * `env`: Set environment variables to use when running the `Cmd`. `env` is either a dictionary mapping strings to strings, an array of strings of the form `"var=val"`, an array or tuple of `"var"=>val` pairs. In order to modify (rather than replace) the existing environment, initialize `env` with `copy(ENV)` and then set `env["var"]=val` as desired. To add to an environment block within a `Cmd` object without replacing all elements, use [`addenv()`](@ref) which will return a `Cmd` object with the updated environment. * `dir::AbstractString`: Specify a working directory for the command (instead of the current directory). For any keywords that are not specified, the current settings from `cmd` are used. Note that the `Cmd(exec)` constructor does not create a copy of `exec`. Any subsequent changes to `exec` will be reflected in the `Cmd` object. The most common way to construct a `Cmd` object is with command literals (backticks), e.g. `ls -l` This can then be passed to the `Cmd` constructor to modify its settings, e.g. Cmd(`echo "Hello world"`, ignorestatus=true, detach=false) """ Cmd hash(x::Cmd, h::UInt) = hash(x.exec, hash(x.env, hash(x.ignorestatus, hash(x.dir, hash(x.flags, h))))) ==(x::Cmd, y::Cmd) = x.exec == y.exec && x.env == y.env && x.ignorestatus == y.ignorestatus && x.dir == y.dir && isequal(x.flags, y.flags) struct OrCmds <: AbstractCmd a::AbstractCmd b::AbstractCmd OrCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) end struct ErrOrCmds <: AbstractCmd a::AbstractCmd b::AbstractCmd ErrOrCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) end struct AndCmds <: AbstractCmd a::AbstractCmd b::AbstractCmd AndCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) end hash(x::AndCmds, h::UInt) = hash(x.a, hash(x.b, h)) ==(x::AndCmds, y::AndCmds) = x.a == y.a && x.b == y.b shell_escape(cmd::Cmd; special::AbstractString="") = shell_escape(cmd.exec..., special=special) shell_escape_posixly(cmd::Cmd) = shell_escape_posixly(cmd.exec...) shell_escape_csh(cmd::Cmd) = shell_escape_csh(cmd.exec...) escape_microsoft_c_args(cmd::Cmd) = escape_microsoft_c_args(cmd.exec...) escape_microsoft_c_args(io::IO, cmd::Cmd) = escape_microsoft_c_args(io::IO, cmd.exec...) function show(io::IO, cmd::Cmd) print_env = cmd.env !== nothing print_dir = !isempty(cmd.dir) (print_env || print_dir) && print(io, "setenv(") print_cpus = cmd.cpus !== nothing print_cpus && print(io, "setcpuaffinity(") print(io, '`') join(io, map(cmd.exec) do arg replace(sprint(context=io) do io with_output_color(:underline, io) do io print_shell_word(io, arg, shell_special) end end, '`' => "\\`") end, ' ') print(io, '`') if print_cpus print(io, ", ") show(io, collect(Int, something(cmd.cpus))) print(io, ")") end print_env && (print(io, ","); show(io, cmd.env)) print_dir && (print(io, "; dir="); show(io, cmd.dir)) (print_dir || print_env) && print(io, ")") nothing end function show(io::IO, cmds::Union{OrCmds,ErrOrCmds}) print(io, "pipeline(") show(io, cmds.a) print(io, ", ") print(io, isa(cmds, ErrOrCmds) ? "stderr=" : "stdout=") show(io, cmds.b) print(io, ")") end function show(io::IO, cmds::AndCmds) show(io, cmds.a) print(io, " & ") show(io, cmds.b) end const STDIN_NO = 0 const STDOUT_NO = 1 const STDERR_NO = 2 struct FileRedirect filename::String append::Bool FileRedirect(filename::AbstractString, append::Bool) = FileRedirect(convert(String, filename), append) function FileRedirect(filename::String, append::Bool) if lowercase(filename) == (@static Sys.iswindows() ? "nul" : "/dev/null") @warn "For portability use devnull instead of a file redirect" maxlog=1 end return new(filename, append) end end # setup_stdio โ‰ˆ cconvert # rawhandle โ‰ˆ unsafe_convert rawhandle(::DevNull) = C_NULL rawhandle(x::OS_HANDLE) = x if OS_HANDLE !== RawFD rawhandle(x::RawFD) = Libc._get_osfhandle(x) end setup_stdio(stdio::Union{DevNull,OS_HANDLE,RawFD}, ::Bool) = (stdio, false) const Redirectable = Union{IO, FileRedirect, RawFD, OS_HANDLE} const StdIOSet = NTuple{3, Redirectable} struct CmdRedirect <: AbstractCmd cmd::AbstractCmd handle::Redirectable stream_no::Int readable::Bool end CmdRedirect(cmd, handle, stream_no) = CmdRedirect(cmd, handle, stream_no, stream_no == STDIN_NO) function show(io::IO, cr::CmdRedirect) print(io, "pipeline(") show(io, cr.cmd) print(io, ", ") if cr.stream_no == STDOUT_NO print(io, "stdout") elseif cr.stream_no == STDERR_NO print(io, "stderr") elseif cr.stream_no == STDIN_NO print(io, "stdin") else print(io, cr.stream_no) end print(io, cr.readable ? "<" : ">") show(io, cr.handle) print(io, ")") end """ ignorestatus(command) Mark a command object so that running it will not throw an error if the result code is non-zero. """ ignorestatus(cmd::Cmd) = Cmd(cmd, ignorestatus=true) ignorestatus(cmd::Union{OrCmds,AndCmds}) = typeof(cmd)(ignorestatus(cmd.a), ignorestatus(cmd.b)) """ detach(command) Mark a command object so that it will be run in a new process group, allowing it to outlive the julia process, and not have Ctrl-C interrupts passed to it. """ detach(cmd::Cmd) = Cmd(cmd; detach=true) # like String(s), but throw an error if s contains NUL, since # libuv requires NUL-terminated strings function cstr(s) if Base.containsnul(s) throw(ArgumentError("strings containing NUL cannot be passed to spawned processes")) end return String(s)::String end # convert various env representations into an array of "key=val" strings byteenv(env::AbstractArray{<:AbstractString}) = String[cstr(x) for x in env] byteenv(env::AbstractDict) = String[cstr(string(k)*"="*string(v)) for (k,v) in env] byteenv(env::Nothing) = nothing byteenv(env::Union{AbstractVector{Pair{T,V}}, Tuple{Vararg{Pair{T,V}}}}) where {T<:AbstractString,V} = String[cstr(k*"="*string(v)) for (k,v) in env] """ setenv(command::Cmd, env; dir) Set environment variables to use when running the given `command`. `env` is either a dictionary mapping strings to strings, an array of strings of the form `"var=val"`, or zero or more `"var"=>val` pair arguments. In order to modify (rather than replace) the existing environment, create `env` through `copy(ENV)` and then setting `env["var"]=val` as desired, or use [`addenv`](@ref). The `dir` keyword argument can be used to specify a working directory for the command. `dir` defaults to the currently set `dir` for `command` (which is the current working directory if not specified already). See also [`Cmd`](@ref), [`addenv`](@ref), [`ENV`](@ref), [`pwd`](@ref). """ setenv(cmd::Cmd, env; dir=cmd.dir) = Cmd(cmd; env=byteenv(env), dir=dir) setenv(cmd::Cmd, env::Pair{<:AbstractString}...; dir=cmd.dir) = setenv(cmd, env; dir=dir) setenv(cmd::Cmd; dir=cmd.dir) = Cmd(cmd; dir=dir) # split environment entry string into before and after first `=` (key and value) function splitenv(e::String) i = findnext('=', e, 2) if i === nothing throw(ArgumentError("malformed environment entry")) end e[1:prevind(e, i)], e[nextind(e, i):end] end """ addenv(command::Cmd, env...; inherit::Bool = true) Merge new environment mappings into the given [`Cmd`](@ref) object, returning a new `Cmd` object. Duplicate keys are replaced. If `command` does not contain any environment values set already, it inherits the current environment at time of `addenv()` call if `inherit` is `true`. Keys with value `nothing` are deleted from the env. See also [`Cmd`](@ref), [`setenv`](@ref), [`ENV`](@ref). !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ function addenv(cmd::Cmd, env::Dict; inherit::Bool = true) new_env = Dict{String,String}() if cmd.env === nothing if inherit merge!(new_env, ENV) end else for (k, v) in splitenv.(cmd.env) new_env[string(k)::String] = string(v)::String end end for (k, v) in env if v === nothing delete!(new_env, string(k)::String) else new_env[string(k)::String] = string(v)::String end end return setenv(cmd, new_env) end function addenv(cmd::Cmd, pairs::Pair{<:AbstractString}...; inherit::Bool = true) return addenv(cmd, Dict(k => v for (k, v) in pairs); inherit) end function addenv(cmd::Cmd, env::Vector{<:AbstractString}; inherit::Bool = true) return addenv(cmd, Dict(k => v for (k, v) in splitenv.(env)); inherit) end """ setcpuaffinity(original_command::Cmd, cpus) -> command::Cmd Set the CPU affinity of the `command` by a list of CPU IDs (1-based) `cpus`. Passing `cpus = nothing` means to unset the CPU affinity if the `original_command` has any. This function is supported only in Linux and Windows. It is not supported in macOS because libuv does not support affinity setting. !!! compat "Julia 1.8" This function requires at least Julia 1.8. # Examples In Linux, the `taskset` command line program can be used to see how `setcpuaffinity` works. ```julia julia> run(setcpuaffinity(`sh -c 'taskset -p \$\$'`, [1, 2, 5])); pid 2273's current affinity mask: 13 ``` Note that the mask value `13` reflects that the first, second, and the fifth bits (counting from the least significant position) are turned on: ```julia julia> 0b010011 0x13 ``` """ function setcpuaffinity end setcpuaffinity(cmd::Cmd, ::Nothing) = Cmd(cmd; cpus = nothing) setcpuaffinity(cmd::Cmd, cpus) = Cmd(cmd; cpus = collect(UInt16, cpus)) (&)(left::AbstractCmd, right::AbstractCmd) = AndCmds(left, right) redir_out(src::AbstractCmd, dest::AbstractCmd) = OrCmds(src, dest) redir_err(src::AbstractCmd, dest::AbstractCmd) = ErrOrCmds(src, dest) # Stream Redirects redir_out(dest::Redirectable, src::AbstractCmd) = CmdRedirect(src, dest, STDIN_NO) redir_out(src::AbstractCmd, dest::Redirectable) = CmdRedirect(src, dest, STDOUT_NO) redir_err(src::AbstractCmd, dest::Redirectable) = CmdRedirect(src, dest, STDERR_NO) # File redirects redir_out(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, false), STDOUT_NO) redir_out(src::AbstractString, dest::AbstractCmd) = CmdRedirect(dest, FileRedirect(src, false), STDIN_NO) redir_err(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, false), STDERR_NO) redir_out_append(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, true), STDOUT_NO) redir_err_append(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, true), STDERR_NO) """ pipeline(command; stdin, stdout, stderr, append=false) Redirect I/O to or from the given `command`. Keyword arguments specify which of the command's streams should be redirected. `append` controls whether file output appends to the file. This is a more general version of the 2-argument `pipeline` function. `pipeline(from, to)` is equivalent to `pipeline(from, stdout=to)` when `from` is a command, and to `pipeline(to, stdin=from)` when `from` is another kind of data source. **Examples**: ```julia run(pipeline(`dothings`, stdout="out.txt", stderr="errs.txt")) run(pipeline(`update`, stdout="log.txt", append=true)) ``` """ function pipeline(cmd::AbstractCmd; stdin=nothing, stdout=nothing, stderr=nothing, append::Bool=false) if append && stdout === nothing && stderr === nothing throw(ArgumentError("append set to true, but no output redirections specified")) end if stdin !== nothing cmd = redir_out(stdin, cmd) end if stdout !== nothing cmd = append ? redir_out_append(cmd, stdout) : redir_out(cmd, stdout) end if stderr !== nothing cmd = append ? redir_err_append(cmd, stderr) : redir_err(cmd, stderr) end return cmd end pipeline(cmd::AbstractCmd, dest) = pipeline(cmd, stdout=dest) pipeline(src::Union{Redirectable,AbstractString}, cmd::AbstractCmd) = pipeline(cmd, stdin=src) """ pipeline(from, to, ...) Create a pipeline from a data source to a destination. The source and destination can be commands, I/O streams, strings, or results of other `pipeline` calls. At least one argument must be a command. Strings refer to filenames. When called with more than two arguments, they are chained together from left to right. For example, `pipeline(a,b,c)` is equivalent to `pipeline(pipeline(a,b),c)`. This provides a more concise way to specify multi-stage pipelines. **Examples**: ```julia run(pipeline(`ls`, `grep xyz`)) run(pipeline(`ls`, "out.txt")) run(pipeline("out.txt", `grep xyz`)) ``` """ pipeline(a, b, c, d...) = pipeline(pipeline(a, b), c, d...) ## implementation of `cmd` syntax ## cmd_interpolate(xs...) = cstr(string(map(cmd_interpolate1, xs)...)) cmd_interpolate1(x) = x cmd_interpolate1(::Nothing) = throw(ArgumentError("`nothing` can not be interpolated into commands (`Cmd`)")) arg_gen() = String[] arg_gen(x::AbstractString) = String[cstr(x)] function arg_gen(cmd::Cmd) if has_nondefault_cmd_flags(cmd) throw(ArgumentError("Non-default environment behavior is only permitted for the first interpolant.")) end cmd.exec end function arg_gen(head) if isiterable(typeof(head)) vals = String[] for x in head push!(vals, cmd_interpolate(x)) end return vals else return String[cmd_interpolate(head)] end end function arg_gen(head, tail...) head = arg_gen(head) tail = arg_gen(tail...) vals = String[] for h = head, t = tail push!(vals, cmd_interpolate(h,t)) end return vals end function cmd_gen(parsed) args = String[] if length(parsed) >= 1 && isa(parsed[1], Tuple{Cmd}) cmd = (parsed[1]::Tuple{Cmd})[1] (ignorestatus, flags, env, dir) = (cmd.ignorestatus, cmd.flags, cmd.env, cmd.dir) append!(args, cmd.exec) for arg in tail(parsed) append!(args, Base.invokelatest(arg_gen, arg...)::Vector{String}) end return Cmd(Cmd(args), ignorestatus, flags, env, dir) else for arg in parsed append!(args, arg_gen(arg...)::Vector{String}) end return Cmd(args) end end """ @cmd str Similar to `cmd`, generate a `Cmd` from the `str` string which represents the shell command(s) to be executed. The [`Cmd`](@ref) object can be run as a process and can outlive the spawning julia process (see `Cmd` for more). # Examples ```jldoctest julia> cm = @cmd " echo 1 " `echo 1` julia> run(cm) 1 Process(`echo 1`, ProcessExited(0)) ``` """ macro cmd(str) cmd_ex = shell_parse(str, special=shell_special, filename=String(__source__.file))[1] return :(cmd_gen($(esc(cmd_ex)))) end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/process.jl@X# This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct Process <: AbstractPipe cmd::Cmd handle::Ptr{Cvoid} in::IO out::IO err::IO exitcode::Int64 termsignal::Int32 exitnotify::ThreadSynchronizer function Process(cmd::Cmd, handle::Ptr{Cvoid}) this = new(cmd, handle, devnull, devnull, devnull, typemin(fieldtype(Process, :exitcode)), typemin(fieldtype(Process, :termsignal)), ThreadSynchronizer()) finalizer(uvfinalize, this) return this end end pipe_reader(p::Process) = p.out pipe_writer(p::Process) = p.in # Represents a whole pipeline of any number of related processes # so the entire pipeline can be treated as one entity mutable struct ProcessChain <: AbstractPipe processes::Vector{Process} in::IO out::IO err::IO function ProcessChain() return new(Process[], devnull, devnull, devnull) end end pipe_reader(p::ProcessChain) = p.out pipe_writer(p::ProcessChain) = p.in # release ownership of the libuv handle function uvfinalize(proc::Process) if proc.handle != C_NULL iolock_begin() if proc.handle != C_NULL disassociate_julia_struct(proc.handle) ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) proc.handle = C_NULL end iolock_end() end nothing end # called when the process dies function uv_return_spawn(p::Ptr{Cvoid}, exit_status::Int64, termsignal::Int32) data = ccall(:jl_uv_process_data, Ptr{Cvoid}, (Ptr{Cvoid},), p) data == C_NULL && return proc = unsafe_pointer_to_objref(data)::Process proc.exitcode = exit_status proc.termsignal = termsignal disassociate_julia_struct(proc.handle) # ensure that data field is set to C_NULL ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) proc.handle = C_NULL lock(proc.exitnotify) try notify(proc.exitnotify) finally unlock(proc.exitnotify) end nothing end # called when the libuv handle is destroyed function _uv_hook_close(proc::Process) Libc.free(@atomicswap :not_atomic proc.handle = C_NULL) nothing end const SpawnIO = Union{IO, RawFD, OS_HANDLE} const SpawnIOs = Vector{SpawnIO} # convenience name for readability function as_cpumask(cpus::Vector{UInt16}) n = max(Int(maximum(cpus)), Int(ccall(:uv_cpumask_size, Cint, ()))) cpumask = zeros(Bool, n) for i in cpus cpumask[i] = true end return cpumask end # handle marshalling of `Cmd` arguments from Julia to C @noinline function _spawn_primitive(file, cmd::Cmd, stdio::SpawnIOs) loop = eventloop() cpumask = cmd.cpus cpumask === nothing || (cpumask = as_cpumask(cpumask)) GC.@preserve stdio begin iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout let h = rawhandle(io) h === C_NULL ? (0x00, UInt(0)) : h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) : h isa Ptr{Cvoid} ? (0x04, UInt(h)) : error("invalid spawn handle $h from $io") end for io in stdio] handle = Libc.malloc(_sizeof_uv_process) disassociate_julia_struct(handle) (; exec, flags, env, dir) = cmd iolock_begin() err = ccall(:jl_spawn, Int32, (Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Tuple{Cint, UInt}}, Int, UInt32, Ptr{Cstring}, Cstring, Ptr{Bool}, Csize_t, Ptr{Cvoid}), file, exec, loop, handle, iohandles, length(iohandles), flags, env === nothing ? C_NULL : env, isempty(dir) ? C_NULL : dir, cpumask === nothing ? C_NULL : cpumask, cpumask === nothing ? 0 : length(cpumask), @cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32))) if err == 0 pp = Process(cmd, handle) associate_julia_struct(handle, pp) else ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually end iolock_end() end if err != 0 throw(_UVError("could not spawn " * repr(cmd), err)) end return pp end _spawn(cmds::AbstractCmd) = _spawn(cmds, SpawnIO[]) # optimization: we can spawn `Cmd` directly without allocating the ProcessChain function _spawn(cmd::Cmd, stdios::SpawnIOs) isempty(cmd.exec) && throw(ArgumentError("cannot spawn empty command")) pp = setup_stdios(stdios) do stdios return _spawn_primitive(cmd.exec[1], cmd, stdios) end return pp end # assume that having a ProcessChain means that the stdio are setup function _spawn(cmds::AbstractCmd, stdios::SpawnIOs) pp = setup_stdios(stdios) do stdios return _spawn(cmds, stdios, ProcessChain()) end return pp end # helper function for making a copy of a SpawnIOs, with replacement function _stdio_copy(stdios::SpawnIOs, fd::Int, @nospecialize replace) nio = max(fd, length(stdios)) new = SpawnIOs(undef, nio) copyto!(fill!(new, devnull), stdios) new[fd] = replace return new end function _spawn(redirect::CmdRedirect, stdios::SpawnIOs, args...) fdnum = redirect.stream_no + 1 io, close_io = setup_stdio(redirect.handle, redirect.readable) try stdios = _stdio_copy(stdios, fdnum, io) return _spawn(redirect.cmd, stdios, args...) finally close_io && close_stdio(io) end end function _spawn(cmds::OrCmds, stdios::SpawnIOs, chain::ProcessChain) in_pipe, out_pipe = link_pipe(false, false) try stdios_left = _stdio_copy(stdios, 2, out_pipe) _spawn(cmds.a, stdios_left, chain) stdios_right = _stdio_copy(stdios, 1, in_pipe) _spawn(cmds.b, stdios_right, chain) finally close_pipe_sync(out_pipe) close_pipe_sync(in_pipe) end return chain end function _spawn(cmds::ErrOrCmds, stdios::SpawnIOs, chain::ProcessChain) in_pipe, out_pipe = link_pipe(false, false) try stdios_left = _stdio_copy(stdios, 3, out_pipe) _spawn(cmds.a, stdios_left, chain) stdios_right = _stdio_copy(stdios, 1, in_pipe) _spawn(cmds.b, stdios_right, chain) finally close_pipe_sync(out_pipe) close_pipe_sync(in_pipe) end return chain end function _spawn(cmds::AndCmds, stdios::SpawnIOs, chain::ProcessChain) _spawn(cmds.a, stdios, chain) _spawn(cmds.b, stdios, chain) return chain end function _spawn(cmd::Cmd, stdios::SpawnIOs, chain::ProcessChain) isempty(cmd.exec) && throw(ArgumentError("cannot spawn empty command")) pp = _spawn_primitive(cmd.exec[1], cmd, stdios) push!(chain.processes, pp) return chain end # open the child end of each element of `stdios`, and initialize the parent end function setup_stdios(f, stdios::SpawnIOs) nstdio = length(stdios) open_io = SpawnIOs(undef, nstdio) close_io = falses(nstdio) try for i in 1:nstdio open_io[i], close_io[i] = setup_stdio(stdios[i], i == 1) end pp = f(open_io) return pp finally for i in 1:nstdio close_io[i] && close_stdio(open_io[i]) end end end function setup_stdio(stdio::PipeEndpoint, child_readable::Bool) if stdio.status == StatusInit # if the PipeEndpoint isn't open, set it to the parent end # and pass the other end to the child rd, wr = link_pipe(!child_readable, child_readable) try open_pipe!(stdio, child_readable ? wr : rd) catch close_pipe_sync(rd) close_pipe_sync(wr) rethrow() end child = child_readable ? rd : wr return (child, true) end # if it's already open, assume that it's already the child end # (since we can't do anything else) return (stdio, false) end function setup_stdio(stdio::Pipe, child_readable::Bool) if stdio.in.status == StatusInit && stdio.out.status == StatusInit link_pipe!(stdio) end io = child_readable ? stdio.out : stdio.in return (io, false) end setup_stdio(stdio::AbstractPipe, readable::Bool) = setup_stdio(readable ? pipe_reader(stdio) : pipe_writer(stdio), readable) function setup_stdio(stdio::IOStream, child_readable::Bool) io = RawFD(fd(stdio)) return (io, false) end function setup_stdio(stdio::FileRedirect, child_readable::Bool) if child_readable attr = JL_O_RDONLY perm = zero(S_IRUSR) else attr = JL_O_WRONLY | JL_O_CREAT attr |= stdio.append ? JL_O_APPEND : JL_O_TRUNC perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH end io = Filesystem.open(stdio.filename, attr, perm) return (io, true) end # incrementally move data between an arbitrary IO and a system Pipe, # including copying the EOF (shutdown) when finished # TODO: probably more efficient (when valid) to use `stdio` directly as the # PipeEndpoint buffer field in some cases function setup_stdio(stdio::IO, child_readable::Bool) parent = PipeEndpoint() rd, wr = link_pipe(!child_readable, child_readable) try open_pipe!(parent, child_readable ? wr : rd) catch close_pipe_sync(rd) close_pipe_sync(wr) rethrow() end child = child_readable ? rd : wr try let in = (child_readable ? parent : stdio), out = (child_readable ? stdio : parent) @async try write(in, out) catch ex @warn "Process I/O error" exception=(ex, catch_backtrace()) finally close(parent) child_readable || closewrite(stdio) end end catch close_pipe_sync(child) rethrow() end return (child, true) end close_stdio(stdio::OS_HANDLE) = close_pipe_sync(stdio) close_stdio(stdio) = close(stdio) # INTERNAL # pad out stdio to have at least three elements, # passing either `devnull` or the corresponding `stdio` # A Redirectable can be any of: # - A system IO handle, to be passed to the child # - An uninitialized pipe, to be created # - devnull (to pass /dev/null for 0-2, or to leave undefined for fd > 2) # - An Filesystem.File or IOStream object to redirect the output to # - A FileRedirect, containing a string specifying a filename to be opened for the child spawn_opts_swallow(stdios::StdIOSet) = SpawnIO[stdios...] spawn_opts_inherit(stdios::StdIOSet) = SpawnIO[stdios...] spawn_opts_swallow(in::Redirectable=devnull, out::Redirectable=devnull, err::Redirectable=devnull) = SpawnIO[in, out, err] # pass original descriptors to child processes by default, because we might # have already exhausted and closed the libuv object for our standard streams. # ref issue #8529 spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::Redirectable=RawFD(2)) = SpawnIO[in, out, err] function eachline(cmd::AbstractCmd; keep::Bool=false) out = PipeEndpoint() processes = _spawn(cmd, SpawnIO[devnull, out, stderr]) # if the user consumes all the data, also check process exit status for success ondone = () -> (success(processes) || pipeline_error(processes); nothing) return EachLine(out, keep=keep, ondone=ondone)::EachLine end """ open(command, mode::AbstractString, stdio=devnull) Run `command` asynchronously. Like `open(command, stdio; read, write)` except specifying the read and write flags via a mode string instead of keyword arguments. Possible mode strings are: | Mode | Description | Keywords | |:-----|:------------|:---------------------------------| | `r` | read | none | | `w` | write | `write = true` | | `r+` | read, write | `read = true, write = true` | | `w+` | read, write | `read = true, write = true` | """ function open(cmds::AbstractCmd, mode::AbstractString, stdio::Redirectable=devnull) if mode == "r+" || mode == "w+" return open(cmds, stdio, read = true, write = true) elseif mode == "r" return open(cmds, stdio) elseif mode == "w" return open(cmds, stdio, write = true) else throw(ArgumentError("mode must be \"r\", \"w\", \"r+\", or \"w+\", not $(repr(mode))")) end end # return a Process object to read-to/write-from the pipeline """ open(command, stdio=devnull; write::Bool = false, read::Bool = !write) Start running `command` asynchronously, and return a `process::IO` object. If `read` is true, then reads from the process come from the process's standard output and `stdio` optionally specifies the process's standard input stream. If `write` is true, then writes go to the process's standard input and `stdio` optionally specifies the process's standard output stream. The process's standard error stream is connected to the current global `stderr`. """ function open(cmds::AbstractCmd, stdio::Redirectable=devnull; write::Bool=false, read::Bool=!write) if read && write stdio === devnull || throw(ArgumentError("no stream can be specified for `stdio` in read-write mode")) in = PipeEndpoint() out = PipeEndpoint() processes = _spawn(cmds, SpawnIO[in, out, stderr]) processes.in = in processes.out = out elseif read out = PipeEndpoint() processes = _spawn(cmds, SpawnIO[stdio, out, stderr]) processes.out = out elseif write in = PipeEndpoint() processes = _spawn(cmds, SpawnIO[in, stdio, stderr]) processes.in = in else stdio === devnull || throw(ArgumentError("no stream can be specified for `stdio` in no-access mode")) processes = _spawn(cmds, SpawnIO[devnull, devnull, stderr]) end return processes end """ open(f::Function, command, args...; kwargs...) Similar to `open(command, args...; kwargs...)`, but calls `f(stream)` on the resulting process stream, then closes the input stream and waits for the process to complete. Return the value returned by `f` on success. Throw an error if the process failed, or if the process attempts to print anything to stdout. """ function open(f::Function, cmds::AbstractCmd, args...; kwargs...) P = open(cmds, args...; kwargs...) function waitkill(P::Union{Process,ProcessChain}) close(P) # 0.1 seconds after we hope it dies (from closing stdio), # we kill the process with SIGTERM (15) local t = Timer(0.1) do t process_running(P) && kill(P) end wait(P) close(t) end ret = try f(P) catch waitkill(P) rethrow() end close(P.in) if !(eof(P.out)::Bool) waitkill(P) throw(_UVError("open(do)", UV_EPIPE)) end success(P) || pipeline_error(P) return ret end """ read(command::Cmd) Run `command` and return the resulting output as an array of bytes. """ function read(cmd::AbstractCmd) procs = open(cmd, "r", devnull) bytes = read(procs.out) success(procs) || pipeline_error(procs) return bytes::Vector{UInt8} end """ read(command::Cmd, String) Run `command` and return the resulting output as a `String`. """ read(cmd::AbstractCmd, ::Type{String}) = String(read(cmd))::String """ run(command, args...; wait::Bool = true) Run a command object, constructed with backticks (see the [Running External Programs](@ref) section in the manual). Throws an error if anything goes wrong, including the process exiting with a non-zero status (when `wait` is true). The `args...` allow you to pass through file descriptors to the command, and are ordered like regular unix file descriptors (eg `stdin, stdout, stderr, FD(3), FD(4)...`). If `wait` is false, the process runs asynchronously. You can later wait for it and check its exit status by calling `success` on the returned process object. When `wait` is false, the process' I/O streams are directed to `devnull`. When `wait` is true, I/O streams are shared with the parent process. Use [`pipeline`](@ref) to control I/O redirection. """ function run(cmds::AbstractCmd, args...; wait::Bool = true) if wait ps = _spawn(cmds, spawn_opts_inherit(args...)) success(ps) || pipeline_error(ps) else stdios = spawn_opts_swallow(args...) ps = _spawn(cmds, stdios) # for each stdio input argument, guess whether the user # passed a `stdio` placeholder object as input, and thus # might be able to use the return AbstractProcess as an IO object # (this really only applies to PipeEndpoint, Pipe, TCPSocket, or an AbstractPipe wrapping one of those) if length(stdios) > 0 in = stdios[1] isa(in, IO) && (ps.in = in) if length(stdios) > 1 out = stdios[2] isa(out, IO) && (ps.out = out) if length(stdios) > 2 err = stdios[3] isa(err, IO) && (ps.err = err) end end end end return ps end # some common signal numbers that are usually available on all platforms # and might be useful as arguments to `kill` or testing against `Process.termsignal` const SIGHUP = 1 const SIGINT = 2 const SIGQUIT = 3 # !windows const SIGKILL = 9 const SIGPIPE = 13 # !windows const SIGTERM = 15 function test_success(proc::Process) @assert process_exited(proc) if proc.exitcode < 0 #TODO: this codepath is not currently tested throw(_UVError("could not start process " * repr(proc.cmd), proc.exitcode)) end return proc.exitcode == 0 && proc.termsignal == 0 end function success(x::Process) wait(x) return test_success(x) end success(procs::Vector{Process}) = mapreduce(success, &, procs) success(procs::ProcessChain) = success(procs.processes) """ success(command) Run a command object, constructed with backticks (see the [Running External Programs](@ref) section in the manual), and tell whether it was successful (exited with a code of 0). An exception is raised if the process cannot be started. """ success(cmd::AbstractCmd) = success(_spawn(cmd)) """ ProcessFailedException Indicates problematic exit status of a process. When running commands or pipelines, this is thrown to indicate a nonzero exit code was returned (i.e. that the invoked process failed). """ struct ProcessFailedException <: Exception procs::Vector{Process} end ProcessFailedException(proc::Process) = ProcessFailedException([proc]) function showerror(io::IO, err::ProcessFailedException) if length(err.procs) == 1 proc = err.procs[1] println(io, "failed process: ", proc, " [", proc.exitcode, "]") else println(io, "failed processes:") for proc in err.procs println(io, " ", proc, " [", proc.exitcode, "]") end end end function pipeline_error(proc::Process) if !proc.cmd.ignorestatus throw(ProcessFailedException(proc)) end nothing end function pipeline_error(procs::ProcessChain) failed = Process[] for p = procs.processes if !test_success(p) && !p.cmd.ignorestatus push!(failed, p) end end isempty(failed) && return nothing throw(ProcessFailedException(failed)) end """ kill(p::Process, signum=Base.SIGTERM) Send a signal to a process. The default is to terminate the process. Returns successfully if the process has already exited, but throws an error if killing the process failed for other reasons (e.g. insufficient permissions). """ function kill(p::Process, signum::Integer=SIGTERM) iolock_begin() if process_running(p) @assert p.handle != C_NULL err = ccall(:uv_process_kill, Int32, (Ptr{Cvoid}, Int32), p.handle, signum) if err != 0 && err != UV_ESRCH throw(_UVError("kill", err)) end end iolock_end() nothing end kill(ps::Vector{Process}, signum::Integer=SIGTERM) = for p in ps; kill(p, signum); end kill(ps::ProcessChain, signum::Integer=SIGTERM) = kill(ps.processes, signum) """ getpid(process) -> Int32 Get the child process ID, if it still exists. !!! compat "Julia 1.1" This function requires at least Julia 1.1. """ function Libc.getpid(p::Process) # TODO: due to threading, this method is only weakly synchronized with the user application iolock_begin() ppid = Int32(0) if p.handle != C_NULL # e.g. process_running ppid = ccall(:jl_uv_process_pid, Int32, (Ptr{Cvoid},), p.handle) end iolock_end() ppid <= 0 && throw(_UVError("getpid", UV_ESRCH)) return ppid end ## process status ## """ process_running(p::Process) Determine whether a process is currently running. """ process_running(s::Process) = s.handle != C_NULL process_running(s::Vector{Process}) = any(process_running, s) process_running(s::ProcessChain) = process_running(s.processes) """ process_exited(p::Process) Determine whether a process has exited. """ process_exited(s::Process) = !process_running(s) process_exited(s::Vector{Process}) = all(process_exited, s) process_exited(s::ProcessChain) = process_exited(s.processes) process_signaled(s::Process) = (s.termsignal > 0) function process_status(s::Process) return process_running(s) ? "ProcessRunning" : process_signaled(s) ? "ProcessSignaled(" * string(s.termsignal) * ")" : process_exited(s) ? "ProcessExited(" * string(s.exitcode) * ")" : error("process status error") end function wait(x::Process) process_exited(x) && return iolock_begin() if !process_exited(x) preserve_handle(x) lock(x.exitnotify) iolock_end() try wait(x.exitnotify) finally unlock(x.exitnotify) unpreserve_handle(x) end else iolock_end() end nothing end wait(x::ProcessChain) = foreach(wait, x.processes) show(io::IO, p::Process) = print(io, "Process(", p.cmd, ", ", process_status(p), ")") # allow the elements of the Cmd to be accessed as an array or iterator for f in (:length, :firstindex, :lastindex, :keys, :first, :last, :iterate) @eval $f(cmd::Cmd) = $f(cmd.exec) end Iterators.reverse(cmd::Cmd) = Iterators.reverse(cmd.exec) eltype(::Type{Cmd}) = eltype(fieldtype(Cmd, :exec)) for f in (:iterate, :getindex) @eval $f(cmd::Cmd, i) = $f(cmd.exec, i) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ttyhascolor.jlศ# This file is a part of Julia. License is MIT: https://julialang.org/license if Sys.iswindows() ttyhascolor(term_type = nothing) = true else function ttyhascolor(term_type = get(ENV, "TERM", "")) startswith(term_type, "xterm") && return true try @static if Sys.KERNEL === :FreeBSD return success(`tput AF 0`) else return success(`tput setaf 0`) end catch e return false end end end function get_have_color() global have_color have_color === nothing && (have_color = ttyhascolor()) return have_color::Bool end in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === get_have_color() haskey(::TTY, key::Symbol) = key === :color getindex(::TTY, key::Symbol) = key === :color ? get_have_color() : throw(KeyError(key)) get(::TTY, key::Symbol, default) = key === :color ? get_have_color() : default S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/secretbuffer.jl)# This file is a part of Julia. License is MIT: https://julialang.org/license """ Base.SecretBuffer() An [`IOBuffer`](@ref)-like object where the contents will be securely wiped when garbage collected. It is considered best practice to wipe the buffer using `Base.shred!(::SecretBuffer)` as soon as the secure data are no longer required. When initializing with existing data, the `SecretBuffer!` method is highly recommended to securely zero the passed argument. Avoid initializing with and converting to `String`s as they are unable to be securely zeroed. # Examples ```jldoctest julia> s = Base.SecretBuffer() SecretBuffer("*******") julia> write(s, 's', 'e', 'c', 'r', 'e', 't') 6 julia> seek(s, 0); Char(read(s, UInt8)) 's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase) julia> Base.shred!(s) SecretBuffer("*******") julia> eof(s) true ``` """ mutable struct SecretBuffer <: IO data::Vector{UInt8} size::Int ptr::Int function SecretBuffer(; sizehint=128) s = new(Vector{UInt8}(undef, sizehint), 0, 1) finalizer(final_shred!, s) return s end end """ SecretBuffer(str::AbstractString) A convenience constructor to initialize a `SecretBuffer` from a non-secret string. Strings are bad at keeping secrets because they are unable to be securely zeroed or destroyed. Therefore, avoid using this constructor with secret data. Instead of starting with a string, either construct the `SecretBuffer` incrementally with `SecretBuffer()` and [`write`](@ref), or use a `Vector{UInt8}` with the `Base.SecretBuffer!(::Vector{UInt8})` constructor. """ SecretBuffer(str::AbstractString) = SecretBuffer(String(str)) function SecretBuffer(str::String) buf = codeunits(str) s = SecretBuffer(sizehint=length(buf)) for c in buf write(s, c) end seek(s, 0) s end convert(::Type{SecretBuffer}, s::AbstractString) = SecretBuffer(String(s)) """ SecretBuffer!(data::Vector{UInt8}) Initialize a new `SecretBuffer` from `data`, securely zeroing `data` afterwards. """ function SecretBuffer!(d::Vector{UInt8}) len = length(d) s = SecretBuffer(sizehint=len) for i in 1:len write(s, d[i]) end seek(s, 0) securezero!(d) s end function unsafe_SecretBuffer!(s::Cstring) if s == C_NULL throw(ArgumentError("cannot convert NULL to SecretBuffer")) end len = Int(ccall(:strlen, Csize_t, (Cstring,), s)) unsafe_SecretBuffer!(convert(Ptr{UInt8}, s), len) end function unsafe_SecretBuffer!(p::Ptr{UInt8}, len=1) if p == C_NULL throw(ArgumentError("cannot convert NULL to SecretBuffer")) end s = SecretBuffer(sizehint=len) for i in 1:len write(s, unsafe_load(p, i)) end seek(s, 0) unsafe_securezero!(p, len) s end show(io::IO, s::SecretBuffer) = print(io, "SecretBuffer(\"*******\")") # Unlike other IO objects, equality is computed by value for convenience ==(s1::SecretBuffer, s2::SecretBuffer) = (s1.ptr == s2.ptr) && (s1.size == s2.size) && (UInt8(0) == _bufcmp(s1.data, s2.data, min(s1.size, s2.size))) # Also attempt a constant time buffer comparison algorithm โ€” the length of the secret might be # inferred by a timing attack, but not its values. @noinline function _bufcmp(data1::Vector{UInt8}, data2::Vector{UInt8}, sz::Int) res = UInt8(0) for i = 1:sz res |= xor(data1[i], data2[i]) end return res end # All SecretBuffers hash the same to avoid leaking information or breaking consistency with == const _sb_hash = UInt === UInt32 ? 0x111c0925 : 0xb06061e370557428 hash(s::SecretBuffer, h::UInt) = hash(_sb_hash, h) function write(io::SecretBuffer, b::UInt8) if io.ptr > length(io.data) # We need to resize! the array: do this manually to ensure no copies are left behind newdata = Vector{UInt8}(undef, (io.size+16)*2) copyto!(newdata, io.data) securezero!(io.data) io.data = newdata end io.size == io.ptr-1 && (io.size += 1) io.data[io.ptr] = b io.ptr += 1 return 1 end function write(io::IO, s::SecretBuffer) nb = 0 for i in 1:s.size nb += write(io, s.data[i]) end return nb end cconvert(::Type{Cstring}, s::SecretBuffer) = unsafe_convert(Cstring, s) function unsafe_convert(::Type{Cstring}, s::SecretBuffer) # Ensure that no nuls appear in the valid region if any(==(0x00), s.data[i] for i in 1:s.size) throw(ArgumentError("`SecretBuffers` containing nul bytes cannot be converted to C strings")) end # Add a hidden nul byte just past the end of the valid region p = s.ptr s.ptr = s.size + 1 write(s, '\0') s.ptr = p s.size -= 1 return Cstring(unsafe_convert(Ptr{Cchar}, s.data)) end seek(io::SecretBuffer, n::Integer) = (io.ptr = max(min(n+1, io.size+1), 1); io) seekend(io::SecretBuffer) = seek(io, io.size+1) skip(io::SecretBuffer, n::Integer) = seek(io, position(io) + n) bytesavailable(io::SecretBuffer) = io.size - io.ptr + 1 position(io::SecretBuffer) = io.ptr-1 eof(io::SecretBuffer) = io.ptr > io.size isempty(io::SecretBuffer) = io.size == 0 function peek(io::SecretBuffer, ::Type{UInt8}) eof(io) && throw(EOFError()) return io.data[io.ptr] end function read(io::SecretBuffer, ::Type{UInt8}) eof(io) && throw(EOFError()) byte = io.data[io.ptr] io.ptr += 1 return byte end function final_shred!(s::SecretBuffer) !isshredded(s) && @async @warn("a SecretBuffer was `shred!`ed by the GC; use `shred!` manually after use to minimize exposure.") shred!(s) end """ shred!(s::SecretBuffer) Shreds the contents of a `SecretBuffer` by securely zeroing its data and resetting its pointer and size. This function is used to securely erase the sensitive data held in the buffer, reducing the potential for information leaks. # Example ```julia s = SecretBuffer() write(s, 's', 'e', 'c', 'r', 'e', 't') shred!(s) # s is now empty ``` """ function shred!(s::SecretBuffer) securezero!(s.data) s.ptr = 1 s.size = 0 return s end isshredded(s::SecretBuffer) = all(iszero, s.data) """ shred!(f::Function, x) Applies function `f` to the argument `x` and then shreds `x`. This function is useful when you need to perform some operations on e.g. a `SecretBuffer` and then want to ensure that it is securely shredded afterwards. """ function shred!(f::Function, x) try f(x) finally shred!(x) end end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/floatfuncs.jlJ@# This file is a part of Julia. License is MIT: https://julialang.org/license ## floating-point functions ## copysign(x::Float64, y::Float64) = copysign_float(x, y) copysign(x::Float32, y::Float32) = copysign_float(x, y) copysign(x::Float32, y::Real) = copysign(x, Float32(y)) copysign(x::Float64, y::Real) = copysign(x, Float64(y)) flipsign(x::Float64, y::Float64) = bitcast(Float64, xor_int(bitcast(UInt64, x), and_int(bitcast(UInt64, y), 0x8000000000000000))) flipsign(x::Float32, y::Float32) = bitcast(Float32, xor_int(bitcast(UInt32, x), and_int(bitcast(UInt32, y), 0x80000000))) flipsign(x::Float32, y::Real) = flipsign(x, Float32(y)) flipsign(x::Float64, y::Real) = flipsign(x, Float64(y)) signbit(x::Float64) = signbit(bitcast(Int64, x)) signbit(x::Float32) = signbit(bitcast(Int32, x)) signbit(x::Float16) = signbit(bitcast(Int16, x)) """ maxintfloat(T=Float64) The largest consecutive integer-valued floating-point number that is exactly represented in the given floating-point type `T` (which defaults to `Float64`). That is, `maxintfloat` returns the smallest positive integer-valued floating-point number `n` such that `n+1` is *not* exactly representable in the type `T`. When an `Integer`-type value is needed, use `Integer(maxintfloat(T))`. """ maxintfloat(::Type{Float64}) = 9007199254740992. maxintfloat(::Type{Float32}) = Float32(16777216.) maxintfloat(::Type{Float16}) = Float16(2048f0) maxintfloat(x::T) where {T<:AbstractFloat} = maxintfloat(T) """ maxintfloat(T, S) The largest consecutive integer representable in the given floating-point type `T` that also does not exceed the maximum integer representable by the integer type `S`. Equivalently, it is the minimum of `maxintfloat(T)` and [`typemax(S)`](@ref). """ maxintfloat(::Type{S}, ::Type{T}) where {S<:AbstractFloat, T<:Integer} = min(maxintfloat(S), S(typemax(T))) maxintfloat() = maxintfloat(Float64) isinteger(x::AbstractFloat) = (x - trunc(x) == 0) """ round([T,] x, [r::RoundingMode]) round(x, [r::RoundingMode]; digits::Integer=0, base = 10) round(x, [r::RoundingMode]; sigdigits::Integer, base = 10) Rounds the number `x`. Without keyword arguments, `x` is rounded to an integer value, returning a value of type `T`, or of the same type of `x` if no `T` is provided. An [`InexactError`](@ref) will be thrown if the value is not representable by `T`, similar to [`convert`](@ref). If the `digits` keyword argument is provided, it rounds to the specified number of digits after the decimal place (or before if negative), in base `base`. If the `sigdigits` keyword argument is provided, it rounds to the specified number of significant digits, in base `base`. The [`RoundingMode`](@ref) `r` controls the direction of the rounding; the default is [`RoundNearest`](@ref), which rounds to the nearest integer, with ties (fractional values of 0.5) being rounded to the nearest even integer. Note that `round` may give incorrect results if the global rounding mode is changed (see [`rounding`](@ref)). # Examples ```jldoctest julia> round(1.7) 2.0 julia> round(Int, 1.7) 2 julia> round(1.5) 2.0 julia> round(2.5) 2.0 julia> round(pi; digits=2) 3.14 julia> round(pi; digits=3, base=2) 3.125 julia> round(123.456; sigdigits=2) 120.0 julia> round(357.913; sigdigits=4, base=2) 352.0 ``` !!! note Rounding to specified digits in bases other than 2 can be inexact when operating on binary floating point numbers. For example, the [`Float64`](@ref) value represented by `1.15` is actually *less* than 1.15, yet will be rounded to 1.2. For example: ```jldoctest julia> x = 1.15 1.15 julia> big(1.15) 1.149999999999999911182158029987476766109466552734375 julia> x < 115//100 true julia> round(x, digits=1) 1.2 ``` # Extensions To extend `round` to new numeric types, it is typically sufficient to define `Base.round(x::NewType, r::RoundingMode)`. """ round(T::Type, x) function round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} r != RoundToZero && (x = round(x,r)) trunc(T, x) end # NOTE: this relies on the current keyword dispatch behaviour (#9498). function round(x::Real, r::RoundingMode=RoundNearest; digits::Union{Nothing,Integer}=nothing, sigdigits::Union{Nothing,Integer}=nothing, base::Union{Nothing,Integer}=nothing) if digits === nothing if sigdigits === nothing if base === nothing # avoid recursive calls throw(MethodError(round, (x,r))) else round(x,r) # or throw(ArgumentError("`round` cannot use `base` argument without `digits` or `sigdigits` arguments.")) end else isfinite(x) || return float(x) _round_sigdigits(x, r, sigdigits, base === nothing ? 10 : base) end else if sigdigits === nothing isfinite(x) || return float(x) _round_digits(x, r, digits, base === nothing ? 10 : base) else throw(ArgumentError("`round` cannot use both `digits` and `sigdigits` arguments.")) end end end trunc(x::Real; kwargs...) = round(x, RoundToZero; kwargs...) floor(x::Real; kwargs...) = round(x, RoundDown; kwargs...) ceil(x::Real; kwargs...) = round(x, RoundUp; kwargs...) round(x::Integer, r::RoundingMode) = x # round x to multiples of 1/invstep function _round_invstep(x, invstep, r::RoundingMode) y = round(x * invstep, r) / invstep if !isfinite(y) return x end return y end # round x to multiples of 1/(invstepsqrt^2) # Using square root of step prevents overflowing function _round_invstepsqrt(x, invstepsqrt, r::RoundingMode) y = round((x * invstepsqrt) * invstepsqrt, r) / invstepsqrt / invstepsqrt if !isfinite(y) return x end return y end # round x to multiples of step function _round_step(x, step, r::RoundingMode) # TODO: use div with rounding mode y = round(x / step, r) * step if !isfinite(y) if x > 0 return (r == RoundUp ? oftype(x, Inf) : zero(x)) elseif x < 0 return (r == RoundDown ? -oftype(x, Inf) : -zero(x)) else return x end end return y end function _round_digits(x, r::RoundingMode, digits::Integer, base) fx = float(x) if digits >= 0 invstep = oftype(fx, base)^digits if isfinite(invstep) return _round_invstep(fx, invstep, r) else invstepsqrt = oftype(fx, base)^oftype(fx, digits/2) return _round_invstepsqrt(fx, invstepsqrt, r) end else step = oftype(fx, base)^-digits return _round_step(fx, step, r) end end hidigit(x::Integer, base) = ndigits0z(x, base) function hidigit(x::AbstractFloat, base) iszero(x) && return 0 if base == 10 return 1 + floor(Int, log10(abs(x))) elseif base == 2 return 1 + exponent(x) else return 1 + floor(Int, log(base, abs(x))) end end hidigit(x::Real, base) = hidigit(float(x), base) function _round_sigdigits(x, r::RoundingMode, sigdigits::Integer, base) h = hidigit(x, base) _round_digits(x, r, sigdigits-h, base) end # C-style round function round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway}) y = trunc(x) ifelse(x==y,y,trunc(2*x-y)) end # Java-style round function round(x::T, ::RoundingMode{:NearestTiesUp}) where {T <: AbstractFloat} copysign(floor((x + (T(0.25) - eps(T(0.5)))) + (T(0.25) + eps(T(0.5)))), x) end function Base.round(x::AbstractFloat, ::typeof(RoundFromZero)) signbit(x) ? round(x, RoundDown) : round(x, RoundUp) end # isapprox: approximate equality of numbers """ isapprox(x, y; atol::Real=0, rtol::Real=atol>0 ? 0 : โˆšeps, nans::Bool=false[, norm::Function]) Inexact equality comparison. Two numbers compare equal if their relative distance *or* their absolute distance is within tolerance bounds: `isapprox` returns `true` if `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))`. The default `atol` (absolute tolerance) is zero and the default `rtol` (relative tolerance) depends on the types of `x` and `y`. The keyword argument `nans` determines whether or not NaN values are considered equal (defaults to false). For real or complex floating-point values, if an `atol > 0` is not specified, `rtol` defaults to the square root of [`eps`](@ref) of the type of `x` or `y`, whichever is bigger (least precise). This corresponds to requiring equality of about half of the significant digits. Otherwise, e.g. for integer arguments or if an `atol > 0` is supplied, `rtol` defaults to zero. The `norm` keyword defaults to `abs` for numeric `(x,y)` and to `LinearAlgebra.norm` for arrays (where an alternative `norm` choice is sometimes useful). When `x` and `y` are arrays, if `norm(x-y)` is not finite (i.e. `ยฑInf` or `NaN`), the comparison falls back to checking whether all elements of `x` and `y` are approximately equal component-wise. The binary operator `โ‰ˆ` is equivalent to `isapprox` with the default arguments, and `x โ‰‰ y` is equivalent to `!isapprox(x,y)`. Note that `x โ‰ˆ 0` (i.e., comparing to zero with the default tolerances) is equivalent to `x == 0` since the default `atol` is `0`. In such cases, you should either supply an appropriate `atol` (or use `norm(x) โ‰ค atol`) or rearrange your code (e.g. use `x โ‰ˆ y` rather than `x - y โ‰ˆ 0`). It is not possible to pick a nonzero `atol` automatically because it depends on the overall scaling (the "units") of your problem: for example, in `x - y โ‰ˆ 0`, `atol=1e-9` is an absurdly small tolerance if `x` is the [radius of the Earth](https://en.wikipedia.org/wiki/Earth_radius) in meters, but an absurdly large tolerance if `x` is the [radius of a Hydrogen atom](https://en.wikipedia.org/wiki/Bohr_radius) in meters. !!! compat "Julia 1.6" Passing the `norm` keyword argument when comparing numeric (non-array) arguments requires Julia 1.6 or later. # Examples ```jldoctest julia> isapprox(0.1, 0.15; atol=0.05) true julia> isapprox(0.1, 0.15; rtol=0.34) true julia> isapprox(0.1, 0.15; rtol=0.33) false julia> 0.1 + 1e-10 โ‰ˆ 0.1 true julia> 1e-10 โ‰ˆ 0 false julia> isapprox(1e-10, 0, atol=1e-8) true julia> isapprox([10.0^9, 1.0], [10.0^9, 2.0]) # using `norm` true ``` """ function isapprox(x::Number, y::Number; atol::Real=0, rtol::Real=rtoldefault(x,y,atol), nans::Bool=false, norm::Function=abs) xโ€ฒ, yโ€ฒ = promote(x, y) # to avoid integer overflow x == y || (isfinite(x) && isfinite(y) && norm(x-y) <= max(atol, rtol*max(norm(xโ€ฒ), norm(yโ€ฒ)))) || (nans && isnan(x) && isnan(y)) end function isapprox(x::Integer, y::Integer; atol::Real=0, rtol::Real=rtoldefault(x,y,atol), nans::Bool=false, norm::Function=abs) if norm === abs && atol < 1 && rtol == 0 return x == y else return norm(x - y) <= max(atol, rtol*max(norm(x), norm(y))) end end """ isapprox(x; kwargs...) / โ‰ˆ(x; kwargs...) Create a function that compares its argument to `x` using `โ‰ˆ`, i.e. a function equivalent to `y -> y โ‰ˆ x`. The keyword arguments supported here are the same as those in the 2-argument `isapprox`. !!! compat "Julia 1.5" This method requires Julia 1.5 or later. """ isapprox(y; kwargs...) = x -> isapprox(x, y; kwargs...) const โ‰ˆ = isapprox """ x โ‰‰ y This is equivalent to `!isapprox(x,y)` (see [`isapprox`](@ref)). """ โ‰‰(args...; kws...) = !โ‰ˆ(args...; kws...) # default tolerance arguments rtoldefault(::Type{T}) where {T<:AbstractFloat} = sqrt(eps(T)) rtoldefault(::Type{<:Real}) = 0 function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where {T<:Number,S<:Number} rtol = max(rtoldefault(real(T)),rtoldefault(real(S))) return atol > 0 ? zero(rtol) : rtol end # fused multiply-add """ fma(x, y, z) Computes `x*y+z` without rounding the intermediate result `x*y`. On some systems this is significantly more expensive than `x*y+z`. `fma` is used to improve accuracy in certain algorithms. See [`muladd`](@ref). """ function fma end function fma_emulated(a::Float32, b::Float32, c::Float32)::Float32 ab = Float64(a) * b res = ab+c reinterpret(UInt64, res)&0x1fff_ffff!=0x1000_0000 && return res # yes error compensation is necessary. It sucks reslo = abs(c)>abs(ab) ? ab-(res - c) : c-(res - ab) res = iszero(reslo) ? res : (signbit(reslo) ? prevfloat(res) : nextfloat(res)) return res end """ Splits a Float64 into a hi bit and a low bit where the high bit has 27 trailing 0s and the low bit has 26 trailing 0s""" @inline function splitbits(x::Float64) hi = reinterpret(Float64, reinterpret(UInt64, x) & 0xffff_ffff_f800_0000) return hi, x-hi end function twomul(a::Float64, b::Float64) ahi, alo = splitbits(a) bhi, blo = splitbits(b) abhi = a*b blohi, blolo = splitbits(blo) ablo = alo*blohi - (((abhi - ahi*bhi) - alo*bhi) - ahi*blo) + blolo*alo return abhi, ablo end function fma_emulated(a::Float64, b::Float64,c::Float64) abhi, ablo = @inline twomul(a,b) if !isfinite(abhi+c) || isless(abs(abhi), nextfloat(0x1p-969)) || issubnormal(a) || issubnormal(b) aandbfinite = isfinite(a) && isfinite(b) if !(isfinite(c) && aandbfinite) return aandbfinite ? c : abhi+c end (iszero(a) || iszero(b)) && return abhi+c # The checks above satisfy exponent's nothrow precondition bias = Math._exponent_finite_nonzero(a) + Math._exponent_finite_nonzero(b) c_denorm = ldexp(c, -bias) if isfinite(c_denorm) # rescale a and b to [1,2), equivalent to ldexp(a, -exponent(a)) issubnormal(a) && (a *= 0x1p52) issubnormal(b) && (b *= 0x1p52) a = reinterpret(Float64, (reinterpret(UInt64, a) & ~Base.exponent_mask(Float64)) | Base.exponent_one(Float64)) b = reinterpret(Float64, (reinterpret(UInt64, b) & ~Base.exponent_mask(Float64)) | Base.exponent_one(Float64)) c = c_denorm abhi, ablo = twomul(a,b) # abhi <= 4 -> isfinite(r) (ฮฑ) r = abhi+c # s โ‰ˆ 0 (ฮฒ) s = (abs(abhi) > abs(c)) ? (abhi-r+c+ablo) : (c-r+abhi+ablo) # ฮฑ โฉ“ ฮฒ -> isfinite(sumhi) (ฮณ) sumhi = r+s # If result is subnormal, ldexp will cause double rounding because subnormals have fewer mantisa bits. # As such, we need to check whether round to even would lead to double rounding and manually round sumhi to avoid it. if issubnormal(ldexp(sumhi, bias)) sumlo = r-sumhi+s # finite: See ฮณ # non-zero: If sumhi == ยฑ0., then ldexp(sumhi, bias) == ยฑ0, # so we don't take this branch. bits_lost = -bias-Math._exponent_finite_nonzero(sumhi)-1022 sumhiInt = reinterpret(UInt64, sumhi) if (bits_lost != 1) โŠป (sumhiInt&1 == 1) sumhi = nextfloat(sumhi, cmp(sumlo,0)) end end return ldexp(sumhi, bias) end isinf(abhi) && signbit(c) == signbit(a*b) && return abhi # fall through end r = abhi+c s = (abs(abhi) > abs(c)) ? (abhi-r+c+ablo) : (c-r+abhi+ablo) return r+s end fma_llvm(x::Float32, y::Float32, z::Float32) = fma_float(x, y, z) fma_llvm(x::Float64, y::Float64, z::Float64) = fma_float(x, y, z) # Disable LLVM's fma if it is incorrect, e.g. because LLVM falls back # onto a broken system libm; if so, use a software emulated fma @assume_effects :consistent fma(x::Float32, y::Float32, z::Float32) = Core.Intrinsics.have_fma(Float32) ? fma_llvm(x,y,z) : fma_emulated(x,y,z) @assume_effects :consistent fma(x::Float64, y::Float64, z::Float64) = Core.Intrinsics.have_fma(Float64) ? fma_llvm(x,y,z) : fma_emulated(x,y,z) function fma(a::Float16, b::Float16, c::Float16) Float16(muladd(Float32(a), Float32(b), Float32(c))) #don't use fma if the hardware doesn't have it. end # This is necessary at least on 32-bit Intel Linux, since fma_llvm may # have called glibc, and some broken glibc fma implementations don't # properly restore the rounding mode Rounding.setrounding_raw(Float32, Rounding.JL_FE_TONEAREST) Rounding.setrounding_raw(Float64, Rounding.JL_FE_TONEAREST) K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/math.jl˜ฌ# This file is a part of Julia. License is MIT: https://julialang.org/license module Math export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot, sech, csch, coth, asech, acsch, acoth, sinpi, cospi, sincospi, tanpi, sinc, cosc, cosd, cotd, cscd, secd, sind, tand, sincosd, acosd, acotd, acscd, asecd, asind, atand, rad2deg, deg2rad, log, log2, log10, log1p, exponent, exp, exp2, exp10, expm1, cbrt, sqrt, fourthroot, significand, hypot, max, min, minmax, ldexp, frexp, clamp, clamp!, modf, ^, mod2pi, rem2pi, @evalpoly, evalpoly import .Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sqrt, log2, log10, max, min, minmax, ^, exp2, muladd, rem, exp10, expm1, log1p, @constprop, @assume_effects using .Base: sign_mask, exponent_mask, exponent_one, exponent_half, uinttype, significand_mask, significand_bits, exponent_bits, exponent_bias, exponent_max, exponent_raw_max using Core.Intrinsics: sqrt_llvm using .Base: IEEEFloat @noinline function throw_complex_domainerror(f::Symbol, x) throw(DomainError(x, LazyString(f," was called with a negative real argument but will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) end @noinline function throw_complex_domainerror_neg1(f::Symbol, x) throw(DomainError(x, LazyString(f," was called with a real argument < -1 but will only return a complex result if called with a complex argument. Try ", f,"(Complex(x))."))) end @noinline function throw_exp_domainerror(x) throw(DomainError(x, LazyString( "Exponentiation yielding a complex result requires a ", "complex argument.\nReplace x^y with (x+0im)^y, ", "Complex(x)^y, or similar."))) end # non-type specific math functions function two_mul(x::T, y::T) where {T<:Number} xy = x*y xy, fma(x, y, -xy) end @assume_effects :consistent @inline function two_mul(x::Float64, y::Float64) if Core.Intrinsics.have_fma(Float64) xy = x*y return xy, fma(x, y, -xy) end return Base.twomul(x,y) end @assume_effects :consistent @inline function two_mul(x::T, y::T) where T<: Union{Float16, Float32} if Core.Intrinsics.have_fma(T) xy = x*y return xy, fma(x, y, -xy) end xy = widen(x)*y Txy = T(xy) return Txy, T(xy-Txy) end """ clamp(x, lo, hi) Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments are promoted to a common type. See also [`clamp!`](@ref), [`min`](@ref), [`max`](@ref). !!! compat "Julia 1.3" `missing` as the first argument requires at least Julia 1.3. # Examples ```jldoctest julia> clamp.([pi, 1.0, big(10)], 2.0, 9.0) 3-element Vector{BigFloat}: 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 2.0 9.0 julia> clamp.([11, 8, 5], 10, 6) # an example where lo > hi 3-element Vector{Int64}: 6 6 10 ``` """ clamp(x::X, lo::L, hi::H) where {X,L,H} = ifelse(x > hi, convert(promote_type(X,L,H), hi), ifelse(x < lo, convert(promote_type(X,L,H), lo), convert(promote_type(X,L,H), x))) """ clamp(x, T)::T Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. See also [`trunc`](@ref). # Examples ```jldoctest julia> clamp(200, Int8) 127 julia> clamp(-200, Int8) -128 julia> trunc(Int, 4pi^2) 39 ``` """ clamp(x, ::Type{T}) where {T<:Integer} = clamp(x, typemin(T), typemax(T)) % T """ clamp!(array::AbstractArray, lo, hi) Restrict values in `array` to the specified range, in-place. See also [`clamp`](@ref). !!! compat "Julia 1.3" `missing` entries in `array` require at least Julia 1.3. # Examples ```jldoctest julia> row = collect(-4:4)'; julia> clamp!(row, 0, Inf) 1ร—9 adjoint(::Vector{Int64}) with eltype Int64: 0 0 0 0 0 1 2 3 4 julia> clamp.((-4:4)', 0, Inf) 1ร—9 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 1.0 2.0 3.0 4.0 ``` """ function clamp!(x::AbstractArray, lo, hi) @inbounds for i in eachindex(x) x[i] = clamp(x[i], lo, hi) end x end """ clamp(x::Integer, r::AbstractUnitRange) Clamp `x` to lie within range `r`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. """ clamp(x::Integer, r::AbstractUnitRange{<:Integer}) = clamp(x, first(r), last(r)) """ evalpoly(x, p) Evaluate the polynomial ``\\sum_k x^{k-1} p[k]`` for the coefficients `p[1]`, `p[2]`, ...; that is, the coefficients are given in ascending order by power of `x`. Loops are unrolled at compile time if the number of coefficients is statically known, i.e. when `p` is a `Tuple`. This function generates efficient code using Horner's method if `x` is real, or using a Goertzel-like [^DK62] algorithm if `x` is complex. [^DK62]: Donald Knuth, Art of Computer Programming, Volume 2: Seminumerical Algorithms, Sec. 4.6.4. !!! compat "Julia 1.4" This function requires Julia 1.4 or later. # Example ```jldoctest julia> evalpoly(2, (1, 2, 3)) 17 ``` """ function evalpoly(x, p::Tuple) if @generated N = length(p.parameters::Core.SimpleVector) ex = :(p[end]) for i in N-1:-1:1 ex = :(muladd(x, $ex, p[$i])) end ex else _evalpoly(x, p) end end evalpoly(x, p::AbstractVector) = _evalpoly(x, p) function _evalpoly(x, p) Base.require_one_based_indexing(p) N = length(p) ex = p[end] for i in N-1:-1:1 ex = muladd(x, ex, p[i]) end ex end function evalpoly(z::Complex, p::Tuple) if @generated N = length(p.parameters) a = :(p[end]) b = :(p[end-1]) as = [] for i in N-2:-1:1 ai = Symbol("a", i) push!(as, :($ai = $a)) a = :(muladd(r, $ai, $b)) b = :(muladd(-s, $ai, p[$i])) end ai = :a0 push!(as, :($ai = $a)) C = Expr(:block, :(x = real(z)), :(y = imag(z)), :(r = x + x), :(s = muladd(x, x, y*y)), as..., :(muladd($ai, z, $b))) else _evalpoly(z, p) end end evalpoly(z::Complex, p::Tuple{<:Any}) = p[1] evalpoly(z::Complex, p::AbstractVector) = _evalpoly(z, p) function _evalpoly(z::Complex, p) Base.require_one_based_indexing(p) length(p) == 1 && return p[1] N = length(p) a = p[end] b = p[end-1] x = real(z) y = imag(z) r = 2x s = muladd(x, x, y*y) for i in N-2:-1:1 ai = a a = muladd(r, ai, b) b = muladd(-s, ai, p[i]) end ai = a muladd(ai, z, b) end """ @horner(x, p...) Evaluate `p[1] + x * (p[2] + x * (....))`, i.e. a polynomial via Horner's rule. See also [`@evalpoly`](@ref), [`evalpoly`](@ref). """ macro horner(x, p...) xesc, pesc = esc(x), esc.(p) :(invoke(evalpoly, Tuple{Any, Tuple}, $xesc, ($(pesc...),))) end # Evaluate p[1] + z*p[2] + z^2*p[3] + ... + z^(n-1)*p[n]. This uses # Horner's method if z is real, but for complex z it uses a more # efficient algorithm described in Knuth, TAOCP vol. 2, section 4.6.4, # equation (3). """ @evalpoly(z, c...) Evaluate the polynomial ``\\sum_k z^{k-1} c[k]`` for the coefficients `c[1]`, `c[2]`, ...; that is, the coefficients are given in ascending order by power of `z`. This macro expands to efficient inline code that uses either Horner's method or, for complex `z`, a more efficient Goertzel-like algorithm. See also [`evalpoly`](@ref). # Examples ```jldoctest julia> @evalpoly(3, 1, 0, 1) 10 julia> @evalpoly(2, 1, 0, 1) 5 julia> @evalpoly(2, 1, 1, 1) 7 ``` """ macro evalpoly(z, p...) zesc, pesc = esc(z), esc.(p) :(evalpoly($zesc, ($(pesc...),))) end # polynomial evaluation using compensated summation. # much more accurate, especially when lo can be combined with other rounding errors Base.@assume_effects :terminates_locally @inline function exthorner(x, p::Tuple) hi, lo = p[end], zero(x) for i in length(p)-1:-1:1 pi = getfield(p, i) # needed to prove consistency prod, err = two_mul(hi,x) hi = pi+prod lo = fma(lo, x, prod - (hi - pi) + err) end return hi, lo end """ rad2deg(x) Convert `x` from radians to degrees. See also [`deg2rad`](@ref). # Examples ```jldoctest julia> rad2deg(pi) 180.0 ``` """ rad2deg(z::AbstractFloat) = z * (180 / oftype(z, pi)) """ deg2rad(x) Convert `x` from degrees to radians. See also [`rad2deg`](@ref), [`sind`](@ref), [`pi`](@ref). # Examples ```jldoctest julia> deg2rad(90) 1.5707963267948966 ``` """ deg2rad(z::AbstractFloat) = z * (oftype(z, pi) / 180) rad2deg(z::Real) = rad2deg(float(z)) deg2rad(z::Real) = deg2rad(float(z)) rad2deg(z::Number) = (z/pi)*180 deg2rad(z::Number) = (z*pi)/180 log(b::T, x::T) where {T<:Number} = log(x)/log(b) """ log(b,x) Compute the base `b` logarithm of `x`. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> log(4,8) 1.5 julia> log(4,2) 0.5 julia> log(-2, 3) ERROR: DomainError with -2.0: log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] julia> log(2, -3) ERROR: DomainError with -3.0: log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] ``` !!! note If `b` is a power of 2 or 10, [`log2`](@ref) or [`log10`](@ref) should be used, as these will typically be faster and more accurate. For example, ```jldoctest julia> log(100,1000000) 2.9999999999999996 julia> log10(1000000)/2 3.0 ``` """ log(b::Number, x::Number) = log(promote(b,x)...) # type specific math functions const libm = Base.libm_name # functions with no domain error """ sinh(x) Compute hyperbolic sine of `x`. """ sinh(x::Number) """ cosh(x) Compute hyperbolic cosine of `x`. """ cosh(x::Number) """ tanh(x) Compute hyperbolic tangent of `x`. See also [`tan`](@ref), [`atanh`](@ref). # Examples ```jldoctest julia> tanh.(-3:3f0) # Here 3f0 isa Float32 7-element Vector{Float32}: -0.9950548 -0.9640276 -0.7615942 0.0 0.7615942 0.9640276 0.9950548 julia> tan.(im .* (1:3)) 3-element Vector{ComplexF64}: 0.0 + 0.7615941559557649im 0.0 + 0.9640275800758169im 0.0 + 0.9950547536867306im ``` """ tanh(x::Number) """ atan(y) atan(y, x) Compute the inverse tangent of `y` or `y/x`, respectively. For one argument, this is the angle in radians between the positive *x*-axis and the point (1, *y*), returning a value in the interval ``[-\\pi/2, \\pi/2]``. For two arguments, this is the angle in radians between the positive *x*-axis and the point (*x*, *y*), returning a value in the interval ``[-\\pi, \\pi]``. This corresponds to a standard [`atan2`](https://en.wikipedia.org/wiki/Atan2) function. Note that by convention `atan(0.0,x)` is defined as ``\\pi`` and `atan(-0.0,x)` is defined as ``-\\pi`` when `x < 0`. See also [`atand`](@ref) for degrees. # Examples ```jldoctest julia> rad2deg(atan(-1/โˆš3)) -30.000000000000004 julia> rad2deg(atan(-1, โˆš3)) -30.000000000000004 julia> rad2deg(atan(1, -โˆš3)) 150.0 ``` """ atan(x::Number) """ asinh(x) Compute the inverse hyperbolic sine of `x`. """ asinh(x::Number) # utility for converting NaN return to DomainError # the branch in nan_dom_err prevents its callers from inlining, so be sure to force it # until the heuristics can be improved @inline nan_dom_err(out, x) = isnan(out) & !isnan(x) ? throw(DomainError(x, "NaN result for non-NaN input.")) : out # functions that return NaN on non-NaN argument for domain error """ sin(x) Compute sine of `x`, where `x` is in radians. See also [`sind`](@ref), [`sinpi`](@ref), [`sincos`](@ref), [`cis`](@ref), [`asin`](@ref). # Examples ```jldoctest julia> round.(sin.(range(0, 2pi, length=9)'), digits=3) 1ร—9 Matrix{Float64}: 0.0 0.707 1.0 0.707 0.0 -0.707 -1.0 -0.707 -0.0 julia> sind(45) 0.7071067811865476 julia> sinpi(1/4) 0.7071067811865475 julia> round.(sincos(pi/6), digits=3) (0.5, 0.866) julia> round(cis(pi/6), digits=3) 0.866 + 0.5im julia> round(exp(im*pi/6), digits=3) 0.866 + 0.5im ``` """ sin(x::Number) """ cos(x) Compute cosine of `x`, where `x` is in radians. See also [`cosd`](@ref), [`cospi`](@ref), [`sincos`](@ref), [`cis`](@ref). """ cos(x::Number) """ tan(x) Compute tangent of `x`, where `x` is in radians. """ tan(x::Number) """ asin(x) Compute the inverse sine of `x`, where the output is in radians. See also [`asind`](@ref) for output in degrees. # Examples ```jldoctest julia> asin.((0, 1/2, 1)) (0.0, 0.5235987755982989, 1.5707963267948966) julia> asind.((0, 1/2, 1)) (0.0, 30.000000000000004, 90.0) ``` """ asin(x::Number) """ acos(x) Compute the inverse cosine of `x`, where the output is in radians """ acos(x::Number) """ acosh(x) Compute the inverse hyperbolic cosine of `x`. """ acosh(x::Number) """ atanh(x) Compute the inverse hyperbolic tangent of `x`. """ atanh(x::Number) """ log(x) Compute the natural logarithm of `x`. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. Use complex negative arguments to obtain complex results. See also [`โ„ฏ`](@ref), [`log1p`](@ref), [`log2`](@ref), [`log10`](@ref). # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> log(2) 0.6931471805599453 julia> log(-3) ERROR: DomainError with -3.0: log was called with a negative real argument but will only return a complex result if called with a complex argument. Try log(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] julia> log.(exp.(-1:1)) 3-element Vector{Float64}: -1.0 0.0 1.0 ``` """ log(x::Number) """ log2(x) Compute the logarithm of `x` to base 2. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. See also: [`exp2`](@ref), [`ldexp`](@ref), [`ispow2`](@ref). # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> log2(4) 2.0 julia> log2(10) 3.321928094887362 julia> log2(-2) ERROR: DomainError with -2.0: log2 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log2(Complex(x)). Stacktrace: [1] throw_complex_domainerror(f::Symbol, x::Float64) at ./math.jl:31 [...] julia> log2.(2.0 .^ (-1:1)) 3-element Vector{Float64}: -1.0 0.0 1.0 ``` """ log2(x) """ log10(x) Compute the logarithm of `x` to base 10. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> log10(100) 2.0 julia> log10(2) 0.3010299956639812 julia> log10(-2) ERROR: DomainError with -2.0: log10 was called with a negative real argument but will only return a complex result if called with a complex argument. Try log10(Complex(x)). Stacktrace: [1] throw_complex_domainerror(f::Symbol, x::Float64) at ./math.jl:31 [...] ``` """ log10(x) """ log1p(x) Accurate natural logarithm of `1+x`. Throws [`DomainError`](@ref) for [`Real`](@ref) arguments less than -1. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> log1p(-0.5) -0.6931471805599453 julia> log1p(0) 0.0 julia> log1p(-2) ERROR: DomainError with -2.0: log1p was called with a real argument < -1 but will only return a complex result if called with a complex argument. Try log1p(Complex(x)). Stacktrace: [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 [...] ``` """ log1p(x) @inline function sqrt(x::Union{Float32,Float64}) x < zero(x) && throw_complex_domainerror(:sqrt, x) sqrt_llvm(x) end """ sqrt(x) Return ``\\sqrt{x}``. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. Use complex negative arguments instead. The prefix operator `โˆš` is equivalent to `sqrt`. See also: [`hypot`](@ref). # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> sqrt(big(81)) 9.0 julia> sqrt(big(-81)) ERROR: DomainError with -81.0: NaN result for non-NaN input. Stacktrace: [1] sqrt(::BigFloat) at ./mpfr.jl:501 [...] julia> sqrt(big(complex(-81))) 0.0 + 9.0im julia> .โˆš(1:4) 4-element Vector{Float64}: 1.0 1.4142135623730951 1.7320508075688772 2.0 ``` """ sqrt(x) """ fourthroot(x) Return the fourth root of `x` by applying `sqrt` twice successively. """ fourthroot(x::Number) = sqrt(sqrt(x)) """ hypot(x, y) Compute the hypotenuse ``\\sqrt{|x|^2+|y|^2}`` avoiding overflow and underflow. This code is an implementation of the algorithm described in: An Improved Algorithm for `hypot(a,b)` by Carlos F. Borges The article is available online at arXiv at the link https://arxiv.org/abs/1904.09481 hypot(x...) Compute the hypotenuse ``\\sqrt{\\sum |x_i|^2}`` avoiding overflow and underflow. See also `norm` in the [`LinearAlgebra`](@ref man-linalg) standard library. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> a = Int64(10)^10; julia> hypot(a, a) 1.4142135623730951e10 julia> โˆš(a^2 + a^2) # a^2 overflows ERROR: DomainError with -2.914184810805068e18: sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] julia> hypot(3, 4im) 5.0 julia> hypot(-5.7) 5.7 julia> hypot(3, 4im, 12.0) 13.0 julia> using LinearAlgebra julia> norm([a, a, a, a]) == hypot(a, a, a, a) true ``` """ hypot(x::Number) = abs(float(x)) hypot(x::Number, y::Number) = _hypot(promote(float(x), y)...) hypot(x::Number, y::Number, xs::Number...) = _hypot(promote(float(x), y, xs...)) function _hypot(x, y) # preserves unit axu = abs(x) ayu = abs(y) # unitless ax = axu / oneunit(axu) ay = ayu / oneunit(ayu) # Return Inf if either or both inputs is Inf (Compliance with IEEE754) if isinf(ax) || isinf(ay) return typeof(axu)(Inf) end # Order the operands if ay > ax axu, ayu = ayu, axu ax, ay = ay, ax end # Widely varying operands if ay <= ax*sqrt(eps(typeof(ax))/2) #Note: This also gets ay == 0 return axu end # Operands do not vary widely scale = eps(typeof(ax))*sqrt(floatmin(ax)) #Rescaling constant if ax > sqrt(floatmax(ax)/2) ax = ax*scale ay = ay*scale scale = inv(scale) elseif ay < sqrt(floatmin(ax)) ax = ax/scale ay = ay/scale else scale = oneunit(scale) end h = sqrt(muladd(ax, ax, ay*ay)) # This branch is correctly rounded but requires a native hardware fma. if Core.Intrinsics.have_fma(typeof(h)) hsquared = h*h axsquared = ax*ax h -= (fma(-ay, ay, hsquared-axsquared) + fma(h, h,-hsquared) - fma(ax, ax, -axsquared))/(2*h) # This branch is within one ulp of correctly rounded. else if h <= 2*ay delta = h-ay h -= muladd(delta, delta-2*(ax-ay), ax*(2*delta - ax))/(2*h) else delta = h-ax h -= muladd(delta, delta, muladd(ay, (4*delta - ay), 2*delta*(ax - 2*ay)))/(2*h) end end return h*scale*oneunit(axu) end @inline function _hypot(x::Float32, y::Float32) if isinf(x) || isinf(y) return Inf32 end _x, _y = Float64(x), Float64(y) return Float32(sqrt(muladd(_x, _x, _y*_y))) end @inline function _hypot(x::Float16, y::Float16) if isinf(x) || isinf(y) return Inf16 end _x, _y = Float32(x), Float32(y) return Float16(sqrt(muladd(_x, _x, _y*_y))) end _hypot(x::ComplexF16, y::ComplexF16) = Float16(_hypot(ComplexF32(x), ComplexF32(y))) function _hypot(x::NTuple{N,<:Number}) where {N} maxabs = maximum(abs, x) if isnan(maxabs) && any(isinf, x) return typeof(maxabs)(Inf) elseif (iszero(maxabs) || isinf(maxabs)) return maxabs else return maxabs * sqrt(sum(y -> abs2(y / maxabs), x)) end end function _hypot(x::NTuple{N,<:IEEEFloat}) where {N} T = eltype(x) infT = convert(T, Inf) x = abs.(x) # doesn't change result but enables computational shortcuts # note: any() was causing this to not inline for N=3 but mapreduce() was not mapreduce(==(infT), |, x) && return infT # return Inf even if an argument is NaN maxabs = reinterpret(T, maximum(z -> reinterpret(Signed, z), x)) # for abs(::IEEEFloat) values, a ::BitInteger cast does not change the result maxabs > zero(T) || return maxabs # catch NaN before the @fastmath below, but also shortcut 0 since we can (remove if no more @fastmath below) scale,invscale = scaleinv(maxabs) # @fastmath(+) to allow reassociation (see #48129) add_fast(x, y) = Core.Intrinsics.add_float_fast(x, y) # @fastmath is not available during bootstrap return scale * sqrt(mapreduce(y -> abs2(y * invscale), add_fast, x)) end atan(y::Real, x::Real) = atan(promote(float(y),float(x))...) atan(y::T, x::T) where {T<:AbstractFloat} = Base.no_op_err("atan", T) _isless(x::T, y::T) where {T<:AbstractFloat} = (x < y) || (signbit(x) > signbit(y)) min(x::T, y::T) where {T<:AbstractFloat} = isnan(x) || ~isnan(y) && _isless(x, y) ? x : y max(x::T, y::T) where {T<:AbstractFloat} = isnan(x) || ~isnan(y) && _isless(y, x) ? x : y minmax(x::T, y::T) where {T<:AbstractFloat} = min(x, y), max(x, y) _isless(x::Float16, y::Float16) = signbit(widen(x) - widen(y)) const has_native_fminmax = Sys.ARCH === :aarch64 @static if has_native_fminmax @eval begin Base.@assume_effects :total @inline llvm_min(x::Float64, y::Float64) = ccall("llvm.minimum.f64", llvmcall, Float64, (Float64, Float64), x, y) Base.@assume_effects :total @inline llvm_min(x::Float32, y::Float32) = ccall("llvm.minimum.f32", llvmcall, Float32, (Float32, Float32), x, y) Base.@assume_effects :total @inline llvm_max(x::Float64, y::Float64) = ccall("llvm.maximum.f64", llvmcall, Float64, (Float64, Float64), x, y) Base.@assume_effects :total @inline llvm_max(x::Float32, y::Float32) = ccall("llvm.maximum.f32", llvmcall, Float32, (Float32, Float32), x, y) end end function min(x::T, y::T) where {T<:Union{Float32,Float64}} @static if has_native_fminmax return llvm_min(x,y) end diff = x - y argmin = ifelse(signbit(diff), x, y) anynan = isnan(x)|isnan(y) return ifelse(anynan, diff, argmin) end function max(x::T, y::T) where {T<:Union{Float32,Float64}} @static if has_native_fminmax return llvm_max(x,y) end diff = x - y argmax = ifelse(signbit(diff), y, x) anynan = isnan(x)|isnan(y) return ifelse(anynan, diff, argmax) end function minmax(x::T, y::T) where {T<:Union{Float32,Float64}} @static if has_native_fminmax return llvm_min(x, y), llvm_max(x, y) end diff = x - y sdiff = signbit(diff) min, max = ifelse(sdiff, x, y), ifelse(sdiff, y, x) anynan = isnan(x)|isnan(y) return ifelse(anynan, diff, min), ifelse(anynan, diff, max) end """ ldexp(x, n) Compute ``x \\times 2^n``. # Examples ```jldoctest julia> ldexp(5., 2) 20.0 ``` """ function ldexp(x::T, e::Integer) where T<:IEEEFloat xu = reinterpret(Unsigned, x) xs = xu & ~sign_mask(T) xs >= exponent_mask(T) && return x # NaN or Inf k = (xs >> significand_bits(T)) % Int if k == 0 # x is subnormal xs == 0 && return x # +-0 m = leading_zeros(xs) - exponent_bits(T) ys = xs << unsigned(m) xu = ys | (xu & sign_mask(T)) k = 1 - m # underflow, otherwise may have integer underflow in the following n + k e < -50000 && return flipsign(T(0.0), x) end # For cases where e of an Integer larger than Int make sure we properly # overflow/underflow; this is optimized away otherwise. if e > typemax(Int) return flipsign(T(Inf), x) elseif e < typemin(Int) return flipsign(T(0.0), x) end n = e % Int k += n # overflow, if k is larger than maximum possible exponent if k >= exponent_raw_max(T) return flipsign(T(Inf), x) end if k > 0 # normal case xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) return reinterpret(T, xu) else # subnormal case if k <= -significand_bits(T) # underflow # overflow, for the case of integer overflow in n + k e > 50000 && return flipsign(T(Inf), x) return flipsign(T(0.0), x) end k += significand_bits(T) # z = T(2.0) ^ (-significand_bits(T)) z = reinterpret(T, rem(exponent_bias(T)-significand_bits(T), uinttype(T)) << significand_bits(T)) xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) return z*reinterpret(T, xu) end end ldexp(x::Float16, q::Integer) = Float16(ldexp(Float32(x), q)) """ exponent(x) -> Int Returns the largest integer `y` such that `2^y โ‰ค abs(x)`. For a normalized floating-point number `x`, this corresponds to the exponent of `x`. # Examples ```jldoctest julia> exponent(8) 3 julia> exponent(64//1) 6 julia> exponent(6.5) 2 julia> exponent(16.0) 4 julia> exponent(3.142e-4) -12 ``` """ function exponent(x::T) where T<:IEEEFloat @noinline throw1(x) = throw(DomainError(x, "Cannot be NaN or Inf.")) @noinline throw2(x) = throw(DomainError(x, "Cannot be ยฑ0.0.")) xs = reinterpret(Unsigned, x) & ~sign_mask(T) xs >= exponent_mask(T) && throw1(x) k = Int(xs >> significand_bits(T)) if k == 0 # x is subnormal xs == 0 && throw2(x) m = leading_zeros(xs) - exponent_bits(T) k = 1 - m end return k - exponent_bias(T) end # Like exponent, but assumes the nothrow precondition. For # internal use only. Could be written as # @assume_effects :nothrow exponent() # but currently this form is easier on the compiler. function _exponent_finite_nonzero(x::T) where T<:IEEEFloat # @precond :nothrow !isnan(x) && !isinf(x) && !iszero(x) xs = reinterpret(Unsigned, x) & ~sign_mask(T) k = rem(xs >> significand_bits(T), Int) if k == 0 # x is subnormal m = leading_zeros(xs) - exponent_bits(T) k = 1 - m end return k - exponent_bias(T) end """ significand(x) Extract the significand (a.k.a. mantissa) of a floating-point number. If `x` is a non-zero finite number, then the result will be a number of the same type and sign as `x`, and whose absolute value is on the interval ``[1,2)``. Otherwise `x` is returned. # Examples ```jldoctest julia> significand(15.2) 1.9 julia> significand(-15.2) -1.9 julia> significand(-15.2) * 2^3 -15.2 julia> significand(-Inf), significand(Inf), significand(NaN) (-Inf, Inf, NaN) ``` """ function significand(x::T) where T<:IEEEFloat xu = reinterpret(Unsigned, x) xs = xu & ~sign_mask(T) xs >= exponent_mask(T) && return x # NaN or Inf if xs <= (~exponent_mask(T) & ~sign_mask(T)) # x is subnormal xs == 0 && return x # +-0 m = unsigned(leading_zeros(xs) - exponent_bits(T)) xs <<= m xu = xs | (xu & sign_mask(T)) end xu = (xu & ~exponent_mask(T)) | exponent_one(T) return reinterpret(T, xu) end """ frexp(val) Return `(x,exp)` such that `x` has a magnitude in the interval ``[1/2, 1)`` or 0, and `val` is equal to ``x \\times 2^{exp}``. # Examples ```jldoctest julia> frexp(12.8) (0.8, 4) ``` """ function frexp(x::T) where T<:IEEEFloat xu = reinterpret(Unsigned, x) xs = xu & ~sign_mask(T) xs >= exponent_mask(T) && return x, 0 # NaN or Inf k = Int(xs >> significand_bits(T)) if k == 0 # x is subnormal xs == 0 && return x, 0 # +-0 m = leading_zeros(xs) - exponent_bits(T) xs <<= unsigned(m) xu = xs | (xu & sign_mask(T)) k = 1 - m end k -= (exponent_bias(T) - 1) xu = (xu & ~exponent_mask(T)) | exponent_half(T) return reinterpret(T, xu), k end """ $(@__MODULE__).scaleinv(x) Compute `(scale, invscale)` where `scale` and `invscale` are non-subnormal (https://en.wikipedia.org/wiki/Subnormal_number) finite powers of two such that `scale * invscale == 1` and `scale` is roughly on the order of `abs(x)`. Inf, NaN, and zero inputs also result in finite nonzero outputs. These values are useful to scale computations to prevent overflow and underflow without round-off errors or division. UNSTABLE DETAIL: For `x isa IEEEFLoat`, `scale` is chosen to be the `prevpow(2,abs(x))` when possible, but is never less than floatmin(x) or greater than inv(floatmin(x)). `Inf` and `NaN` resolve to `inv(floatmin(x))`. This behavior is subject to change. # Examples ```jldoctest julia> $(@__MODULE__).scaleinv(7.5) (4.0, 0.25) ``` """ function scaleinv(x::T) where T<:IEEEFloat # by removing the sign and significand and restricting values to a limited range, # we can invert a number using bit-twiddling instead of division U = uinttype(T) umin = reinterpret(U, floatmin(T)) umax = reinterpret(U, inv(floatmin(T))) emask = exponent_mask(T) # used to strip sign and significand u = clamp(reinterpret(U, x) & emask, umin, umax) scale = reinterpret(T, u) invscale = reinterpret(T, umin + umax - u) # inv(scale) return scale, invscale end # NOTE: This `rem` method is adapted from the msun `remainder` and `remainderf` # functions, which are under the following license: # # Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. # # Developed at SunSoft, a Sun Microsystems, Inc. business. # Permission to use, copy, modify, and distribute this # software is freely granted, provided that this notice # is preserved. function rem(x::T, p::T, ::RoundingMode{:Nearest}) where T<:IEEEFloat (iszero(p) || !isfinite(x) || isnan(p)) && return T(NaN) x == p && return copysign(zero(T), x) oldx = x x = abs(rem(x, 2p)) # 2p may overflow but that's okay p = abs(p) if p < 2 * floatmin(T) # Check whether dividing p by 2 will underflow if 2x > p x -= p if 2x >= p x -= p end end else p_half = p / 2 if x > p_half x -= p if x >= p_half x -= p end end end return flipsign(x, oldx) end """ modf(x) Return a tuple `(fpart, ipart)` of the fractional and integral parts of a number. Both parts have the same sign as the argument. # Examples ```jldoctest julia> modf(3.5) (0.5, 3.0) julia> modf(-3.5) (-0.5, -3.0) ``` """ modf(x) = isinf(x) ? (flipsign(zero(x), x), x) : (rem(x, one(x)), trunc(x)) function modf(x::T) where T<:IEEEFloat isinf(x) && return (copysign(zero(T), x), x) ix = trunc(x) rx = copysign(x - ix, x) return (rx, ix) end # @constprop aggressive to help the compiler see the switch between the integer and float # variants for callers with constant `y` @constprop :aggressive function ^(x::Float64, y::Float64) xu = reinterpret(UInt64, x) xu == reinterpret(UInt64, 1.0) && return 1.0 # Exponents greater than this will always overflow or underflow. # Note that NaN can pass through this, but that will end up fine. if !(abs(y)<0x1.8p62) isnan(y) && return y y = sign(y)*0x1.8p62 end yint = unsafe_trunc(Int64, y) # This is actually safe since julia freezes the result y == yint && return @noinline x^yint 2*xu==0 && return abs(y)*Inf*(!(y>0)) # if x==0 x<0 && throw_exp_domainerror(x) # |y| is small enough that y isn't an integer !isfinite(x) && return x*(y>0 || isnan(x)) # x is inf or NaN if xu < (UInt64(1)<<52) # x is subnormal xu = reinterpret(UInt64, x * 0x1p52) # normalize x xu &= ~sign_mask(Float64) xu -= UInt64(52) << 52 # mess with the exponent end return pow_body(xu, y) end @inline function pow_body(xu::UInt64, y::Float64) logxhi,logxlo = Base.Math._log_ext(xu) xyhi, xylo = two_mul(logxhi,y) xylo = muladd(logxlo, y, xylo) hi = xyhi+xylo return Base.Math.exp_impl(hi, xylo-(hi-xyhi), Val(:โ„ฏ)) end @constprop :aggressive function ^(x::T, y::T) where T <: Union{Float16, Float32} x == 1 && return one(T) # Exponents greater than this will always overflow or underflow. # Note that NaN can pass through this, but that will end up fine. max_exp = T == Float16 ? T(3<<14) : T(0x1.Ap30) if !(abs(y)0 || isnan(x)) x==0 && return abs(y)*T(Inf)*(!(y>0)) return pow_body(x, y) end @inline function pow_body(x::T, y::T) where T <: Union{Float16, Float32} return T(exp2(log2(abs(widen(x))) * y)) end # compensated power by squaring @constprop :aggressive @inline function ^(x::Float64, n::Integer) n == 0 && return one(x) return pow_body(x, n) end @assume_effects :terminates_locally @noinline function pow_body(x::Float64, n::Integer) y = 1.0 xnlo = ynlo = 0.0 n == 3 && return x*x*x # keep compatibility with literal_pow if n < 0 rx = inv(x) n==-2 && return rx*rx #keep compatibility with literal_pow isfinite(x) && (xnlo = -fma(x, rx, -1.) * rx) x = rx n = -n end while n > 1 if n&1 > 0 err = muladd(y, xnlo, x*ynlo) y, ynlo = two_mul(x,y) ynlo += err end err = x*2*xnlo x, xnlo = two_mul(x, x) xnlo += err n >>>= 1 end err = muladd(y, xnlo, x*ynlo) return ifelse(isfinite(x) & isfinite(err), muladd(x, y, err), x*y) end function ^(x::Float32, n::Integer) n == -2 && return (i=inv(x); i*i) n == 3 && return x*x*x #keep compatibility with literal_pow n < 0 && return Float32(Base.power_by_squaring(inv(Float64(x)),-n)) Float32(Base.power_by_squaring(Float64(x),n)) end @inline ^(x::Float16, y::Integer) = Float16(Float32(x) ^ y) @inline literal_pow(::typeof(^), x::Float16, ::Val{p}) where {p} = Float16(literal_pow(^,Float32(x),Val(p))) ## rem2pi-related calculations ## function add22condh(xh::Float64, xl::Float64, yh::Float64, yl::Float64) # This algorithm, due to Dekker, computes the sum of two # double-double numbers and returns the high double. References: # [1] http://www.digizeitschriften.de/en/dms/img/?PID=GDZPPN001170007 # [2] https://doi.org/10.1007/BF01397083 r = xh+yh s = (abs(xh) > abs(yh)) ? (xh-r+yh+yl+xl) : (yh-r+xh+xl+yl) zh = r+s return zh end # multiples of pi/2, as double-double (ie with "tail") const pi1o2_h = 1.5707963267948966 # convert(Float64, pi * BigFloat(1/2)) const pi1o2_l = 6.123233995736766e-17 # convert(Float64, pi * BigFloat(1/2) - pi1o2_h) const pi2o2_h = 3.141592653589793 # convert(Float64, pi * BigFloat(1)) const pi2o2_l = 1.2246467991473532e-16 # convert(Float64, pi * BigFloat(1) - pi2o2_h) const pi3o2_h = 4.71238898038469 # convert(Float64, pi * BigFloat(3/2)) const pi3o2_l = 1.8369701987210297e-16 # convert(Float64, pi * BigFloat(3/2) - pi3o2_h) const pi4o2_h = 6.283185307179586 # convert(Float64, pi * BigFloat(2)) const pi4o2_l = 2.4492935982947064e-16 # convert(Float64, pi * BigFloat(2) - pi4o2_h) """ rem2pi(x, r::RoundingMode) Compute the remainder of `x` after integer division by `2ฯ€`, with the quotient rounded according to the rounding mode `r`. In other words, the quantity x - 2ฯ€*round(x/(2ฯ€),r) without any intermediate rounding. This internally uses a high precision approximation of 2ฯ€, and so will give a more accurate result than `rem(x,2ฯ€,r)` - if `r == RoundNearest`, then the result is in the interval ``[-ฯ€, ฯ€]``. This will generally be the most accurate result. See also [`RoundNearest`](@ref). - if `r == RoundToZero`, then the result is in the interval ``[0, 2ฯ€]`` if `x` is positive,. or ``[-2ฯ€, 0]`` otherwise. See also [`RoundToZero`](@ref). - if `r == RoundDown`, then the result is in the interval ``[0, 2ฯ€]``. See also [`RoundDown`](@ref). - if `r == RoundUp`, then the result is in the interval ``[-2ฯ€, 0]``. See also [`RoundUp`](@ref). # Examples ```jldoctest julia> rem2pi(7pi/4, RoundNearest) -0.7853981633974485 julia> rem2pi(7pi/4, RoundDown) 5.497787143782138 ``` """ function rem2pi end function rem2pi(x::Float64, ::RoundingMode{:Nearest}) isnan(x) && return x isinf(x) && return NaN abs(x) < pi && return x n,y = rem_pio2_kernel(x) if iseven(n) if n & 2 == 2 # n % 4 == 2: add/subtract pi if y.hi <= 0 return add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) else return add22condh(y.hi,y.lo,-pi2o2_h,-pi2o2_l) end else # n % 4 == 0: add 0 return y.hi+y.lo end else if n & 2 == 2 # n % 4 == 3: subtract pi/2 return add22condh(y.hi,y.lo,-pi1o2_h,-pi1o2_l) else # n % 4 == 1: add pi/2 return add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) end end end function rem2pi(x::Float64, ::RoundingMode{:ToZero}) isnan(x) && return x isinf(x) && return NaN ax = abs(x) ax <= 2*Float64(pi,RoundDown) && return x n,y = rem_pio2_kernel(ax) if iseven(n) if n & 2 == 2 # n % 4 == 2: add pi z = add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) else # n % 4 == 0: add 0 or 2pi if y.hi > 0 z = y.hi+y.lo else # negative: add 2pi z = add22condh(y.hi,y.lo,pi4o2_h,pi4o2_l) end end else if n & 2 == 2 # n % 4 == 3: add 3pi/2 z = add22condh(y.hi,y.lo,pi3o2_h,pi3o2_l) else # n % 4 == 1: add pi/2 z = add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) end end copysign(z,x) end function rem2pi(x::Float64, ::RoundingMode{:Down}) isnan(x) && return x isinf(x) && return NaN if x < pi4o2_h if x >= 0 return x elseif x > -pi4o2_h return add22condh(x,0.0,pi4o2_h,pi4o2_l) end end n,y = rem_pio2_kernel(x) if iseven(n) if n & 2 == 2 # n % 4 == 2: add pi return add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) else # n % 4 == 0: add 0 or 2pi if y.hi > 0 return y.hi+y.lo else # negative: add 2pi return add22condh(y.hi,y.lo,pi4o2_h,pi4o2_l) end end else if n & 2 == 2 # n % 4 == 3: add 3pi/2 return add22condh(y.hi,y.lo,pi3o2_h,pi3o2_l) else # n % 4 == 1: add pi/2 return add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) end end end function rem2pi(x::Float64, ::RoundingMode{:Up}) isnan(x) && return x isinf(x) && return NaN if x > -pi4o2_h if x <= 0 return x elseif x < pi4o2_h return add22condh(x,0.0,-pi4o2_h,-pi4o2_l) end end n,y = rem_pio2_kernel(x) if iseven(n) if n & 2 == 2 # n % 4 == 2: sub pi return add22condh(y.hi,y.lo,-pi2o2_h,-pi2o2_l) else # n % 4 == 0: sub 0 or 2pi if y.hi < 0 return y.hi+y.lo else # positive: sub 2pi return add22condh(y.hi,y.lo,-pi4o2_h,-pi4o2_l) end end else if n & 2 == 2 # n % 4 == 3: sub pi/2 return add22condh(y.hi,y.lo,-pi1o2_h,-pi1o2_l) else # n % 4 == 1: sub 3pi/2 return add22condh(y.hi,y.lo,-pi3o2_h,-pi3o2_l) end end end rem2pi(x::Float32, r::RoundingMode) = Float32(rem2pi(Float64(x), r)) rem2pi(x::Float16, r::RoundingMode) = Float16(rem2pi(Float64(x), r)) rem2pi(x::Int32, r::RoundingMode) = rem2pi(Float64(x), r) function rem2pi(x::Int64, r::RoundingMode) fx = Float64(x) fx == x || throw(ArgumentError("Int64 argument to rem2pi is too large: $x")) rem2pi(fx, r) end """ mod2pi(x) Modulus after division by `2ฯ€`, returning in the range ``[0,2ฯ€)``. This function computes a floating point representation of the modulus after division by numerically exact `2ฯ€`, and is therefore not exactly the same as `mod(x,2ฯ€)`, which would compute the modulus of `x` relative to division by the floating-point number `2ฯ€`. !!! note Depending on the format of the input value, the closest representable value to 2ฯ€ may be less than 2ฯ€. For example, the expression `mod2pi(2ฯ€)` will not return `0`, because the intermediate value of `2*ฯ€` is a `Float64` and `2*Float64(ฯ€) < 2*big(ฯ€)`. See [`rem2pi`](@ref) for more refined control of this behavior. # Examples ```jldoctest julia> mod2pi(9*pi/4) 0.7853981633974481 ``` """ mod2pi(x) = rem2pi(x,RoundDown) # generic fallback; for number types, promotion.jl does promotion """ muladd(x, y, z) Combined multiply-add: computes `x*y+z`, but allowing the add and multiply to be merged with each other or with surrounding operations for performance. For example, this may be implemented as an [`fma`](@ref) if the hardware supports it efficiently. The result can be different on different machines and can also be different on the same machine due to constant propagation or other optimizations. See [`fma`](@ref). # Examples ```jldoctest julia> muladd(3, 2, 1) 7 julia> 3 * 2 + 1 7 ``` """ muladd(x,y,z) = x*y+z # helper functions for Libm functionality """ highword(x) Return the high word of `x` as a `UInt32`. """ @inline highword(x::Float64) = highword(reinterpret(UInt64, x)) @inline highword(x::UInt64) = (x >>> 32) % UInt32 @inline highword(x::Float32) = reinterpret(UInt32, x) @inline fromhighword(::Type{Float64}, u::UInt32) = reinterpret(Float64, UInt64(u) << 32) @inline fromhighword(::Type{Float32}, u::UInt32) = reinterpret(Float32, u) """ poshighword(x) Return positive part of the high word of `x` as a `UInt32`. """ @inline poshighword(x::Float64) = poshighword(reinterpret(UInt64, x)) @inline poshighword(x::UInt64) = highword(x) & 0x7fffffff @inline poshighword(x::Float32) = highword(x) & 0x7fffffff # More special functions include("special/cbrt.jl") include("special/exp.jl") include("special/hyperbolic.jl") include("special/trig.jl") include("special/rem_pio2.jl") include("special/log.jl") # Float16 definitions for func in (:sin,:cos,:tan,:asin,:acos,:atan,:cosh,:tanh,:asinh,:acosh, :atanh,:log,:log2,:log10,:sqrt,:fourthroot,:log1p) @eval begin $func(a::Float16) = Float16($func(Float32(a))) $func(a::ComplexF16) = ComplexF16($func(ComplexF32(a))) end end for func in (:exp,:exp2,:exp10,:sinh) @eval $func(a::ComplexF16) = ComplexF16($func(ComplexF32(a))) end atan(a::Float16,b::Float16) = Float16(atan(Float32(a),Float32(b))) sincos(a::Float16) = Float16.(sincos(Float32(a))) for f in (:sin, :cos, :tan, :asin, :atan, :acos, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, :exp, :exp2, :exp10, :expm1, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt, :sinpi, :cospi, :sincospi, :tanpi) @eval function ($f)(x::Real) xf = float(x) x === xf && throw(MethodError($f, (x,))) return ($f)(xf) end @eval $(f)(::Missing) = missing end for f in (:atan, :hypot, :log) @eval $(f)(::Missing, ::Missing) = missing @eval $(f)(::Number, ::Missing) = missing @eval $(f)(::Missing, ::Number) = missing end exp2(x::AbstractFloat) = 2^x exp10(x::AbstractFloat) = 10^x clamp(::Missing, lo, hi) = missing fourthroot(::Missing) = missing end # module S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/cbrt.jl+# This file is a part of Julia. License is MIT: https://julialang.org/license # Float32/Float64 based on C implementations from FDLIBM (http://www.netlib.org/fdlibm/) # and FreeBSD: # ## ==================================================== ## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. ## ## Developed at SunPro, a Sun Microsystems, Inc. business. ## Permission to use, copy, modify, and distribute this ## software is freely granted, provided that this notice ## is preserved. ## ==================================================== ## Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. ## Debugged and optimized by Bruce D. Evans. """ cbrt(x::Real) Return the cube root of `x`, i.e. ``x^{1/3}``. Negative values are accepted (returning the negative real root when ``x < 0``). The prefix operator `โˆ›` is equivalent to `cbrt`. # Examples ```jldoctest julia> cbrt(big(27)) 3.0 julia> cbrt(big(-27)) -3.0 ``` """ cbrt(x::AbstractFloat) = x < 0 ? -(-x)^(1//3) : x^(1//3) """ _approx_cbrt(x) Approximate `cbrt` to 5 bits precision cbrt(2^e * (1+m)) โ‰ˆ 2^(eรท3) * (1 + (e%3+m)รท3) where: - `e` is integral and >= 0 - `m` is real and in [0, 1), - `รท` is integer division - `%` is integer remainder The RHS is always >= the LHS and has a maximum relative error of about 1 in 16. Adding a bias of -0.03306235651 to the `(e%3+m)รท3` term reduces the error to about 1 in 32. With the IEEE floating point representation, for finite positive normal values, ordinary integer division of the value in bits magically gives almost exactly the RHS of the above provided we first subtract the exponent bias and later add it back. We do the subtraction virtually to keep e >= 0 so that ordinary integer division rounds towards minus infinity; this is also efficient. All operations can be done in 32-bit. These implementations assume that NaNs, infinities and zeros have already been filtered. """ @inline function _approx_cbrt(x::T) where {T<:Union{Float32,Float64}} # floor(UInt32, adj * exp2(k)) should be evaluated to 2 constants. adj = exponent_bias(T)*2/3 - 0.03306235651 k = significand_bits(T) - (8*sizeof(T) - 32) u = highword(x) & 0x7fff_ffff if u >= Base.Math.highword(floatmin(T)) v = div(u, UInt32(3)) + floor(UInt32, adj * exp2(k)) else # subnormal x *= maxintfloat(T) adj -= exponent(maxintfloat(T))/3 u = highword(x) & 0x7fff_ffff v = div(u, UInt32(3)) + floor(UInt32, adj * exp2(k)) end return copysign(fromhighword(T, v), x) end @inline function _improve_cbrt(x::Float32, t::Float32) # Newton iterations solving # t^2 - x/t == 0 # with update # t <- t*(t^3 + 2*x)/(2*t^3 + x) # Use double precision so that its terms can be arranged for efficiency # without causing overflow or underflow. xx = Float64(x) tt = Float64(t) # 1st step: 16 bits accuracy tt3 = tt^3 tt *= (2*xx + tt3)/(x + 2*tt3) # 2nd step: 47 bits accuracy tt3 = tt^3 tt *= (2*xx + tt3)/(x + 2*tt3) return Float32(tt) end @inline function _improve_cbrt(x::Float64, t::Float64) # cbrt to 23 bits: # # cbrt(x) = t * cbrt(x / t^3) ~= t * P(t^3 / x) # # where P(r) is a polynomial of degree 4 that approximates 1/cbrt(r) # to within 2^-23.5 when |r - 1| < 1/10. The rough approximation # has produced t such than |t/cbrt(x) - 1| ~< 1/32, and cubing this # gives us bounds for r = t^3/x. r = (t*t)*(t/x) t *= (@horner(r, 1.87595182427177009643, -1.88497979543377169875, 1.621429720105354466140) + r^3 * @horner(r, -0.758397934778766047437, 0.145996192886612446982)) # Round t away from zero to 23 bits (sloppily except for ensuring that # the result is larger in magnitude than cbrt(x) but not much more than # 2 23-bit ulps larger). With rounding towards zero, the error bound # would be ~5/6 instead of ~4/6. With a maximum error of 2 23-bit ulps # in the rounded t, the infinite-precision error in the Newton # approximation barely affects third digit in the final error # 0.667; the error in the rounded t can be up to about 3 23-bit ulps # before the final error is larger than 0.667 ulps. u = reinterpret(UInt64, t) u = (u + 0x8000_0000) & UInt64(0xffff_ffff_c000_0000) t = reinterpret(Float64, u) # one step Newton iteration solving # t^3 - x == 0 # with update # t <- t + t * (x/t^2 - t) / (3*t) # to 53 bits with error < 0.667 ulps s = t*t # t*t is exact r = x/s # error <= 0.5 ulps; |r| < |t| w = t+t # t+t is exact r = (r - t)/(w + r) # r-t is exact; w+r ~= 3*t t = muladd(t, r, t) # error <= 0.5 + 0.5/3 + epsilon return t end function cbrt(x::Union{Float32,Float64}) if !isfinite(x) || iszero(x) return x end t = _approx_cbrt(x) return _improve_cbrt(x, t) end function cbrt(a::Float16) if !isfinite(a) || iszero(a) return a end x = Float32(a) # 5 bit approximation. Simpler than _approx_cbrt since subnormals can not appear u = highword(x) & 0x7fff_ffff v = div(u, UInt32(3)) + 0x2a5119f2 t = copysign(fromhighword(Float32, v), x) # 2 newton iterations t = 0.33333334f0 * (2f0*t + x/(t*t)) t = 0.33333334f0 * (2f0*t + x/(t*t)) return Float16(t) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/exp.jlZ# This file is a part of Julia. License is MIT: https://julialang.org/license # magic rounding constant: 1.5*2^52 Adding, then subtracting it from a float rounds it to an Int. # This works because eps(MAGIC_ROUND_CONST(T)) == one(T), so adding it to a smaller number aligns the lsb to the 1s place. # Values for which this trick doesn't work are going to have outputs of 0 or Inf. MAGIC_ROUND_CONST(::Type{Float64}) = 6.755399441055744e15 MAGIC_ROUND_CONST(::Type{Float32}) = 1.048576f7 # max, min, and subnormal arguments # max_exp = T(exponent_bias(T)*log(base, big(2)) + log(base, 2 - big(2.0)^-significand_bits(T))) MAX_EXP(n::Val{2}, ::Type{Float32}) = 128.0f0 MAX_EXP(n::Val{2}, ::Type{Float64}) = 1024.0 MAX_EXP(n::Val{:โ„ฏ}, ::Type{Float32}) = 88.72284f0 MAX_EXP(n::Val{:โ„ฏ}, ::Type{Float64}) = 709.7827128933841 MAX_EXP(n::Val{10}, ::Type{Float32}) = 38.53184f0 MAX_EXP(n::Val{10}, ::Type{Float64}) = 308.25471555991675 # min_exp = T(-(exponent_bias(T)+significand_bits(T)) * log(base, big(2))) MIN_EXP(n::Val{2}, ::Type{Float32}) = -150.0f0 MIN_EXP(n::Val{2}, ::Type{Float64}) = -1075.0 MIN_EXP(n::Val{:โ„ฏ}, ::Type{Float32}) = -103.97208f0 MIN_EXP(n::Val{:โ„ฏ}, ::Type{Float64}) = -745.1332191019412 MIN_EXP(n::Val{10}, ::Type{Float32}) = -45.1545f0 MIN_EXP(n::Val{10}, ::Type{Float64}) = -323.60724533877976 # subnorm_exp = abs(log(base, floatmin(T))) # these vals are positive since it's easier to take abs(x) than -abs(x) SUBNORM_EXP(n::Val{2}, ::Type{Float32}) = 126.00001f0 SUBNORM_EXP(n::Val{2}, ::Type{Float64}) = 1022.0 SUBNORM_EXP(n::Val{:โ„ฏ}, ::Type{Float32}) = 87.33655f0 SUBNORM_EXP(n::Val{:โ„ฏ}, ::Type{Float64}) = 708.3964185322641 SUBNORM_EXP(n::Val{10}, ::Type{Float32}) = 37.92978f0 SUBNORM_EXP(n::Val{10}, ::Type{Float64}) = 307.6526555685887 # 256/log(base, 2) (For Float64 reductions) LogBo256INV(::Val{2}, ::Type{Float64}) = 256.0 LogBo256INV(::Val{:โ„ฏ}, ::Type{Float64}) = 369.3299304675746 LogBo256INV(::Val{10}, ::Type{Float64}) = 850.4135922911647 # -log(base, 2)/256 in upper and lower bits # Upper is truncated to only have 34 bits of significand since N has at most # ceil(log2(-MIN_EXP(base, Float64)*LogBo256INV(Val(2), Float64))) = 19 bits. # This ensures no rounding when multiplying LogBo256U*N for FMAless hardware LogBo256U(::Val{2}, ::Type{Float64}) = -0.00390625 LogBo256U(::Val{:โ„ฏ}, ::Type{Float64}) = -0.002707606173999011 LogBo256U(::Val{10}, ::Type{Float64}) = -0.0011758984204561784 LogBo256L(::Val{2}, ::Type{Float64}) = 0.0 LogBo256L(::Val{:โ„ฏ}, ::Type{Float64}) = -6.327543041662719e-14 LogBo256L(::Val{10}, ::Type{Float64}) = -1.0624811566412999e-13 # 1/log(base, 2) (For Float32 reductions) LogBINV(::Val{2}, ::Type{Float32}) = 1.0f0 LogBINV(::Val{:โ„ฏ}, ::Type{Float32}) = 1.442695f0 LogBINV(::Val{10}, ::Type{Float32}) = 3.321928f0 # -log(base, 2) in upper and lower bits # Upper is truncated to only have 16 bits of significand since N has at most # ceil(log2(-MIN_EXP(n, Float32)*LogBINV(Val(2), Float32))) = 8 bits. # This ensures no rounding when multiplying LogBU*N for FMAless hardware LogBU(::Val{2}, ::Type{Float32}) = -1.0f0 LogBU(::Val{:โ„ฏ}, ::Type{Float32}) = -0.69314575f0 LogBU(::Val{10}, ::Type{Float32}) = -0.3010254f0 LogBL(::Val{2}, ::Type{Float32}) = 0.0f0 LogBL(::Val{:โ„ฏ}, ::Type{Float32}) = -1.4286068f-6 LogBL(::Val{10}, ::Type{Float32}) = -4.605039f-6 # -log(base, 2) as a Float32 for Float16 version. LogB(::Val{2}, ::Type{Float16}) = -1.0f0 LogB(::Val{:โ„ฏ}, ::Type{Float16}) = -0.6931472f0 LogB(::Val{10}, ::Type{Float16}) = -0.30103f0 # Range reduced kernels function expm1b_kernel(::Val{2}, x::Float64) return x * evalpoly(x, (0.6931471805599393, 0.24022650695910058, 0.05550411502333161, 0.009618129548366803)) end function expm1b_kernel(::Val{:โ„ฏ}, x::Float64) return x * evalpoly(x, (0.9999999999999912, 0.4999999999999997, 0.1666666857598779, 0.04166666857598777)) end function expm1b_kernel(::Val{10}, x::Float64) return x * evalpoly(x, (2.3025850929940255, 2.6509490552391974, 2.034678825384765, 1.1712552025835192)) end function expb_kernel(::Val{2}, x::Float32) return evalpoly(x, (1.0f0, 0.6931472f0, 0.2402265f0, 0.05550411f0, 0.009618025f0, 0.0013333423f0, 0.00015469732f0, 1.5316464f-5)) end function expb_kernel(::Val{:โ„ฏ}, x::Float32) return evalpoly(x, (1.0f0, 1.0f0, 0.5f0, 0.16666667f0, 0.041666217f0, 0.008333249f0, 0.001394858f0, 0.00019924171f0)) end function expb_kernel(::Val{10}, x::Float32) return evalpoly(x, (1.0f0, 2.3025851f0, 2.650949f0, 2.0346787f0, 1.1712426f0, 0.53937745f0, 0.20788547f0, 0.06837386f0)) end # Table stores data with 60 sig figs by using the fact that the first 12 bits of all the # values would be the same if stored as regular Float64. # This only gains 8 bits since the least significant 4 bits of the exponent # of the small part are not the same for all table entries const JU_MASK = typemax(UInt64)>>12 const JL_MASK = typemax(UInt64)>>8 const JU_CONST = 0x3FF0000000000000 const JL_CONST = 0x3C00000000000000 #function make_table(size) # t_array = zeros(UInt64, size); # for j in 1:size # val = 2.0^(BigFloat(j-1)/size) # valU = Float64(val, RoundDown) # valL = Float64(val-valU) # valU = reinterpret(UInt64, valU) & JU_MASK # valL = ((reinterpret(UInt64, valL) & JL_MASK)>>44)<<52 # t_array[j] = valU | valL # end # return Tuple(t_array) #end #const J_TABLE = make_table(256); const J_TABLE = (0x0000000000000000, 0xaac00b1afa5abcbe, 0x9b60163da9fb3335, 0xab502168143b0280, 0xadc02c9a3e778060, 0x656037d42e11bbcc, 0xa7a04315e86e7f84, 0x84c04e5f72f654b1, 0x8d7059b0d3158574, 0xa510650a0e3c1f88, 0xa8d0706b29ddf6dd, 0x83207bd42b72a836, 0x6180874518759bc8, 0xa4b092bdf66607df, 0x91409e3ecac6f383, 0x85d0a9c79b1f3919, 0x98a0b5586cf9890f, 0x94f0c0f145e46c85, 0x9010cc922b7247f7, 0xa210d83b23395deb, 0x4030e3ec32d3d1a2, 0xa5b0efa55fdfa9c4, 0xae40fb66affed31a, 0x8d41073028d7233e, 0xa4911301d0125b50, 0xa1a11edbab5e2ab5, 0xaf712abdc06c31cb, 0xae8136a814f204aa, 0xa661429aaea92ddf, 0xa9114e95934f312d, 0x82415a98c8a58e51, 0x58f166a45471c3c2, 0xab9172b83c7d517a, 0x70917ed48695bbc0, 0xa7718af9388c8de9, 0x94a1972658375d2f, 0x8e51a35beb6fcb75, 0x97b1af99f8138a1c, 0xa351bbe084045cd3, 0x9001c82f95281c6b, 0x9e01d4873168b9aa, 0xa481e0e75eb44026, 0xa711ed5022fcd91c, 0xa201f9c18438ce4c, 0x8dc2063b88628cd6, 0x935212be3578a819, 0x82a21f49917ddc96, 0x8d322bdda27912d1, 0x99b2387a6e756238, 0x8ac2451ffb82140a, 0x8ac251ce4fb2a63f, 0x93e25e85711ece75, 0x82b26b4565e27cdd, 0x9e02780e341ddf29, 0xa2d284dfe1f56380, 0xab4291ba7591bb6f, 0x86129e9df51fdee1, 0xa352ab8a66d10f12, 0xafb2b87fd0dad98f, 0xa572c57e39771b2e, 0x9002d285a6e4030b, 0x9d12df961f641589, 0x71c2ecafa93e2f56, 0xaea2f9d24abd886a, 0x86f306fe0a31b715, 0x89531432edeeb2fd, 0x8a932170fc4cd831, 0xa1d32eb83ba8ea31, 0x93233c08b26416ff, 0xab23496266e3fa2c, 0xa92356c55f929ff0, 0xa8f36431a2de883a, 0xa4e371a7373aa9ca, 0xa3037f26231e7549, 0xa0b38cae6d05d865, 0xa3239a401b7140ee, 0xad43a7db34e59ff6, 0x9543b57fbfec6cf4, 0xa083c32dc313a8e4, 0x7fe3d0e544ede173, 0x8ad3dea64c123422, 0xa943ec70df1c5174, 0xa413fa4504ac801b, 0x8bd40822c367a024, 0xaf04160a21f72e29, 0xa3d423fb27094689, 0xab8431f5d950a896, 0x88843ffa3f84b9d4, 0x48944e086061892d, 0xae745c2042a7d231, 0x9c946a41ed1d0057, 0xa1e4786d668b3236, 0x73c486a2b5c13cd0, 0xab1494e1e192aed1, 0x99c4a32af0d7d3de, 0xabb4b17dea6db7d6, 0x7d44bfdad5362a27, 0x9054ce41b817c114, 0x98e4dcb299fddd0d, 0xa564eb2d81d8abfe, 0xa5a4f9b2769d2ca6, 0x7a2508417f4531ee, 0xa82516daa2cf6641, 0xac65257de83f4eee, 0xabe5342b569d4f81, 0x879542e2f4f6ad27, 0xa8a551a4ca5d920e, 0xa7856070dde910d1, 0x99b56f4736b527da, 0xa7a57e27dbe2c4ce, 0x82958d12d497c7fd, 0xa4059c0827ff07cb, 0x9635ab07dd485429, 0xa245ba11fba87a02, 0x3c45c9268a5946b7, 0xa195d84590998b92, 0x9ba5e76f15ad2148, 0xa985f6a320dceb70, 0xa60605e1b976dc08, 0x9e46152ae6cdf6f4, 0xa636247eb03a5584, 0x984633dd1d1929fd, 0xa8e6434634ccc31f, 0xa28652b9febc8fb6, 0xa226623882552224, 0xa85671c1c70833f5, 0x60368155d44ca973, 0x880690f4b19e9538, 0xa216a09e667f3bcc, 0x7a36b052fa75173e, 0xada6c012750bdabe, 0x9c76cfdcddd47645, 0xae46dfb23c651a2e, 0xa7a6ef9298593ae4, 0xa9f6ff7df9519483, 0x59d70f7466f42e87, 0xaba71f75e8ec5f73, 0xa6f72f8286ead089, 0xa7a73f9a48a58173, 0x90474fbd35d7cbfd, 0xa7e75feb564267c8, 0x9b777024b1ab6e09, 0x986780694fde5d3f, 0x934790b938ac1cf6, 0xaaf7a11473eb0186, 0xa207b17b0976cfda, 0x9f17c1ed0130c132, 0x91b7d26a62ff86f0, 0x7057e2f336cf4e62, 0xabe7f3878491c490, 0xa6c80427543e1a11, 0x946814d2add106d9, 0xa1582589994cce12, 0x9998364c1eb941f7, 0xa9c8471a4623c7ac, 0xaf2857f4179f5b20, 0xa01868d99b4492ec, 0x85d879cad931a436, 0x99988ac7d98a6699, 0x9d589bd0a478580f, 0x96e8ace5422aa0db, 0x9ec8be05bad61778, 0xade8cf3216b5448b, 0xa478e06a5e0866d8, 0x85c8f1ae99157736, 0x959902fed0282c8a, 0xa119145b0b91ffc5, 0xab2925c353aa2fe1, 0xae893737b0cdc5e4, 0xa88948b82b5f98e4, 0xad395a44cbc8520e, 0xaf296bdd9a7670b2, 0xa1797d829fde4e4f, 0x7ca98f33e47a22a2, 0xa749a0f170ca07b9, 0xa119b2bb4d53fe0c, 0x7c79c49182a3f090, 0xa579d674194bb8d4, 0x7829e86319e32323, 0xaad9fa5e8d07f29d, 0xa65a0c667b5de564, 0x9c6a1e7aed8eb8bb, 0x963a309bec4a2d33, 0xa2aa42c980460ad7, 0xa16a5503b23e255c, 0x650a674a8af46052, 0x9bca799e1330b358, 0xa58a8bfe53c12e58, 0x90fa9e6b5579fdbf, 0x889ab0e521356eba, 0xa81ac36bbfd3f379, 0x97ead5ff3a3c2774, 0x97aae89f995ad3ad, 0xa5aafb4ce622f2fe, 0xa21b0e07298db665, 0x94db20ce6c9a8952, 0xaedb33a2b84f15fa, 0xac1b468415b749b0, 0xa1cb59728de55939, 0x92ab6c6e29f1c52a, 0xad5b7f76f2fb5e46, 0xa24b928cf22749e3, 0xa08ba5b030a10649, 0xafcbb8e0b79a6f1e, 0x823bcc1e904bc1d2, 0xafcbdf69c3f3a206, 0xa08bf2c25bd71e08, 0xa89c06286141b33c, 0x811c199bdd85529c, 0xa48c2d1cd9fa652b, 0x9b4c40ab5fffd07a, 0x912c544778fafb22, 0x928c67f12e57d14b, 0xa86c7ba88988c932, 0x71ac8f6d9406e7b5, 0xaa0ca3405751c4da, 0x750cb720dcef9069, 0xac5ccb0f2e6d1674, 0xa88cdf0b555dc3f9, 0xa2fcf3155b5bab73, 0xa1ad072d4a07897b, 0x955d1b532b08c968, 0xa15d2f87080d89f1, 0x93dd43c8eacaa1d6, 0x82ed5818dcfba487, 0x5fed6c76e862e6d3, 0xa77d80e316c98397, 0x9a0d955d71ff6075, 0x9c2da9e603db3285, 0xa24dbe7cd63a8314, 0x92ddd321f301b460, 0xa1ade7d5641c0657, 0xa72dfc97337b9b5e, 0xadae11676b197d16, 0xa42e264614f5a128, 0xa30e3b333b16ee11, 0x839e502ee78b3ff6, 0xaa7e653924676d75, 0x92de7a51fbc74c83, 0xa77e8f7977cdb73f, 0xa0bea4afa2a490d9, 0x948eb9f4867cca6e, 0xa1becf482d8e67f0, 0x91cee4aaa2188510, 0x9dcefa1bee615a27, 0xa66f0f9c1cb64129, 0x93af252b376bba97, 0xacdf3ac948dd7273, 0x99df50765b6e4540, 0x9faf6632798844f8, 0xa12f7bfdad9cbe13, 0xaeef91d802243c88, 0x874fa7c1819e90d8, 0xacdfbdba3692d513, 0x62efd3c22b8f71f1, 0x74afe9d96b2a23d9) # :nothrow needed since the compiler can't prove `ind` is inbounds. Base.@assume_effects :nothrow function table_unpack(ind::Int32) ind = ind & 255 + 1 # 255 == length(J_TABLE) - 1 j = getfield(J_TABLE, ind) # use getfield so the compiler can prove consistent jU = reinterpret(Float64, JU_CONST | (j&JU_MASK)) jL = reinterpret(Float64, JL_CONST | (j>>8)) return jU, jL end # Method for Float64 # 1. Argument reduction: Reduce x to an r so that |r| <= log(b, 2)/512. Given x, base b, # find r and integers k, j such that # x = (k + j/256)*log(b, 2) + r, 0 <= j < 256, |r| <= log(b,2)/512. # # 2. Approximate b^r-1 by 3rd-degree minimax polynomial p_b(r) on the interval [-log(b,2)/512, log(b,2)/512]. # Since the bounds on r are very tight, this is sufficient to be accurate to floating point epsilon. # # 3. Scale back: b^x = 2^k * 2^(j/256) * (1 + p_b(r)) # Since the range of possible j is small, 2^(j/256) is stored for all possible values in slightly extended precision. # Method for Float32 # 1. Argument reduction: Reduce x to an r so that |r| <= log(b, 2)/2. Given x, base b, # find r and integer N such that # x = N*log(b, 2) + r, |r| <= log(b,2)/2. # # 2. Approximate b^r by 7th-degree minimax polynomial p_b(r) on the interval [-log(b,2)/2, log(b,2)/2]. # 3. Scale back: b^x = 2^N * p_b(r) # For both, a little extra care needs to be taken if b^r is subnormal. # The solution is to do the scaling back in 2 steps as just messing with the exponent wouldn't work. @inline function exp_impl(x::Float64, base) T = Float64 N_float = muladd(x, LogBo256INV(base, T), MAGIC_ROUND_CONST(T)) N = reinterpret(UInt64, N_float) % Int32 N_float -= MAGIC_ROUND_CONST(T) #N_float now equals round(x*LogBo256INV(base, T)) r = muladd(N_float, LogBo256U(base, T), x) r = muladd(N_float, LogBo256L(base, T), r) k = N >> 8 jU, jL = table_unpack(N) small_part = muladd(jU, expm1b_kernel(base, r), jL) + jU if !(abs(x) <= SUBNORM_EXP(base, T)) x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 # The UInt64 forces promotion. (Only matters for 32 bit systems.) twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end #k == 1024 && return (small_part * 2.0) * 2.0^1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) end # Computes base^(x+xlo). Used for pow. @inline function exp_impl(x::Float64, xlo::Float64, base) T = Float64 N_float = muladd(x, LogBo256INV(base, T), MAGIC_ROUND_CONST(T)) N = reinterpret(UInt64, N_float) % Int32 N_float -= MAGIC_ROUND_CONST(T) #N_float now equals round(x*LogBo256INV(base, T)) r = muladd(N_float, LogBo256U(base, T), x) r = muladd(N_float, LogBo256L(base, T), r) k = N >> 8 jU, jL = table_unpack(N) kern = expm1b_kernel(base, r) very_small = muladd(kern, jU*xlo, jL) hi, lo = Base.canonicalize2(1.0, kern) small_part = fma(jU, hi, muladd(jU, (lo+xlo), very_small)) if !(abs(x) <= SUBNORM_EXP(base, T)) x >= MAX_EXP(base, T) && return Inf x <= MIN_EXP(base, T) && return 0.0 if k <= -53 # The UInt64 forces promotion. (Only matters for 32 bit systems.) twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end #k == 1024 && return (small_part * 2.0) * 2.0^1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) end @inline function exp_impl_fast(x::Float64, base) T = Float64 x >= MAX_EXP(base, T) && return Inf x <= -SUBNORM_EXP(base, T) && return 0.0 N_float = muladd(x, LogBo256INV(base, T), MAGIC_ROUND_CONST(T)) N = reinterpret(UInt64, N_float) % Int32 N_float -= MAGIC_ROUND_CONST(T) #N_float now equals round(x*LogBo256INV(base, T)) r = muladd(N_float, LogBo256U(base, T), x) r = muladd(N_float, LogBo256L(base, T), r) k = N >> 8 jU = reinterpret(Float64, JU_CONST | (@inbounds J_TABLE[N&255 + 1] & JU_MASK)) small_part = muladd(jU, expm1b_kernel(base, r), jU) twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) end @inline function exp_impl(x::Float32, base) T = Float32 N_float = round(x*LogBINV(base, T)) N = unsafe_trunc(Int32, N_float) r = muladd(N_float, LogBU(base, T), x) r = muladd(N_float, LogBL(base, T), r) small_part = expb_kernel(base, r) power = (N+Int32(127)) x > MAX_EXP(base, T) && return Inf32 x < MIN_EXP(base, T) && return 0.0f0 if x <= -SUBNORM_EXP(base, T) power += Int32(24) small_part *= Float32(0x1p-24) end if N == 128 power -= Int32(1) small_part *= 2f0 end return small_part * reinterpret(T, power << Int32(23)) end @inline function exp_impl_fast(x::Float32, base) T = Float32 x >= MAX_EXP(base, T) && return Inf32 x <= -SUBNORM_EXP(base, T) && return 0f0 N_float = round(x*LogBINV(base, T)) N = unsafe_trunc(Int32, N_float) r = muladd(N_float, LogBU(base, T), x) r = muladd(N_float, LogBL(base, T), r) small_part = expb_kernel(base, r) twopk = reinterpret(T, (N+Int32(127)) << Int32(23)) return twopk*small_part end @inline function exp_impl(a::Float16, base) T = Float32 x = T(a) N_float = round(x*LogBINV(base, T)) N = unsafe_trunc(Int32, N_float) r = muladd(N_float, LogB(base, Float16), x) small_part = expb_kernel(base, r) if !(abs(x) <= 25) x > 16 && return Inf16 x < 25 && return zero(Float16) end twopk = reinterpret(T, (N+Int32(127)) << Int32(23)) return Float16(twopk*small_part) end for (func, fast_func, base) in ((:exp2, :exp2_fast, Val(2)), (:exp, :exp_fast, Val(:โ„ฏ)), (:exp10, :exp10_fast, Val(10))) @eval begin @noinline $func(x::Union{Float16,Float32,Float64}) = exp_impl(x, $base) @noinline $fast_func(x::Union{Float32,Float64}) = exp_impl_fast(x, $base) end end @doc """ exp(x) Compute the natural base exponential of `x`, in other words ``โ„ฏ^x``. See also [`exp2`](@ref), [`exp10`](@ref) and [`cis`](@ref). # Examples ```jldoctest julia> exp(1.0) 2.718281828459045 julia> exp(im * pi) โ‰ˆ cis(pi) true ``` """ exp(x::Real) """ exp2(x) Compute the base 2 exponential of `x`, in other words ``2^x``. See also [`ldexp`](@ref), [`<<`](@ref). # Examples ```jldoctest julia> exp2(5) 32.0 julia> 2^5 32 julia> exp2(63) > typemax(Int) true ``` """ exp2(x) """ exp10(x) Compute the base 10 exponential of `x`, in other words ``10^x``. # Examples ```jldoctest julia> exp10(2) 100.0 julia> 10^2 100 ``` """ exp10(x) # functions with special cases for integer arguments @inline function exp2(x::Base.BitInteger) if x > 1023 Inf64 elseif x <= -1023 # if -1073 < x <= -1023 then Result will be a subnormal number # Hex literal with padding must be used to work on 32bit machine reinterpret(Float64, 0x0000_0000_0000_0001 << ((x + 1074) % UInt)) else # We will cast everything to Int64 to avoid errors in case of Int128 # If x is a Int128, and is outside the range of Int64, then it is not -1023 MAX_EXP(Float64) && return Inf x < MIN_EXP(Float64) && return -1.0 end N_float = muladd(x, LogBo256INV(Val(:โ„ฏ), T), MAGIC_ROUND_CONST(T)) N = reinterpret(UInt64, N_float) % Int32 N_float -= MAGIC_ROUND_CONST(T) #N_float now equals round(x*LogBo256INV(Val(:โ„ฏ), T)) r = muladd(N_float, LogBo256U(Val(:โ„ฏ), T), x) r = muladd(N_float, LogBo256L(Val(:โ„ฏ), T), r) k = Int64(N >> 8) jU, jL = table_unpack(N) p = expm1b_kernel(Val(:โ„ฏ), r) twopk = reinterpret(Float64, (1023+k) << 52) twopnk = reinterpret(Float64, (1023-k) << 52) k>=106 && return reinterpret(Float64, (1022+k) << 52)*(jU + muladd(jU, p, jL))*2 k>=53 && return twopk*(jU + muladd(jU, p, (jL-twopnk))) k<=-2 && return twopk*(jU + muladd(jU, p, jL))-1 return twopk*((jU-twopnk) + fma(jU, p, jL)) end function expm1(x::Float32) x > MAX_EXP(Float32) && return Inf32 x < MIN_EXP(Float32) && return -1f0 if -0.2876821f0 <=x <= 0.22314355f0 return expm1_small(x) end x = Float64(x) N_float = round(x*Ln2INV(Float64)) N = unsafe_trunc(Int64, N_float) r = muladd(N_float, Ln2(Float64), x) hi = evalpoly(r, (1.0, .5, 0.16666667546642386, 0.041666183019487026, 0.008332997481506921, 0.0013966479175977883, 0.0002004037059220124)) small_part = r*hi twopk = reinterpret(Float64, (N+1023) << 52) return Float32(muladd(twopk, small_part, twopk-1.0)) end function expm1(x::Float16) x > MAX_EXP(Float16) && return Inf16 x < MIN_EXP(Float16) && return Float16(-1.0) x = Float32(x) if -0.2876821f0 <=x <= 0.22314355f0 return Float16(x*evalpoly(x, (1f0, .5f0, 0.16666628f0, 0.04166785f0, 0.008351848f0, 0.0013675707f0))) end N_float = round(x*Ln2INV(Float32)) N = unsafe_trunc(Int32, N_float) r = muladd(N_float, Ln2(Float32), x) hi = evalpoly(r, (1f0, .5f0, 0.16666667f0, 0.041665863f0, 0.008333111f0, 0.0013981499f0, 0.00019983904f0)) small_part = r*hi twopk = reinterpret(Float32, (N+Int32(127)) << Int32(23)) return Float16(muladd(twopk, small_part, twopk-1f0)) end """ expm1(x) Accurately compute ``e^x-1``. It avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small values of x. # Examples ```jldoctest julia> expm1(1e-16) 1.0e-16 julia> exp(1e-16) - 1 0.0 ``` """ expm1(x) Y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/hyperbolic.jl’## This file is a part of Julia. License is MIT: https://julialang.org/license # asinh, acosh, and atanh are heavily based on FDLIBM code: # e_sinh.c, e_sinhf, e_cosh.c, e_coshf, s_tanh.c, s_tanhf.c, s_asinh.c, # s_asinhf.c, e_acosh.c, e_coshf.c, e_atanh.c, and e_atanhf.c # that are made available under the following licence: # ==================================================== # Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. # # Developed at SunSoft, a Sun Microsystems, Inc. business. # Permission to use, copy, modify, and distribute this # software is freely granted, provided that this notice # is preserved. # ==================================================== # Hyperbolic functions # sinh methods H_SMALL_X(::Type{Float64}) = 2.0^-28 H_MEDIUM_X(::Type{Float64}) = 22.0 H_SMALL_X(::Type{Float32}) = 2f-12 H_MEDIUM_X(::Type{Float32}) = 9f0 H_LARGE_X(::Type{Float64}) = 709.7822265633563 # nextfloat(709.7822265633562) H_LARGE_X(::Type{Float32}) = 88.72283f0 SINH_SMALL_X(::Type{Float64}) = 2.1 SINH_SMALL_X(::Type{Float32}) = 3.0f0 # For Float64, use DoubleFloat scheme for extra accuracy function sinh_kernel(x::Float64) x2, x2lo = two_mul(x,x) hi_order = evalpoly(x2, (8.333333333336817e-3, 1.9841269840165435e-4, 2.7557319381151335e-6, 2.5052096530035283e-8, 1.6059550718903307e-10, 7.634842144412119e-13, 2.9696954760355812e-15)) hi,lo = exthorner(x2, (1.0, 0.16666666666666635, hi_order)) return muladd(x, hi, muladd(x, lo, x*x2lo*0.16666666666666635)) end # For Float32, using Float64 is simpler, faster, and doesn't require FMA function sinh_kernel(x::Float32) x=Float64(x) res = evalpoly(x*x, (1.0, 0.1666666779967941, 0.008333336726447933, 0.00019841001151414065, 2.7555538207080807e-6, 2.5143389765825282e-8, 1.6260094552031644e-10)) return Float32(res*x) end @inline function sinh16_kernel(x::Float32) res = evalpoly(x*x, (1.0f0, 0.16666667f0, 0.008333337f0, 0.00019841001f0, 2.7555539f-6, 2.514339f-8, 1.6260095f-10)) return Float16(res*x) end function sinh(x::T) where T<:Union{Float32,Float64} # Method # mathematically sinh(x) is defined to be (exp(x)-exp(-x))/2 # 1. Sometimes replace x by |x| (sinh(-x) = -sinh(x)). # 2. Find the branch and the expression to calculate and return it # a) 0 <= x < SINH_SMALL_X # approximate sinh(x) with a minimax polynomial # b) SINH_SMALL_X <= x < H_LARGE_X # return sinh(x) = (exp(x) - exp(-x))/2 # d) H_LARGE_X <= x # return sinh(x) = exp(x/2)/2 * exp(x/2) # Note that this branch automatically deals with Infs and NaNs absx = abs(x) if absx <= SINH_SMALL_X(T) return sinh_kernel(x) elseif absx >= H_LARGE_X(T) E = exp(T(.5)*absx) return copysign(T(.5)*E*E, x) end E = exp(absx) return copysign(T(.5)*(E - 1/E),x) end function Base.sinh(a::Float16) x = Float32(a) absx = abs(x) absx <= SINH_SMALL_X(Float32) && return sinh16_kernel(x) E = exp(absx) return Float16(copysign(.5f0*(E - 1/E),x)) end COSH_SMALL_X(::Type{T}) where T= one(T) function cosh_kernel(x2::Float32) return evalpoly(x2, (1.0f0, 0.49999997f0, 0.041666888f0, 0.0013882756f0, 2.549933f-5)) end function cosh_kernel(x2::Float64) return evalpoly(x2, (1.0, 0.5000000000000002, 0.04166666666666269, 1.3888888889206764e-3, 2.4801587176784207e-5, 2.7557345825742837e-7, 2.0873617441235094e-9, 1.1663435515945578e-11)) end function cosh(x::T) where T<:Union{Float32,Float64} # Method # mathematically cosh(x) is defined to be (exp(x)+exp(-x))/2 # 1. Replace x by |x| (cosh(x) = cosh(-x)). # 2. Find the branch and the expression to calculate and return it # a) x <= COSH_SMALL_X # approximate sinh(x) with a minimax polynomial # b) COSH_SMALL_X <= x < H_LARGE_X # return cosh(x) = = (exp(x) + exp(-x))/2 # e) H_LARGE_X <= x # return cosh(x) = exp(x/2)/2 * exp(x/2) # Note that this branch automatically deals with Infs and NaNs absx = abs(x) if absx <= COSH_SMALL_X(T) return cosh_kernel(x*x) elseif absx >= H_LARGE_X(T) E = exp(T(.5)*absx) return T(.5)*E*E end E = exp(absx) return T(.5)*(E + 1/E) end # tanh methods TANH_LARGE_X(::Type{Float64}) = 44.0 TANH_LARGE_X(::Type{Float32}) = 18.0f0 TANH_SMALL_X(::Type{Float64}) = 1.0 TANH_SMALL_X(::Type{Float32}) = 1.3862944f0 #2*log(2) @inline function tanh_kernel(x::Float64) return evalpoly(x, (1.0, -0.33333333333332904, 0.13333333333267555, -0.05396825393066753, 0.02186948742242217, -0.008863215974794633, 0.003591910693118715, -0.0014542587440487815, 0.0005825521659411748, -0.00021647574085351332, 5.5752458452673005e-5)) end @inline function tanh_kernel(x::Float32) return evalpoly(x, (1.0f0, -0.3333312f0, 0.13328037f0, -0.05350336f0, 0.019975215f0, -0.0050525228f0)) end function tanh(x::T) where T<:Union{Float32, Float64} # Method # mathematically tanh(x) is defined to be (exp(x)-exp(-x))/(exp(x)+exp(-x)) # 1. reduce x to non-negative by tanh(-x) = -tanh(x). # 2. Find the branch and the expression to calculate and return it # a) 0 <= x < H_SMALL_X # Use a minimax polynomial over the range # b) H_SMALL_X <= x < TANH_LARGE_X # 1 - 2/(exp(2x) + 1) # c) TANH_LARGE_X <= x # return 1 abs2x = abs(2x) abs2x >= TANH_LARGE_X(T) && return copysign(one(T), x) abs2x <= TANH_SMALL_X(T) && return x*tanh_kernel(x*x) k = exp(abs2x) return copysign(1 - 2/(k+1), x) end # Inverse hyperbolic functions AH_LN2(::Type{Float64}) = 6.93147180559945286227e-01 AH_LN2(::Type{Float32}) = 6.9314718246f-01 # asinh methods function asinh(x::T) where T <: Union{Float32, Float64} # Method # mathematically asinh(x) = sign(x)*log(|x| + sqrt(x*x + 1)) # is the principle value of the inverse hyperbolic sine # 1. Find the branch and the expression to calculate and return it # a) |x| < 2^-28 # return x # b) |x| < 2 # return sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) # c) 2 <= |x| < 2^28 # return sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) # d) |x| >= 2^28 # return sign(x)*(log(x)+ln2)) if !isfinite(x) return x end absx = abs(x) if absx < T(2) # in a) if absx < T(2)^-28 return x end # in b) t = x*x w = log1p(absx + t/(T(1) + sqrt(T(1) + t))) elseif absx < T(2)^28 # in c) t = absx w = log(T(2)*t + T(1)/(sqrt(x*x + T(1)) + t)) else # in d) w = log(absx) + AH_LN2(T) end return copysign(w, x) end # acosh methods @noinline acosh_domain_error(x) = throw(DomainError(x, "acosh(x) is only defined for x โ‰ฅ 1.")) function acosh(x::T) where T <: Union{Float32, Float64} # Method # mathematically acosh(x) if defined to be log(x + sqrt(x*x-1)) # 1. Find the branch and the expression to calculate and return it # a) x = 1 # return log1p(t+sqrt(2.0*t+t*t)) where t=x-1. # b) 1 < x < 2 # return log1p(t+sqrt(2.0*t+t*t)) where t=x-1. # c) 2 <= x < # return log(2x-1/(sqrt(x*x-1)+x)) # d) x >= 2^28 # return log(x)+ln2 # Special cases: # if x < 1 throw DomainError isnan(x) && return x if x < T(1) return acosh_domain_error(x) elseif x == T(1) # in a) return T(0) elseif x < T(2) # in b) t = x - T(1) return log1p(t + sqrt(T(2)*t + t*t)) elseif x < T(2)^28 # in c) t = x*x return log(T(2)*x - T(1)/(x+sqrt(t - T(1)))) else # in d) return log(x) + AH_LN2(T) end end # atanh methods @noinline atanh_domain_error(x) = throw(DomainError(x, "atanh(x) is only defined for |x| โ‰ค 1.")) function atanh(x::T) where T <: Union{Float32, Float64} # Method # 1.Reduced x to positive by atanh(-x) = -atanh(x) # 2. Find the branch and the expression to calculate and return it # a) 0 <= x < 0.5 # return 0.5*log1p(2x/(1-x)) # b) 0.5 <= x <= 1 # return 0.5*log((x+1)/(1-x)) # Special cases: # if |x| > 1 throw DomainError isnan(x) && return x absx = abs(x) if absx > 1 atanh_domain_error(x) end if absx < T(0.5) # in a) t = log1p(T(2)*absx/(T(1)-absx)) else # in b) t = log((T(1)+absx)/(T(1)-absx)) end return T(0.5)*copysign(t, x) end S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/trig.jl:ง# This file is a part of Julia. Except for the *_kernel functions (see below), # license is MIT: https://julialang.org/license struct DoubleFloat64 hi::Float64 lo::Float64 end struct DoubleFloat32 hi::Float64 end # sin_kernel and cos_kernel functions are only valid for |x| < pi/4 = 0.7854 # translated from openlibm code: k_sin.c, k_cos.c, k_sinf.c, k_cosf.c. # atan functions are based on openlibm code: s_atan.c, s_atanf.c. # acos functions are based on openlibm code: e_acos.c, e_acosf.c. # asin functions are based on openlibm code: e_asin.c, e_asinf.c. The above # functions are made available under the following licence: ## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. ## ## Developed at SunPro, a Sun Microsystems, Inc. business. ## Permission to use, copy, modify, and distribute this ## software is freely granted, provided that this notice ## is preserved. # Trigonometric functions # sin methods @noinline sin_domain_error(x) = throw(DomainError(x, "sin(x) is only defined for finite x.")) function sin(x::T) where T<:Union{Float32, Float64} absx = abs(x) if absx < T(pi)/4 #|x| ~<= pi/4, no need for reduction if absx < sqrt(eps(T)) return x end return sin_kernel(x) elseif isnan(x) return x elseif isinf(x) sin_domain_error(x) end n, y = rem_pio2_kernel(x) n = n&3 if n == 0 return sin_kernel(y) elseif n == 1 return cos_kernel(y) elseif n == 2 return -sin_kernel(y) else return -cos_kernel(y) end end # Coefficients in 13th order polynomial approximation on [0; ฯ€/4] # sin(x) โ‰ˆ x + S1*xยณ + S2*xโต + S3*xโท + S4*xโน + S5*xยนยน + S6*xยนยณ # D for double, S for sin, number is the order of x-1 const DS1 = -1.66666666666666324348e-01 const DS2 = 8.33333333332248946124e-03 const DS3 = -1.98412698298579493134e-04 const DS4 = 2.75573137070700676789e-06 const DS5 = -2.50507602534068634195e-08 const DS6 = 1.58969099521155010221e-10 """ sin_kernel(yhi, ylo) Computes the sine on the interval [-ฯ€/4; ฯ€/4]. """ @inline function sin_kernel(y::DoubleFloat64) yยฒ = y.hi*y.hi yโด = yยฒ*yยฒ r = @horner(yยฒ, DS2, DS3, DS4) + yยฒ*yโด*@horner(yยฒ, DS5, DS6) yยณ = yยฒ*y.hi y.hi-((yยฒ*(0.5*y.lo-yยณ*r)-y.lo)-yยณ*DS1) end @inline function sin_kernel(y::Float64) yยฒ = y*y yโด = yยฒ*yยฒ r = @horner(yยฒ, DS2, DS3, DS4) + yยฒ*yโด*@horner(yยฒ, DS5, DS6) yยณ = yยฒ*y y+yยณ*(DS1+yยฒ*r) end # sin_kernels accepting values from rem_pio2 in the Float32 case @inline sin_kernel(x::Float32) = sin_kernel(DoubleFloat32(x)) @inline function sin_kernel(y::DoubleFloat32) S1 = -0.16666666641626524 S2 = 0.008333329385889463 z = y.hi*y.hi w = z*z r = @horner(z, -0.00019839334836096632, 2.718311493989822e-6) s = z*y.hi Float32((y.hi + s*@horner(z, S1, S2)) + s*w*r) end # cos methods @noinline cos_domain_error(x) = throw(DomainError(x, "cos(x) is only defined for finite x.")) function cos(x::T) where T<:Union{Float32, Float64} absx = abs(x) if absx < T(pi)/4 if absx < sqrt(eps(T)/T(2.0)) return T(1.0) end return cos_kernel(x) elseif isnan(x) return x elseif isinf(x) cos_domain_error(x) else n, y = rem_pio2_kernel(x) n = n&3 if n == 0 return cos_kernel(y) elseif n == 1 return -sin_kernel(y) elseif n == 2 return -cos_kernel(y) else return sin_kernel(y) end end end const DC1 = 4.16666666666666019037e-02 const DC2 = -1.38888888888741095749e-03 const DC3 = 2.48015872894767294178e-05 const DC4 = -2.75573143513906633035e-07 const DC5 = 2.08757232129817482790e-09 const DC6 = -1.13596475577881948265e-11 """ cos_kernel(y) Compute the cosine on the interval yโˆˆ[-ฯ€/4; ฯ€/4]. """ @inline function cos_kernel(y::DoubleFloat64) yยฒ = y.hi*y.hi yโด = yยฒ*yยฒ r = yยฒ*@horner(yยฒ, DC1, DC2, DC3) + yโด*yโด*@horner(yยฒ, DC4, DC5, DC6) half_yยฒ = 0.5*yยฒ w = 1.0-half_yยฒ w + (((1.0-w)-half_yยฒ) + (yยฒ*r-y.hi*y.lo)) end @inline function cos_kernel(y::Float64) yยฒ = y*y yโด = yยฒ*yยฒ r = yยฒ*@horner(yยฒ, DC1, DC2, DC3) + yโด*yโด*@horner(yยฒ, DC4, DC5, DC6) half_yยฒ = 0.5*yยฒ w = 1.0-half_yยฒ w + (((1.0-w)-half_yยฒ) + (yยฒ*r)) end # cos_kernels accepting values from rem_pio2 in the Float32 case cos_kernel(x::Float32) = cos_kernel(DoubleFloat32(x)) @inline function cos_kernel(y::DoubleFloat32) C0 = -0.499999997251031 C1 = 0.04166662332373906 yยฒ = y.hi*y.hi yโด = yยฒ*yยฒ r = @horner(yยฒ, -0.001388676377460993, 2.439044879627741e-5) Float32(((1.0+yยฒ*C0) + yโด*C1) + (yโด*yยฒ)*r) end ### sincos methods @noinline sincos_domain_error(x) = throw(DomainError(x, "sincos(x) is only defined for finite x.")) """ sincos(x) Simultaneously compute the sine and cosine of `x`, where `x` is in radians, returning a tuple `(sine, cosine)`. See also [`cis`](@ref), [`sincospi`](@ref), [`sincosd`](@ref). """ function sincos(x::T) where T<:Union{Float32, Float64} if abs(x) < T(pi)/4 if x == zero(T) return x, one(T) end return sincos_kernel(x) elseif isnan(x) return x, x elseif isinf(x) sincos_domain_error(x) end n, y = rem_pio2_kernel(x) n = n&3 # calculate both kernels at the reduced y... si, co = sincos_kernel(y) # ... and use the same selection scheme as above: (sin, cos, -sin, -cos) for # for sin and (cos, -sin, -cos, sin) for cos if n == 0 return si, co elseif n == 1 return co, -si elseif n == 2 return -si, -co else return -co, si end end _sincos(x::AbstractFloat) = sincos(x) _sincos(x) = (sin(x), cos(x)) sincos(x) = _sincos(float(x)) # There's no need to write specialized kernels, as inlining takes care of remo- # ving superfluous calculations. @inline sincos_kernel(y::Union{Float32, Float64, DoubleFloat32, DoubleFloat64}) = (sin_kernel(y), cos_kernel(y)) # tangent methods @noinline tan_domain_error(x) = throw(DomainError(x, "tan(x) is only defined for finite x.")) function tan(x::T) where T<:Union{Float32, Float64} absx = abs(x) if absx < T(pi)/4 if absx < sqrt(eps(T))/2 # first order dominates, but also allows tan(-0)=-0 return x end return tan_kernel(x) elseif isnan(x) return x elseif isinf(x) tan_domain_error(x) end n, y = rem_pio2_kernel(x) if iseven(n) return tan_kernel(y,1) else return tan_kernel(y,-1) end end @inline tan_kernel(y::Float64) = tan_kernel(DoubleFloat64(y, 0.0), 1) @inline function tan_kernel(y::DoubleFloat64, k) # kernel tan function on ~[-pi/4, pi/4] (except on -0) # Input y is assumed to be bounded by ~pi/4 in magnitude. # Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned. # Algorithm # 1. Since tan(-y) = -tan(y), we need only to consider positive y. # 2. Callers must return tan(-0) = -0 without calling here since our # odd polynomial is not evaluated in a way that preserves -0. # Callers may do the optimization tan(y) ~ y for tiny y. # 3. tan(y) is approximated by a odd polynomial of degree 27 on # [0,0.67434] # 3 27 # tan(y) ~ y + T1*y + ... + T13*y โ‰ก P(y) # where # # |tan(y) 2 4 26 | -59.2 # (tan(y)-P(y))/y = |----- - (1+T1*y +T2*y +.... +T13*y )| <= 2 # | y | # # Note: tan(y+z) = tan(y) + tan'(y)*z # ~ tan(y) + (1+y*y)*z # Therefore, for better accuracy in computing tan(y+z), let # 3 2 2 2 2 # r = y *(T2+y *(T3+y *(...+y *(T12+y *T13)))) # then # 3 2 # tan(y+z) = y + (T1*y + (y *(r+z)+z)) # # 4. For y in [0.67434,pi/4], let z = pi/4 - y, then # tan(y) = tan(pi/4-z) = (1-tan(z))/(1+tan(z)) # = 1 - 2*(tan(z) - (tan(z)^2)/(1+tan(z))) yhi = y.hi ylo = y.lo if abs(yhi) >= 0.6744 if yhi < 0.0 yhi = -yhi ylo = -ylo end # Then, accurately reduce y as "pio4hi"-yhi+"pio4lo"-ylo yhi = (pi/4 - yhi) + (3.06161699786838301793e-17 - ylo) # yhi is guaranteed to be exact, so ylo is identically zero ylo = 0.0 end yยฒ = yhi * yhi yโด = yยฒ * yยฒ # Break P(y)-T1*yยณ = y^5*(T[2]+y^2*T[3]+...) into yโต*r + yโต*v where # r = T[2]+y^4*T[4]+...+y^20*T[12]) # v = (y^2*(T[3]+y^4*T[5]+...+y^22*[T13])) r = @horner(yโด, 1.33333333333201242699e-01, # T2 2.18694882948595424599e-02, # T4 3.59207910759131235356e-03, # T6 5.88041240820264096874e-04, # T8 7.81794442939557092300e-05, # T10 -1.85586374855275456654e-05) # T12 v = yยฒ * @horner(yโด, 5.39682539762260521377e-02, # T3 8.86323982359930005737e-03, # T5 1.45620945432529025516e-03, # T7 2.46463134818469906812e-04, # T9 7.14072491382608190305e-05, # T11 2.59073051863633712884e-05) # T13 # Precompute yยณ yยณ = yยฒ * yhi # Calculate P(y)-y-T1*yยณ = yโต*r + yโต*v = yยฒ(yยณ*(r+v)) r = ylo + yยฒ * (yยณ * (r + v) + ylo) # Calculate P(y)-y = r+T1*yยณ r += 3.33333333333334091986e-01*yยณ # Calculate w = r+y = P(y) Px = yhi + r if abs(y.hi) >= 0.6744 # If the original y was above the threshold, then we calculate # tan(y) = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) # โ‰ˆ 1 - 2*(P(z) - (P(z)^2)/(1+P(z))) # where z = y-ฯ€/4. return (signbit(y.hi) ? -1.0 : 1.0)*(k - 2*(yhi-(Px^2/(k+Px)-r))) end if k == 1 # Else, we simply return w = P(y) if k == 1 (integer multiple from argument # reduction was even)... return Px else # ...or tan(y) โ‰ˆ -1.0/(y+r) if !(k == 1) (integer multiple from argument # reduction was odd). If 2ulp error is allowed, simply return the frac- # tion directly. Instead, we calculate it accurately. # Px0 is w with zeroed out low word Px0 = reinterpret(Float64, (reinterpret(UInt64, Px) >> 32) << 32) v = r - (Px0 - yhi) # Px0+v = r+y t = a = -1.0 / Px # zero out low word of t t = reinterpret(Float64, (reinterpret(UInt64, t) >> 32) << 32) s = 1.0 + t * Px0 return t + a * (s + t * v) end end @inline tan_kernel(y::Float32) = tan_kernel(DoubleFloat32(y), 1) @inline function tan_kernel(y::DoubleFloat32, k) # |tan(y)/y - t(y)| < 2**-25.5 (~[-2e-08, 2e-08]). */ yยฒ = y.hi*y.hi r = @horner(yยฒ, 0.00297435743359967304927, 0.00946564784943673166728) t = @horner(yยฒ, 0.0533812378445670393523, 0.0245283181166547278873) yโด = yยฒ*yยฒ yยณ = yยฒ*y.hi u = @horner(yยฒ, 0.333331395030791399758, 0.133392002712976742718) Py = (y.hi+yยณ*u)+(yยณ*yโด)*(t+yโด*r) if k == 1 return Float32(Py) end return Float32(-1.0/Py) end # fallback methods sin_kernel(x::Real) = sin(x) cos_kernel(x::Real) = cos(x) tan_kernel(x::Real) = tan(x) sincos_kernel(x::Real) = sincos(x) # Inverse trigonometric functions # asin methods ASIN_X_MIN_THRESHOLD(::Type{Float32}) = 2.0f0^-12 ASIN_X_MIN_THRESHOLD(::Type{Float64}) = sqrt(eps(Float64)) arc_p(t::Float64) = t*@horner(t, 1.66666666666666657415e-01, -3.25565818622400915405e-01, 2.01212532134862925881e-01, -4.00555345006794114027e-02, 7.91534994289814532176e-04, 3.47933107596021167570e-05) arc_q(z::Float64) = @horner(z, 1.0, -2.40339491173441421878e+00, 2.02094576023350569471e+00, -6.88283971605453293030e-01, 7.70381505559019352791e-02) arc_p(t::Float32) = t*@horner(t, 1.6666586697f-01, -4.2743422091f-02, -8.6563630030f-03) arc_q(t::Float32) = @horner(t, 1.0f0, -7.0662963390f-01) @inline arc_tRt(t) = arc_p(t)/arc_q(t) @inline function asin_kernel(t::Float64, x::Float64) # we use that for 1/2 <= x < 1 we have # asin(x) = pi/2-2*asin(sqrt((1-x)/2)) # Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; # then for x>0.98 # asin(x) = pi/2 - 2*(s+s*z*R(z)) # = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) # For x<=0.98, let pio4_hi = pio2_hi/2, then # f = hi part of s; # c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) # and # asin(x) = pi/2 - 2*(s+s*z*R(z)) # = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) # = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) pio2_lo = 6.12323399573676603587e-17 s = sqrt_llvm(t) tRt = arc_tRt(t) if abs(x) >= 0.975 # |x| > 0.975 return flipsign(pi/2 - (2.0*(s + s*tRt) - pio2_lo), x) else s0 = reinterpret(Float64, (reinterpret(UInt64, s) >> 32) << 32) c = (t - s0*s0)/(s + s0) p = 2.0*s*tRt - (pio2_lo - 2.0*c) q = pi/4 - 2.0*s0 return flipsign(pi/4 - (p-q), x) end end @inline function asin_kernel(t::Float32, x::Float32) s = sqrt_llvm(Float64(t)) tRt = arc_tRt(t) # rational approximation flipsign(Float32(pi/2 - 2*(s + s*tRt)), x) end @noinline asin_domain_error(x) = throw(DomainError(x, "asin(x) is not defined for |x| > 1.")) function asin(x::T) where T<:Union{Float32, Float64} # Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... # we approximate asin(x) on [0,0.5] by # asin(x) = x + x*x^2*R(x^2) # where # R(x^2) is a rational approximation of (asin(x)-x)/x^3 # and its remez error is bounded by # |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) absx = abs(x) if absx >= T(1.0) # |x|>= 1 if absx == T(1.0) return flipsign(T(pi)/2, x) end asin_domain_error(x) elseif absx < T(1.0)/2 # if |x| sufficiently small, |x| is a good approximation if absx < ASIN_X_MIN_THRESHOLD(T) return x end return muladd(x, arc_tRt(x*x), x) end # else 1/2 <= |x| < 1 t = (T(1.0) - absx)/2 return asin_kernel(t, x) end # atan methods ATAN_1_O_2_HI(::Type{Float64}) = 4.63647609000806093515e-01 # atan(0.5).hi ATAN_2_O_2_HI(::Type{Float64}) = 7.85398163397448278999e-01 # atan(1.0).hi ATAN_3_O_2_HI(::Type{Float64}) = 9.82793723247329054082e-01 # atan(1.5).hi ATAN_INF_HI(::Type{Float64}) = 1.57079632679489655800e+00 # atan(Inf).hi ATAN_1_O_2_HI(::Type{Float32}) = 4.6364760399f-01 # atan(0.5).hi ATAN_2_O_2_HI(::Type{Float32}) = 7.8539812565f-01 # atan(1.0).hi ATAN_3_O_2_HI(::Type{Float32}) = 9.8279368877f-01 # atan(1.5).hi ATAN_INF_HI(::Type{Float32}) = 1.5707962513f+00 # atan(Inf).hi ATAN_1_O_2_LO(::Type{Float64}) = 2.26987774529616870924e-17 # atan(0.5).lo ATAN_2_O_2_LO(::Type{Float64}) = 3.06161699786838301793e-17 # atan(1.0).lo ATAN_3_O_2_LO(::Type{Float64}) = 1.39033110312309984516e-17 # atan(1.5).lo ATAN_INF_LO(::Type{Float64}) = 6.12323399573676603587e-17 # atan(Inf).lo ATAN_1_O_2_LO(::Type{Float32}) = 5.0121582440f-09 # atan(0.5).lo ATAN_2_O_2_LO(::Type{Float32}) = 3.7748947079f-08 # atan(1.0).lo ATAN_3_O_2_LO(::Type{Float32}) = 3.4473217170f-08 # atan(1.5).lo ATAN_INF_LO(::Type{Float32}) = 7.5497894159f-08 # atan(Inf).lo ATAN_LARGE_X(::Type{Float64}) = 2.0^66 # seems too large? 2.0^60 gives the same ATAN_SMALL_X(::Type{Float64}) = 2.0^-27 ATAN_LARGE_X(::Type{Float32}) = 2.0f0^26 ATAN_SMALL_X(::Type{Float32}) = 2.0f0^-12 atan_p(z::Float64, w::Float64) = z*@horner(w, 3.33333333333329318027e-01, 1.42857142725034663711e-01, 9.09088713343650656196e-02, 6.66107313738753120669e-02, 4.97687799461593236017e-02, 1.62858201153657823623e-02) atan_q(w::Float64) = w*@horner(w, -1.99999999998764832476e-01, -1.11111104054623557880e-01, -7.69187620504482999495e-02, -5.83357013379057348645e-02, -3.65315727442169155270e-02) atan_p(z::Float32, w::Float32) = z*@horner(w, 3.3333328366f-01, 1.4253635705f-01, 6.1687607318f-02) atan_q(w::Float32) = w*@horner(w, -1.9999158382f-01, -1.0648017377f-01) @inline function atan_pq(x) xยฒ = x*x xโด = xยฒ*xยฒ # break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly atan_p(xยฒ, xโด), atan_q(xโด) end function atan(x::T) where T<:Union{Float32, Float64} # Method # 1. Reduce x to positive by atan(x) = -atan(-x). # 2. According to the integer k=4t+0.25 chopped, t=x, the argument # is further reduced to one of the following intervals and the # arctangent of t is evaluated by the corresponding formula: # # [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) # [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) # [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) # [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) # [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) # # If isnan(x) is true, then the nan value will eventually be passed to # atan_pq(x) and return the appropriate nan value. absx = abs(x) if absx >= ATAN_LARGE_X(T) return copysign(T(1.5707963267948966), x) end if absx < T(7/16) # no reduction needed if absx < ATAN_SMALL_X(T) return x end p, q = atan_pq(x) return x - x*(p + q) end xsign = sign(x) if absx < T(19/16) # 7/16 <= |x| < 19/16 if absx < T(11/16) # 7/16 <= |x| <11/16 hi = ATAN_1_O_2_HI(T) lo = ATAN_1_O_2_LO(T) x = (T(2.0)*absx - T(1.0))/(T(2.0) + absx) else # 11/16 <= |x| < 19/16 hi = ATAN_2_O_2_HI(T) lo = ATAN_2_O_2_LO(T) x = (absx - T(1.0))/(absx + T(1.0)) end else if absx < T(39/16) # 19/16 <= |x| < 39/16 hi = ATAN_3_O_2_HI(T) lo = ATAN_3_O_2_LO(T) x = (absx - T(1.5))/(T(1.0) + T(1.5)*absx) else # 39/16 <= |x| < upper threshold (2.0^66 or 2.0f0^26) hi = ATAN_INF_HI(T) lo = ATAN_INF_LO(T) x = -T(1.0)/absx end end # end of argument reduction p, q = atan_pq(x) z = hi - ((x*(p + q) - lo) - x) copysign(z, xsign) end # atan2 methods ATAN2_PI_LO(::Type{Float32}) = -8.7422776573f-08 ATAN2_RATIO_BIT_SHIFT(::Type{Float32}) = 23 ATAN2_RATIO_THRESHOLD(::Type{Float32}) = 26 ATAN2_PI_LO(::Type{Float64}) = 1.2246467991473531772E-16 ATAN2_RATIO_BIT_SHIFT(::Type{Float64}) = 20 ATAN2_RATIO_THRESHOLD(::Type{Float64}) = 60 function atan(y::T, x::T) where T<:Union{Float32, Float64} # Method : # M1) Reduce y to positive by atan2(y,x)=-atan2(-y,x). # M2) Reduce x to positive by (if x and y are unexceptional): # ARG (x+iy) = arctan(y/x) ... if x > 0, # ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, # # Special cases: # # S1) ATAN2((anything), NaN ) is NaN; # S2) ATAN2(NAN , (anything) ) is NaN; # S3) ATAN2(+-0, +(anything but NaN)) is +-0 ; # S4) ATAN2(+-0, -(anything but NaN)) is +-pi ; # S5) ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; # S6) ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; # S7) ATAN2(+-(anything but INF and NaN), -INF) is +-pi; # S8) ATAN2(+-INF,+INF ) is +-pi/4 ; # S9) ATAN2(+-INF,-INF ) is +-3pi/4; # S10) ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; if isnan(x) | isnan(y) # S1 or S2 return isnan(x) ? x : y end if x == T(1.0) # then y/x = y and x > 0, see M2 return atan(y) end # generate an m โˆˆ {0, 1, 2, 3} to branch off of m = 2*signbit(x) + 1*signbit(y) if iszero(y) if m == 0 || m == 1 return y # atan(+-0, +anything) = +-0 elseif m == 2 return T(pi) # atan(+0, -anything) = pi elseif m == 3 return -T(pi) # atan(-0, -anything) =-pi end elseif iszero(x) return flipsign(T(pi)/2, y) end if isinf(x) if isinf(y) if m == 0 return T(pi)/4 # atan(+Inf), +Inf)) elseif m == 1 return -T(pi)/4 # atan(-Inf), +Inf)) elseif m == 2 return 3*T(pi)/4 # atan(+Inf, -Inf) elseif m == 3 return -3*T(pi)/4 # atan(-Inf,-Inf) end else if m == 0 return zero(T) # atan(+...,+Inf) */ elseif m == 1 return -zero(T) # atan(-...,+Inf) */ elseif m == 2 return T(pi) # atan(+...,-Inf) */ elseif m == 3 return -T(pi) # atan(-...,-Inf) */ end end end # x wasn't Inf, but y is isinf(y) && return copysign(T(pi)/2, y) ypw = poshighword(y) xpw = poshighword(x) # compute y/x for Float32 k = reinterpret(Int32, ypw-xpw)>>ATAN2_RATIO_BIT_SHIFT(T) if k > ATAN2_RATIO_THRESHOLD(T) # |y/x| > threshold z=T(pi)/2+T(0.5)*ATAN2_PI_LO(T) m&=1; elseif x<0 && k < -ATAN2_RATIO_THRESHOLD(T) # 0 > |y|/x > threshold z = zero(T) else #safe to do y/x z = atan(abs(y/x)) end if m == 0 return z # atan(+,+) elseif m == 1 return -z # atan(-,+) elseif m == 2 return T(pi)-(z-ATAN2_PI_LO(T)) # atan(+,-) else # default case m == 3 return (z-ATAN2_PI_LO(T))-T(pi) # atan(-,-) end end # acos methods ACOS_X_MIN_THRESHOLD(::Type{Float32}) = 2.0f0^-26 ACOS_X_MIN_THRESHOLD(::Type{Float64}) = 2.0^-57 PIO2_HI(::Type{Float32}) = 1.5707962513f+00 PIO2_LO(::Type{Float32}) = 7.5497894159f-08 PIO2_HI(::Type{Float64}) = 1.57079632679489655800e+00 PIO2_LO(::Type{Float64}) = 6.12323399573676603587e-17 ACOS_PI(::Type{Float32}) = 3.1415925026f+00 ACOS_PI(::Type{Float64}) = 3.14159265358979311600e+00 @inline ACOS_CORRECT_LOWWORD(::Type{Float32}, x) = reinterpret(Float32, (reinterpret(UInt32, x) & 0xfffff000)) @inline ACOS_CORRECT_LOWWORD(::Type{Float64}, x) = reinterpret(Float64, (reinterpret(UInt64, x) >> 32) << 32) @noinline acos_domain_error(x) = throw(DomainError(x, "acos(x) not defined for |x| > 1")) function acos(x::T) where T <: Union{Float32, Float64} # Method : # acos(x) = pi/2 - asin(x) # acos(-x) = pi/2 + asin(x) # As a result, we use the same rational approximation (arc_tRt) as in asin. # See the comments in asin for more information about this approximation. # 1) For |x| <= 0.5 # acos(x) = pi/2 - (x + x*x^2*R(x^2)) # 2) For x < -0.5 # acos(x) = pi - 2asin(sqrt((1 - |x|)/2)) # = pi - 0.5*(s+s*z*R(z)) # where z=(1-|x|)/2, s=sqrt(z) # 3) For x > 0.5 # acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1 - x)/2))) # = 2asin(sqrt((1 - x)/2)) # = 2s + 2s*z*R(z) ...z=(1 - x)/2, s=sqrt(z) # = 2f + (2c + 2s*z*R(z)) # where f=hi part of s, and c = (z - f*f)/(s + f) is the correction term # for f so that f + c ~ sqrt(z). # Special cases: # 4) if x is NaN, return x itself; # 5) if |x|>1 throw warning. absx = abs(x) if absx >= T(1.0) # acos(-1) = ฯ€, acos(1) = 0 absx == T(1.0) && return x > T(0.0) ? T(0.0) : T(pi) # acos(x) is not defined for |x| > 1 acos_domain_error(x) # see 5) above elseif absx < T(1.0)/2 # see 1) above # if |x| sufficiently small, acos(x) โ‰ˆ pi/2 absx < ACOS_X_MIN_THRESHOLD(T) && return T(pi)/2 # if |x| < 0.5 we have acos(x) = pi/2 - (x + x*x^2*R(x^2)) return PIO2_HI(T) - (x - (PIO2_LO(T) - x*arc_tRt(x*x))) end z = (T(1.0) - absx)*T(0.5) zRz = arc_tRt(z) s = sqrt_llvm(z) if x < T(0.0) # see 2) above return ACOS_PI(T) - T(2.0)*(s + (zRz*s - PIO2_LO(T))) else # see 3) above # if x > 0.5 we have # acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) # = 2asin(sqrt((1-x)/2)) # = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) # = 2f + (2c + 2s*z*R(z)) # where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term # for f so that f+c ~ sqrt(z). df = ACOS_CORRECT_LOWWORD(T, s) c = (z - df*df)/(s + df) return T(2.0)*(df + (zRz*s + c)) end end # Uses minimax polynomial of sin(ฯ€ * x) for ฯ€ * x in [0, .25] @inline function sinpi_kernel(x::Float64) sinpi_kernel_wide(x) end @inline function sinpi_kernel_wide(x::Float64) xยฒ = x*x xโด = xยฒ*xยฒ r = evalpoly(xยฒ, (2.5501640398773415, -0.5992645293202981, 0.08214588658006512, -7.370429884921779e-3, 4.662827319453555e-4, -2.1717412523382308e-5)) return muladd(3.141592653589793, x, x*muladd(-5.16771278004997, xยฒ, muladd(xโด, r, 1.2245907532225998e-16))) end @inline function sinpi_kernel(x::Float32) Float32(sinpi_kernel_wide(x)) end @inline function sinpi_kernel_wide(x::Float32) x = Float64(x) return x*evalpoly(x*x, (3.1415926535762266, -5.167712769188119, 2.5501626483206374, -0.5992021090314925, 0.08100185277841528)) end @inline function sinpi_kernel(x::Float16) Float16(sinpi_kernel_wide(x)) end @inline function sinpi_kernel_wide(x::Float16) x = Float32(x) return x*evalpoly(x*x, (3.1415927f0, -5.1677127f0, 2.5501626f0, -0.5992021f0, 0.081001855f0)) end # Uses minimax polynomial of cos(ฯ€ * x) for ฯ€ * x in [0, .25] @inline function cospi_kernel(x::Float64) cospi_kernel_wide(x) end @inline function cospi_kernel_wide(x::Float64) xยฒ = x*x r = xยฒ*evalpoly(xยฒ, (4.058712126416765, -1.3352627688537357, 0.23533063027900392, -0.025806887811869204, 1.9294917136379183e-3, -1.0368935675474665e-4)) a_xยฒ = 4.934802200544679 * xยฒ a_xยฒlo = muladd(3.109686485461973e-16, xยฒ, muladd(4.934802200544679, xยฒ, -a_xยฒ)) w = 1.0-a_xยฒ return w + muladd(xยฒ, r, ((1.0-w)-a_xยฒ) - a_xยฒlo) end @inline function cospi_kernel(x::Float32) Float32(cospi_kernel_wide(x)) end @inline function cospi_kernel_wide(x::Float32) x = Float64(x) return evalpoly(x*x, (1.0, -4.934802200541122, 4.058712123568637, -1.3352624040152927, 0.23531426791507182, -0.02550710082498761)) end @inline function cospi_kernel(x::Float16) Float16(cospi_kernel_wide(x)) end @inline function cospi_kernel_wide(x::Float16) x = Float32(x) return evalpoly(x*x, (1.0f0, -4.934802f0, 4.058712f0, -1.3352624f0, 0.23531426f0, -0.0255071f0)) end """ sinpi(x) Compute ``\\sin(\\pi x)`` more accurately than `sin(pi*x)`, especially for large `x`. See also [`sind`](@ref), [`cospi`](@ref), [`sincospi`](@ref). """ function sinpi(_x::T) where T<:IEEEFloat x = abs(_x) if !isfinite(x) isnan(x) && return x throw(DomainError(x, "`x` cannot be infinite.")) end # For large x, answers are all 1 or zero. x >= maxintfloat(T) && return copysign(zero(T), _x) # reduce to interval [0, 0.5] n = round(2*x) rx = float(muladd(T(-.5), n, x)) n = Int64(n) & 3 if n==0 res = sinpi_kernel(rx) elseif n==1 res = cospi_kernel(rx) elseif n==2 res = zero(T)-sinpi_kernel(rx) else res = zero(T)-cospi_kernel(rx) end return ifelse(signbit(_x), -res, res) end """ cospi(x) Compute ``\\cos(\\pi x)`` more accurately than `cos(pi*x)`, especially for large `x`. """ function cospi(x::T) where T<:IEEEFloat x = abs(x) if !isfinite(x) isnan(x) && return x throw(DomainError(x, "`x` cannot be infinite.")) end # For large x, answers are all 1 or zero. x >= maxintfloat(T) && return one(T) # reduce to interval [0, 0.5] n = round(2*x) rx = float(muladd(T(-.5), n, x)) n = Int64(n) & 3 if n==0 return cospi_kernel(rx) elseif n==1 return zero(T)-sinpi_kernel(rx) elseif n==2 return zero(T)-cospi_kernel(rx) else return sinpi_kernel(rx) end end """ sincospi(x) Simultaneously compute [`sinpi(x)`](@ref) and [`cospi(x)`](@ref) (the sine and cosine of `ฯ€*x`, where `x` is in radians), returning a tuple `(sine, cosine)`. !!! compat "Julia 1.6" This function requires Julia 1.6 or later. See also: [`cispi`](@ref), [`sincosd`](@ref), [`sinpi`](@ref). """ function sincospi(_x::T) where T<:IEEEFloat x = abs(_x) if !isfinite(x) isnan(x) && return x, x throw(DomainError(x, "`x` cannot be infinite.")) end # For large x, answers are all 1 or zero. x >= maxintfloat(T) && return (copysign(zero(T), _x), one(T)) # reduce to interval [0, 0.5] n = round(2*x) rx = float(muladd(T(-.5), n, x)) n = Int64(n) & 3 si, co = sinpi_kernel(rx),cospi_kernel(rx) if n==0 si, co = si, co elseif n==1 si, co = co, zero(T)-si elseif n==2 si, co = zero(T)-si, zero(T)-co else si, co = zero(T)-co, si end si = ifelse(signbit(_x), -si, si) return si, co end """ tanpi(x) Compute ``\\tan(\\pi x)`` more accurately than `tan(pi*x)`, especially for large `x`. !!! compat "Julia 1.10" This function requires at least Julia 1.10. See also [`tand`](@ref), [`sinpi`](@ref), [`cospi`](@ref), [`sincospi`](@ref). """ function tanpi(_x::T) where T<:IEEEFloat # This is modified from sincospi. # Would it be faster or more accurate to make a tanpi_kernel? x = abs(_x) if !isfinite(x) isnan(x) && return x throw(DomainError(x, "`x` cannot be infinite.")) end # For large x, answers are all zero. # All integer values for floats larger than maxintfloat are even. x >= maxintfloat(T) && return copysign(zero(T), _x) # reduce to interval [0, 0.5] n = round(2*x) rx = float(muladd(T(-.5), n, x)) n = Int64(n) & 3 si, co = sinpi_kernel_wide(rx), cospi_kernel_wide(rx) if n==0 si, co = si, co elseif n==1 si, co = co, zero(T)-si elseif n==2 si, co = zero(T)-si, zero(T)-co else si, co = zero(T)-co, si end si = ifelse(signbit(_x), -si, si) return float(T)(si / co) end sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x)) cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x)) tanpi(x::Integer) = x >= 0 ? (isodd(x) ? -zero(float(x)) : zero(float(x))) : (isodd(x) ? zero(float(x)) : -zero(float(x))) sincospi(x::Integer) = (sinpi(x), cospi(x)) sinpi(x::AbstractFloat) = sin(pi*x) cospi(x::AbstractFloat) = cos(pi*x) sincospi(x::AbstractFloat) = sincos(pi*x) tanpi(x::AbstractFloat) = tan(pi*x) tanpi(x::Complex) = sinpi(x) / cospi(x) # Is there a better way to do this? function sinpi(z::Complex{T}) where T F = float(T) zr, zi = reim(z) if isinteger(zr) # zr = ...,-2,-1,0,1,2,... # sin(pi*zr) == ยฑ0 # cos(pi*zr) == ยฑ1 # cosh(pi*zi) > 0 s = copysign(zero(F),zr) c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) sh = sinh(pi*zi) Complex(s, c_pos ? sh : -sh) elseif isinteger(2*zr) # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... # sin(pi*zr) == ยฑ1 # cos(pi*zr) == +0 # sign(sinh(pi*zi)) == sign(zi) s_pos = isinteger((2*zr-1)/4) ch = cosh(pi*zi) Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)) elseif !isfinite(zr) if zi == 0 || isinf(zi) Complex(F(NaN), F(zi)) else Complex(F(NaN), F(NaN)) end else pizi = pi*zi sipi, copi = sincospi(zr) Complex(sipi*cosh(pizi), copi*sinh(pizi)) end end function cospi(z::Complex{T}) where T F = float(T) zr, zi = reim(z) if isinteger(zr) # zr = ...,-2,-1,0,1,2,... # sin(pi*zr) == ยฑ0 # cos(pi*zr) == ยฑ1 # sign(sinh(pi*zi)) == sign(zi) # cosh(pi*zi) > 0 s = copysign(zero(F),zr) c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) ch = cosh(pi*zi) Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)) elseif isinteger(2*zr) # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... # sin(pi*zr) == ยฑ1 # cos(pi*zr) == +0 # sign(sinh(pi*zi)) == sign(zi) s_pos = isinteger((2*zr-1)/4) sh = sinh(pi*zi) Complex(zero(F), s_pos ? -sh : sh) elseif !isfinite(zr) if zi == 0 Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) elseif isinf(zi) Complex(F(Inf), F(NaN)) else Complex(F(NaN), F(NaN)) end else pizi = pi*zi sipi, copi = sincospi(zr) Complex(copi*cosh(pizi), -sipi*sinh(pizi)) end end function sincospi(z::Complex{T}) where T F = float(T) zr, zi = reim(z) if isinteger(zr) # zr = ...,-2,-1,0,1,2,... # sin(pi*zr) == ยฑ0 # cos(pi*zr) == ยฑ1 # cosh(pi*zi) > 0 s = copysign(zero(F),zr) c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) pizi = pi*zi sh, ch = sinh(pizi), cosh(pizi) ( Complex(s, c_pos ? sh : -sh), Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)), ) elseif isinteger(2*zr) # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... # sin(pi*zr) == ยฑ1 # cos(pi*zr) == +0 # sign(sinh(pi*zi)) == sign(zi) s_pos = isinteger((2*zr-1)/4) pizi = pi*zi sh, ch = sinh(pizi), cosh(pizi) ( Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)), Complex(zero(F), s_pos ? -sh : sh), ) elseif !isfinite(zr) if zi == 0 Complex(F(NaN), F(zi)), Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) elseif isinf(zi) Complex(F(NaN), F(zi)), Complex(F(Inf), F(NaN)) else Complex(F(NaN), F(NaN)), Complex(F(NaN), F(NaN)) end else pizi = pi*zi sipi, copi = sincospi(zr) sihpi, cohpi = sinh(pizi), cosh(pizi) ( Complex(sipi*cohpi, copi*sihpi), Complex(copi*cohpi, -sipi*sihpi), ) end end """ fastabs(x::Number) Faster `abs`-like function for rough magnitude comparisons. `fastabs` is equivalent to `abs(x)` for most `x`, but for complex `x` it computes `abs(real(x))+abs(imag(x))` rather than requiring `hypot`. """ fastabs(x::Number) = abs(x) fastabs(z::Complex) = abs(real(z)) + abs(imag(z)) # sinc and cosc are zero if the real part is Inf and imag is finite isinf_real(x::Real) = isinf(x) isinf_real(x::Complex) = isinf(real(x)) && isfinite(imag(x)) isinf_real(x::Number) = false """ sinc(x) Compute ``\\sin(\\pi x) / (\\pi x)`` if ``x \\neq 0``, and ``1`` if ``x = 0``. See also [`cosc`](@ref), its derivative. """ sinc(x::Number) = _sinc(float(x)) sinc(x::Integer) = iszero(x) ? one(x) : zero(x) _sinc(x::Number) = iszero(x) ? one(x) : isinf_real(x) ? zero(x) : sinpi(x)/(pi*x) _sinc_threshold(::Type{Float64}) = 0.001 _sinc_threshold(::Type{Float32}) = 0.05f0 @inline _sinc(x::Union{T,Complex{T}}) where {T<:Union{Float32,Float64}} = fastabs(x) < _sinc_threshold(T) ? evalpoly(x^2, (T(1), -T(pi)^2/6, T(pi)^4/120)) : isinf_real(x) ? zero(x) : sinpi(x)/(pi*x) _sinc(x::Float16) = Float16(_sinc(Float32(x))) _sinc(x::ComplexF16) = ComplexF16(_sinc(ComplexF32(x))) """ cosc(x) Compute ``\\cos(\\pi x) / x - \\sin(\\pi x) / (\\pi x^2)`` if ``x \\neq 0``, and ``0`` if ``x = 0``. This is the derivative of `sinc(x)`. """ cosc(x::Number) = _cosc(float(x)) function _cosc(x::Number) # naive cosc formula is susceptible to catastrophic # cancellation error near x=0, so we use the Taylor series # for small enough |x|. if fastabs(x) < 0.5 # generic Taylor series: ฯ€ โˆ‘ (-1)^n (ฯ€x)^{2n-1}/a(n) where # a(n) = (1+2n)*(2n-1)! (= OEIS A174549) s = (term = -(ฯ€*x))/3 ฯ€ยฒxยฒ = term^2 ฮต = eps(fastabs(term)) # error threshold to stop sum n = 1 while true n += 1 term *= ฯ€ยฒxยฒ/((1-2n)*(2n-2)) s += (ฮดs = term/(1+2n)) fastabs(ฮดs) โ‰ค ฮต && break end return ฯ€*s else return isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x) end end # hard-code Float64/Float32 Taylor series, with coefficients # Float64.([(-1)^n*big(pi)^(2n)/((2n+1)*factorial(2n-1)) for n = 1:6]) _cosc(x::Union{Float64,ComplexF64}) = fastabs(x) < 0.14 ? x*evalpoly(x^2, (-3.289868133696453, 3.2469697011334144, -1.1445109447325053, 0.2091827825412384, -0.023460810354558236, 0.001781145516372852)) : isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x) _cosc(x::Union{Float32,ComplexF32}) = fastabs(x) < 0.26f0 ? x*evalpoly(x^2, (-3.289868f0, 3.2469697f0, -1.144511f0, 0.20918278f0)) : isinf_real(x) ? zero(x) : ((pi*x)*cospi(x)-sinpi(x))/((pi*x)*x) _cosc(x::Float16) = Float16(_cosc(Float32(x))) _cosc(x::ComplexF16) = ComplexF16(_cosc(ComplexF32(x))) for (finv, f, finvh, fh, finvd, fd, fn) in ((:sec, :cos, :sech, :cosh, :secd, :cosd, "secant"), (:csc, :sin, :csch, :sinh, :cscd, :sind, "cosecant"), (:cot, :tan, :coth, :tanh, :cotd, :tand, "cotangent")) name = string(finv) hname = string(finvh) dname = string(finvd) @eval begin @doc """ $($name)(x) Compute the $($fn) of `x`, where `x` is in radians. """ ($finv)(z::Number) = inv(($f)(z)) @doc """ $($hname)(x) Compute the hyperbolic $($fn) of `x`. """ ($finvh)(z::Number) = inv(($fh)(z)) @doc """ $($dname)(x) Compute the $($fn) of `x`, where `x` is in degrees. """ ($finvd)(z::Number) = inv(($fd)(z)) end end for (tfa, tfainv, hfa, hfainv, fn) in ((:asec, :acos, :asech, :acosh, "secant"), (:acsc, :asin, :acsch, :asinh, "cosecant"), (:acot, :atan, :acoth, :atanh, "cotangent")) tname = string(tfa) hname = string(hfa) @eval begin @doc """ $($tname)(x) Compute the inverse $($fn) of `x`, where the output is in radians. """ ($tfa)(y::Number) = ($tfainv)(inv(y)) @doc """ $($hname)(x) Compute the inverse hyperbolic $($fn) of `x`. """ ($hfa)(y::Number) = ($hfainv)(inv(y)) end end # multiply in extended precision function deg2rad_ext(x::Float64) m = 0.017453292519943295 m_hi = 0.01745329238474369 m_lo = 1.3519960527851425e-10 u = 134217729.0*x # 0x1p27 + 1 x_hi = u-(u-x) x_lo = x-x_hi y_hi = m*x y_lo = x_hi * m_lo + (x_lo* m_hi + ((x_hi*m_hi-y_hi) + x_lo*m_lo)) DoubleFloat64(y_hi,y_lo) end deg2rad_ext(x::Float32) = DoubleFloat32(deg2rad(Float64(x))) deg2rad_ext(x::Real) = deg2rad(x) # Fallback function sind(x::Real) if isinf(x) return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) return x end rx = copysign(float(rem(x,360)),x) arx = abs(rx) if rx == zero(rx) return rx elseif arx < oftype(rx,45) return sin_kernel(deg2rad_ext(rx)) elseif arx <= oftype(rx,135) y = deg2rad_ext(oftype(rx,90) - arx) return copysign(cos_kernel(y),rx) elseif arx == oftype(rx,180) return copysign(zero(rx),rx) elseif arx < oftype(rx,225) y = deg2rad_ext((oftype(rx,180) - arx)*sign(rx)) return sin_kernel(y) elseif arx <= oftype(rx,315) y = deg2rad_ext(oftype(rx,270) - arx) return -copysign(cos_kernel(y),rx) else y = deg2rad_ext(rx - copysign(oftype(rx,360),rx)) return sin_kernel(y) end end function cosd(x::Real) if isinf(x) return throw(DomainError(x, "`x` cannot be infinite.")) elseif isnan(x) return x end rx = abs(float(rem(x,360))) if rx <= oftype(rx,45) return cos_kernel(deg2rad_ext(rx)) elseif rx < oftype(rx,135) y = deg2rad_ext(oftype(rx,90) - rx) return sin_kernel(y) elseif rx <= oftype(rx,225) y = deg2rad_ext(oftype(rx,180) - rx) return -cos_kernel(y) elseif rx < oftype(rx,315) y = deg2rad_ext(rx - oftype(rx,270)) return sin_kernel(y) else y = deg2rad_ext(oftype(rx,360) - rx) return cos_kernel(y) end end tand(x::Real) = sind(x) / cosd(x) """ sincosd(x) Simultaneously compute the sine and cosine of `x`, where `x` is in degrees. !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ sincosd(x) = (sind(x), cosd(x)) # It turns out that calling these functions separately yields better # performance than considering each case and calling `sincos_kernel`. sincosd(::Missing) = (missing, missing) for (fd, f, fn) in ((:sind, :sin, "sine"), (:cosd, :cos, "cosine"), (:tand, :tan, "tangent")) for (fu, un) in ((:deg2rad, "degrees"),) name = string(fd) @eval begin @doc """ $($name)(x) Compute $($fn) of `x`, where `x` is in $($un). If `x` is a matrix, `x` needs to be a square matrix. !!! compat "Julia 1.7" Matrix arguments require Julia 1.7 or later. """ ($fd)(x) = ($f)(($fu).(x)) end end end for (fd, f, fn) in ((:asind, :asin, "sine"), (:acosd, :acos, "cosine"), (:asecd, :asec, "secant"), (:acscd, :acsc, "cosecant"), (:acotd, :acot, "cotangent")) for (fu, un) in ((:rad2deg, "degrees"),) name = string(fd) @eval begin @doc """ $($name)(x) Compute the inverse $($fn) of `x`, where the output is in $($un). If `x` is a matrix, `x` needs to be a square matrix. !!! compat "Julia 1.7" Matrix arguments require Julia 1.7 or later. """ ($fd)(x) = ($fu).(($f)(x)) end end end """ atand(y) atand(y,x) Compute the inverse tangent of `y` or `y/x`, respectively, where the output is in degrees. !!! compat "Julia 1.7" The one-argument method supports square matrix arguments as of Julia 1.7. """ atand(y) = rad2deg.(atan(y)) atand(y, x) = rad2deg.(atan(y,x)) W/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/rem_pio2.jl”&# This file is a part of Julia. Except for the rem_pio2_kernel, and # cody_waite_* methods (see below) license is MIT: https://julialang.org/license # rem_pio2_kernel and cody_waite_* methods are heavily based on FDLIBM code: # __ieee754_rem_pio2, that is made available under the following licence: ## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. ## ## Developed at SunPro, a Sun Microsystems, Inc. business. ## Permission to use, copy, modify, and distribute this ## software is freely granted, provided that this notice ## is preserved. # Bits of 1/2ฯ€ # 1/2ฯ€ == sum(x / 0x1p64^i for i,x = enumerate(INV_2PI)) # Can be obtained by: # # setprecision(BigFloat, 4096) # I = 0.5/big(pi) # for i = 1:19 # I *= 0x1p64 # k = trunc(UInt64, I) # @printf "0x%016x,\n" k # I -= k # end const INV_2PI = ( 0x28be_60db_9391_054a, 0x7f09_d5f4_7d4d_3770, 0x36d8_a566_4f10_e410, 0x7f94_58ea_f7ae_f158, 0x6dc9_1b8e_9093_74b8, 0x0192_4bba_8274_6487, 0x3f87_7ac7_2c4a_69cf, 0xba20_8d7d_4bae_d121, 0x3a67_1c09_ad17_df90, 0x4e64_758e_60d4_ce7d, 0x2721_17e2_ef7e_4a0e, 0xc7fe_25ff_f781_6603, 0xfbcb_c462_d682_9b47, 0xdb4d_9fb3_c9f2_c26d, 0xd3d1_8fd9_a797_fa8b, 0x5d49_eeb1_faf9_7c5e, 0xcf41_ce7d_e294_a4ba, 0x9afe_d7ec_47e3_5742, 0x1580_cc11_bf1e_daea) @inline function cody_waite_2c_pio2(x::Float64, fn, n) pio2_1 = 1.57079632673412561417e+00 pio2_1t = 6.07710050650619224932e-11 z = muladd(-fn, pio2_1, x) # x - fn*pio2_1 y1 = muladd(-fn, pio2_1t, z) # z - fn*pio2_1t y2 = muladd(-fn, pio2_1t, (z - y1)) # (z - y1) - fn*pio2_1t n, DoubleFloat64(y1, y2) end @inline function cody_waite_ext_pio2(x::Float64, xhp) pio2_1 = 1.57079632673412561417e+00 pio2_1t = 6.07710050650619224932e-11 pio2_2 = 6.07710050630396597660e-11 pio2_2t = 2.02226624879595063154e-21 pio2_3 = 2.02226624871116645580e-21 pio2_3t = 8.47842766036889956997e-32 fn = round(x*(2/pi)) # round to integer # on older systems, the above could be faster with # rf = 1.5/eps(Float64) # fn = (x*(2/pi)+rf)-rf r = muladd(-fn, pio2_1, x) # x - fn*pio2_1 w = fn*pio2_1t # 1st round good to 85 bit j = xhp>>20 y1 = r-w high = highword(y1) i = j-((high>>20)&0x7ff) if i>16 # 2nd iteration needed, good to 118 t = r w = fn*pio2_2 r = t-w w = muladd(fn, pio2_2t,-((t-r)-w)) y1 = r-w high = highword(y1) i = j-((high>>20)&0x7ff) if i>49 # 3rd iteration need, 151 bits acc t = r # will cover all possible cases w = fn*pio2_3 r = t-w w = muladd(fn, pio2_3t, -((t-r)-w)) y1 = r-w end end y2 = (r-y1)-w return unsafe_trunc(Int, fn), DoubleFloat64(y1, y2) end """ fromfraction(f::Int128) Compute a tuple of values `(z1,z2)` such that ``z1 + z2 == f / 2^128`` and the significand of `z1` has 27 trailing zeros. """ function fromfraction(f::Int128) if f == 0 return (0.0,0.0) end # 1. get leading term truncated to 26 bits s = ((f < 0) % UInt64) << 63 # sign bit x = abs(f) % UInt128 # magnitude n1 = Base.top_set_bit(x) # ndigits0z(x,2) m1 = ((x >> (n1-26)) % UInt64) << 27 d1 = ((n1-128+1021) % UInt64) << 52 z1 = reinterpret(Float64, s | (d1 + m1)) # 2. compute remaining term x2 = (x - (UInt128(m1) << (n1-53))) if x2 == 0 return (z1, 0.0) end n2 = Base.top_set_bit(x2) m2 = (x2 >> (n2-53)) % UInt64 d2 = ((n2-128+1021) % UInt64) << 52 z2 = reinterpret(Float64, s | (d2 + m2)) return (z1,z2) end # XXX we want to mark :consistent-cy here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the accesses to `INV_2PI` are really safe here Base.@assume_effects :consistent function paynehanek(x::Float64) # 1. Convert to form # # x = X * 2^k, # # where 2^(n-1) <= X < 2^n is an n-bit integer (n = 53, k = exponent(x)-52 ) # Computations are integer based, so reinterpret x as UInt64 u = reinterpret(UInt64, x) # Strip x of exponent bits and replace with ^1 X = (u & significand_mask(Float64)) | (one(UInt64) << significand_bits(Float64)) # Get k from formula above # k = exponent(x)-52 raw_exponent = ((u & exponent_mask(Float64)) >> significand_bits(Float64)) % Int k = raw_exponent - exponent_bias(Float64) - significand_bits(Float64) # 2. Let ฮฑ = 1/2ฯ€, then: # # ฮฑ*x mod 1 โ‰ก [(ฮฑ*2^k mod 1)*X] mod 1 # # so we can ignore the first k bits of ฮฑ. Extract the next 3 64-bit parts of ฮฑ. # # i.e. equivalent to # setprecision(BigFloat,4096) # ฮฑ = 1/(2*big(pi)) # A = mod(ldexp(ฮฑ,k), 1) # z1 = ldexp(A,64) # a1 = trunc(UInt64, z1) # z2 = ldexp(z1-a1, 64) # a2 = trunc(UInt64, z2) # z3 = ldexp(z2-a2, 64) # a3 = trunc(UInt64, z3) # This is equivalent to # idx, shift = divrem(k, 64) # but divrem is slower. idx = k >> 6 shift = k - (idx << 6) if shift == 0 @inbounds a1 = INV_2PI[idx+1] @inbounds a2 = INV_2PI[idx+2] @inbounds a3 = INV_2PI[idx+3] else # use shifts to extract the relevant 64 bit window @inbounds a1 = (idx < 0 ? zero(UInt64) : INV_2PI[idx+1] << shift) | (INV_2PI[idx+2] >> (64 - shift)) @inbounds a2 = (INV_2PI[idx+2] << shift) | (INV_2PI[idx+3] >> (64 - shift)) @inbounds a3 = (INV_2PI[idx+3] << shift) | (INV_2PI[idx+4] >> (64 - shift)) end # 3. Perform the multiplication: # # X. 0 0 0 # ร— 0. a1 a2 a3 # ============== # _. w w _ # # (i.e. ignoring integer and lowest bit parts of result) w1 = UInt128(X*a1) << 64 # overflow becomes integer w2 = widemul(X,a2) w3 = widemul(X,a3) >> 64 w = w1 + w2 + w3 # quotient fraction after division by 2ฯ€ # adjust for sign of x w = flipsign(w,x) # 4. convert to quadrant, quotient fraction after division by ฯ€/2: q = (((w>>125)%Int +1)>>1) # nearest quadrant f = (w<<2) % Int128 # fraction part of quotient after division by ฯ€/2, taking values on [-0.5,0.5) # 5. convert quotient fraction to split precision Float64 z_hi,z_lo = fromfraction(f) # 6. multiply by ฯ€/2 pio2 = 1.5707963267948966 pio2_hi = 1.5707963407039642 pio2_lo = -1.3909067614167116e-8 y_hi = (z_hi+z_lo)*pio2 y_lo = (((z_hi*pio2_hi - y_hi) + z_hi*pio2_lo) + z_lo*pio2_hi) + z_lo*pio2_lo return q, DoubleFloat64(y_hi, y_lo) end """ rem_pio2_kernel(x::Union{Float32, Float64}) Calculate `x` divided by `ฯ€/2` accurately for arbitrarily large `x`. Returns a pair `(k, r)`, where `k` is the quadrant of the result (multiple of ฯ€/2) and `r` is the remainder, such that ``k * ฯ€/2 = x - r``. The remainder is given as a double-double pair. `k` is positive if `x > 0` and is negative if `x โ‰ค 0`. """ @inline function rem_pio2_kernel(x::Float64) # accurate to 1e-22 xhp = poshighword(x) # xhp <= highword(5pi/4) implies |x| ~<= 5pi/4, if xhp <= 0x400f6a7a # last five bits of xhp == last five bits of highword(pi/2) or # highword(2pi/2) implies |x| ~= pi/2 or 2pi/2, if (xhp & 0xfffff) == 0x921fb # use precise Cody Waite scheme return cody_waite_ext_pio2(x, xhp) end # use Cody Waite with two constants # xhp <= highword(3pi/4) implies |x| ~<= 3pi/4 if xhp <= 0x4002d97c if x > 0.0 return cody_waite_2c_pio2(x, 1.0, 1) else return cody_waite_2c_pio2(x, -1.0, -1) end # 3pi/4 < |x| <= 5pi/4 else if x > 0.0 return cody_waite_2c_pio2(x, 2.0, 2) else return cody_waite_2c_pio2(x, -2.0, -2) end end end # xhp <= highword(9pi/4) implies |x| ~<= 9pi/4 if xhp <= 0x401c463b # xhp <= highword(7pi/4) implies |x| ~<= 7pi/4 if xhp <= 0x4015fdbc # xhp == highword(3pi/2) implies |x| ~= 3pi/2 if xhp == 0x4012d97c # use precise Cody Waite scheme return cody_waite_ext_pio2(x, xhp) end # use Cody Waite with two constants if x > 0.0 return cody_waite_2c_pio2(x, 3.0, 3) else return cody_waite_2c_pio2(x, -3.0, -3) end # 7pi/4 < |x| =< 9pi/4 else # xhp == highword(4pi/2) implies |x| ~= 4pi/2 if xhp == 0x401921fb # use precise Cody Waite scheme return cody_waite_ext_pio2(x, xhp) end # use Cody Waite with two constants if x > 0.0 return cody_waite_2c_pio2(x, 4.0, 4) else return cody_waite_2c_pio2(x, -4.0, -4) end end end # xhp < highword(2.0^20*pi/2) implies |x| ~< 2^20*pi/2 if xhp < 0x413921fb # use precise Cody Waite scheme return cody_waite_ext_pio2(x, xhp) end # if |x| >= 2^20*pi/2 switch to Payne Hanek return paynehanek(x) end @inline function rem_pio2_kernel(x::Float32) xd = convert(Float64, x) # use Cody Waite reduction with two coefficients if abs(x) < Float32(pi*0x1p27) # x < 2^28 * pi/2 fn = round(xd * (2/pi)) r = fma(fn, -pi/2, xd) y = fma(fn, -6.123233995736766e-17, r) # big(pi)/2 - pi/2 remainder return unsafe_trunc(Int, fn), DoubleFloat32(y) end n, y = @noinline paynehanek(xd) return n, DoubleFloat32(y.hi) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/special/log.jl!h# This file is a part of Julia. License is MIT: https://julialang.org/license # Implementation of # "Table-driven Implementation of the Logarithm Function in IEEE Floating-point Arithmetic" # Tang, Ping-Tak Peter # ACM Trans. Math. Softw. (1990), 16(4):378--400 # https://doi.org/10.1145/98267.98294 # Does not currently handle floating point flags (inexact, div-by-zero, etc). import .Base.unsafe_trunc import .Base.Math.@horner # Float64 lookup table. # to generate values: # N=39 # (can be up to N=42). # sN = 2.0^N # isN = 1.0/sN # s7 = 2.0^7 # is7 = 1.0/s7 # for j=0:128 # l_big = Base.log(big(1.0+j*is7)) # l_hi = isN*Float64(round(sN*l_big)) # l_lo = Float64(l_big-l_hi) # j % 2 == 0 && print("\n ") # print("(",l_hi,",",l_lo,"),") # end const t_log_Float64 = ((0.0,0.0),(0.007782140442941454,-8.865052917267247e-13), (0.015504186536418274,-4.530198941364935e-13),(0.0231670592820592,-5.248209479295644e-13), (0.03077165866670839,4.529814257790929e-14),(0.0383188643027097,-5.730994833076631e-13), (0.04580953603181115,-5.16945692881222e-13),(0.053244514518155484,6.567993368985218e-13), (0.06062462181580486,6.299848199383311e-13),(0.06795066190898069,-4.729424109166329e-13), (0.07522342123775161,-1.6408301585598662e-13),(0.08244366921098845,8.614512936087814e-14), (0.08961215869021544,-5.283050530808144e-13),(0.09672962645890948,-3.5836667430094137e-13), (0.10379679368088546,7.581073923016376e-13),(0.11081436634049169,-2.0157368416016215e-13), (0.11778303565552051,8.629474042969438e-13),(0.1247034785010328,-7.556920687451337e-14), (0.1315763577895268,-8.075373495358435e-13),(0.13840232285838283,7.363043577087051e-13), (0.14518200984457508,-7.718001336828099e-14),(0.15191604202664166,-7.996871607743758e-13), (0.15860503017574956,8.890223439724663e-13),(0.16524957289584563,-5.384682618788232e-13), (0.17185025692742784,-7.686134224018169e-13),(0.17840765747314435,-3.2605717931058157e-13), (0.18492233849428885,-2.7685884310448306e-13),(0.1913948530000198,-3.903387893794952e-13), (0.1978257433293038,6.160755775588723e-13),(0.20421554142922105,-5.30156516006026e-13), (0.21056476910780475,-4.55112422774782e-13),(0.21687393830143264,-8.182853292737783e-13), (0.22314355131493357,-7.238189921749681e-13),(0.22937410106533207,-4.86240001538379e-13), (0.23556607131286,-9.30945949519689e-14),(0.24171993688651128,6.338907368997553e-13), (0.24783616390413954,4.4171755371315547e-13),(0.25391520998164196,-6.785208495970588e-13), (0.25995752443668607,2.3999540484211735e-13),(0.2659635484978935,-7.555569400283742e-13), (0.27193371548310097,5.407904186145515e-13),(0.2778684510030871,3.692037508208009e-13), (0.28376817313073843,-9.3834172236637e-14),(0.28963329258294834,9.43339818951269e-14), (0.29546421289342106,4.148131870425857e-13),(0.3012613305781997,-3.7923164802093147e-14), (0.3070250352957373,-8.25463138725004e-13),(0.31275571000333,5.668653582900739e-13), (0.318453731119007,-4.723727821986367e-13),(0.32411946865431673,-1.0475750058776541e-13), (0.32975328637257917,-1.1118671389559323e-13),(0.33535554192167183,-5.339989292003297e-13), (0.3409265869704541,1.3912841212197566e-13),(0.3464667673470103,-8.017372713972018e-13), (0.35197642315688427,2.9391859187648e-13),(0.3574558889213222,4.815896111723205e-13), (0.3629054936900502,-6.817539406325327e-13),(0.36832556115950865,-8.009990055432491e-13), (0.3737164097929053,6.787566823158706e-13),(0.37907835293481185,1.5761203773969435e-13), (0.3844116989112081,-8.760375990774874e-13),(0.38971675114044046,-4.152515806343612e-13), (0.3949938082405424,3.2655698896907146e-13),(0.40024316412745975,-4.4704265010452445e-13), (0.4054651081078191,3.452764795203977e-13),(0.4106599249844294,8.390050778518307e-13), (0.4158278951435932,1.1776978751369214e-13),(0.4209692946442374,-1.0774341461609579e-13), (0.42608439531068143,2.186334329321591e-13),(0.43117346481813,2.413263949133313e-13), (0.4362367667745275,3.90574622098307e-13),(0.44127456080423144,6.437879097373207e-13), (0.44628710262804816,3.713514191959202e-13),(0.45127464413963025,-1.7166921336082432e-13), (0.4562374334818742,-2.8658285157914353e-13),(0.4611757151214988,6.713692791384601e-13), (0.46608972992544295,-8.437281040871276e-13),(0.4709797152190731,-2.821014384618127e-13), (0.4758459048698569,1.0701931762114255e-13),(0.4806885293455707,1.8119346366441111e-13), (0.4855078157816024,9.840465278232627e-14),(0.49030398804461583,5.780031989454028e-13), (0.49507726679803454,-1.8302857356041668e-13),(0.4998278695566114,-1.620740015674495e-13), (0.5045560107519123,4.83033149495532e-13),(0.5092619017905236,-7.156055317238212e-13), (0.5139457511013461,8.882123951857185e-13),(0.5186077642083546,-3.0900580513238243e-13), (0.5232481437651586,-6.10765519728515e-13),(0.5278670896204858,3.565996966334783e-13), (0.532464798869114,3.5782396591276384e-13),(0.5370414658973459,-4.622608700154458e-13), (0.5415972824321216,6.227976291722515e-13),(0.5461324375974073,7.283894727206574e-13), (0.5506471179523942,2.680964661521167e-13),(0.5551415075406112,-1.0960825046059278e-13), (0.5596157879353996,2.3119493838005378e-14),(0.5640701382853877,-5.846905800529924e-13), (0.5685047353526897,-2.1037482511444942e-14),(0.5729197535620187,-2.332318294558741e-13), (0.5773153650352469,-4.2333694288141915e-13),(0.5816917396350618,-4.3933937969737843e-13), (0.5860490450031648,4.1341647073835564e-13),(0.590387446602108,6.841763641591467e-14), (0.5947071077462169,4.758553400443064e-13),(0.5990081896452466,8.367967867475769e-13), (0.6032908514389419,-8.576373464665864e-13),(0.6075552502243227,2.1913281229340092e-13), (0.6118015411066153,-6.224284253643115e-13),(0.6160298772156239,-1.098359432543843e-13), (0.6202404097512044,6.531043137763365e-13),(0.6244332880123693,-4.758019902171077e-13), (0.6286086594227527,-3.785425126545704e-13),(0.6327666695706284,4.0939233218678666e-13), (0.636907462236195,8.742438391485829e-13),(0.6410311794206791,2.521818845684288e-13), (0.6451379613736208,-3.6081313604225574e-14),(0.649227946625615,-5.05185559242809e-13), (0.6533012720119586,7.869940332335532e-13),(0.6573580727090302,-6.702087696194906e-13), (0.6613984822452039,1.6108575753932459e-13),(0.6654226325445052,5.852718843625151e-13), (0.6694306539429817,-3.5246757297904794e-13),(0.6734226752123504,-1.8372084495629058e-13), (0.6773988235909201,8.860668981349492e-13),(0.6813592248072382,6.64862680714687e-13), (0.6853040030982811,6.383161517064652e-13),(0.6892332812385575,2.5144230728376075e-13), (0.6931471805601177,-1.7239444525614835e-13)) # Float32 lookup table # to generate values: # N=16 # sN = 2f0^N # isN = 1f0/sN # s7 = 2.0^7 # is7 = 1.0/s7 # for j=0:128 # j % 4 == 0 && print("\n ") # print(float64(Base.log(big(1.0+j*is7))),",") # end const t_log_Float32 = (0.0,0.007782140442054949,0.015504186535965254,0.02316705928153438, 0.030771658666753687,0.0383188643021366,0.0458095360312942,0.053244514518812285, 0.06062462181643484,0.06795066190850775,0.07522342123758753,0.08244366921107459, 0.08961215868968714,0.09672962645855111,0.10379679368164356,0.11081436634029011, 0.11778303565638346,0.12470347850095724,0.13157635778871926,0.13840232285911913, 0.1451820098444979,0.15191604202584197,0.15860503017663857,0.16524957289530717, 0.17185025692665923,0.1784076574728183,0.184922338494012,0.19139485299962947, 0.19782574332991987,0.2042155414286909,0.21056476910734964,0.21687393830061436, 0.22314355131420976,0.22937410106484582,0.2355660713127669,0.24171993688714516, 0.24783616390458127,0.25391520998096345,0.25995752443692605,0.26596354849713794, 0.27193371548364176,0.2778684510034563,0.2837681731306446,0.28963329258304266, 0.2954642128938359,0.3012613305781618,0.3070250352949119,0.3127557100038969, 0.3184537311185346,0.324119468654212,0.329753286372468,0.3353555419211378, 0.3409265869705932,0.34646676734620857,0.3519764231571782,0.3574558889218038, 0.3629054936893685,0.3683255611587076,0.37371640979358406,0.37907835293496944, 0.38441169891033206,0.3897167511400252,0.394993808240869,0.4002431641270127, 0.4054651081081644,0.4106599249852684,0.415827895143711,0.42096929464412963, 0.4260843953109001,0.4311734648183713,0.43623676677491807,0.4412745608048752, 0.44628710262841953,0.45127464413945856,0.4562374334815876,0.46117571512217015, 0.46608972992459924,0.470979715218791,0.4758459048699639,0.4806885293457519, 0.4855078157817008,0.4903039880451938,0.4950772667978515,0.4998278695564493, 0.5045560107523953,0.5092619017898079,0.5139457511022343,0.5186077642080457, 0.5232481437645479,0.5278670896208424,0.5324647988694718,0.5370414658968836, 0.5415972824327444,0.5461324375981357,0.5506471179526623,0.5551415075405016, 0.5596157879354227,0.564070138284803,0.5685047353526688,0.5729197535617855, 0.5773153650348236,0.5816917396346225,0.5860490450035782,0.5903874466021763, 0.5947071077466928,0.5990081896460834,0.6032908514380843,0.6075552502245418, 0.6118015411059929,0.616029877215514,0.6202404097518576,0.6244332880118935, 0.6286086594223741,0.6327666695710378,0.6369074622370692,0.6410311794209312, 0.6451379613735847,0.6492279466251099,0.6533012720127457,0.65735807270836, 0.661398482245365,0.6654226325450905,0.6694306539426292,0.6734226752121667, 0.6773988235918061,0.6813592248079031,0.6853040030989194,0.689233281238809, 0.6931471805599453) # truncate lower order bits (up to 26) # ideally, this should be able to use ANDPD instructions, see #9868. @inline function truncbits(x::Float64) reinterpret(Float64, reinterpret(UInt64,x) & 0xffff_ffff_f800_0000) end logb(::Type{Float32},::Val{2}) = 1.4426950408889634 logb(::Type{Float32},::Val{:โ„ฏ}) = 1.0 logb(::Type{Float32},::Val{10}) = 0.4342944819032518 logbU(::Type{Float64},::Val{2}) = 1.4426950408889634 logbL(::Type{Float64},::Val{2}) = 2.0355273740931033e-17 logbU(::Type{Float64},::Val{:โ„ฏ}) = 1.0 logbL(::Type{Float64},::Val{:โ„ฏ}) = 0.0 logbU(::Type{Float64},::Val{10}) = 0.4342944819032518 logbL(::Type{Float64},::Val{10}) = 1.098319650216765e-17 # Procedure 1 # XXX we want to mark :consistent-cy here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the access to `t_log_Float64` is really safe here Base.@assume_effects :consistent @inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,base=Val(:โ„ฏ)) jp = unsafe_trunc(Int,128.0*F)-127 ## Steps 1 and 2 @inbounds hi,lo = t_log_Float64[jp] l_hi = mf* 0.6931471805601177 + hi l_lo = mf*-1.7239444525614835e-13 + lo ## Step 3 # @inbounds u = f*c_invF[jp] # u = f/F # q = u*u*@horner(u, # -0x1.0_0000_0000_0001p-1, # +0x1.5_5555_5550_9ba5p-2, # -0x1.f_ffff_ffeb_6526p-3, # +0x1.9_99b4_dfed_6fe4p-3, # -0x1.5_5576_6647_2e04p-3) ## Step 3' (alternative) u = (2.0f)/(y+F) v = u*u q = u*v*@horner(v, 0.08333333333303913, 0.012500053168098584) ## Step 4 m_hi = logbU(Float64, base) m_lo = logbL(Float64, base) return fma(m_hi, l_hi, fma(m_hi, (u + (q + l_lo)), m_lo*l_hi)) end # Procedure 2 @inline function log_proc2(f::Float64,base=Val(:โ„ฏ)) ## Step 1 g = 1.0/(2.0+f) u = 2.0*f*g v = u*u ## Step 2 q = u*v*@horner(v, 0.08333333333333179, 0.012500000003771751, 0.0022321399879194482, 0.0004348877777076146) ## Step 3 # based on: # 2(f-u) = 2(f(2+f)-2f)/(2+f) = 2f^2/(2+f) = fu # 2(f-u1-u2) - f*(u1+u2) = 0 # 2(f-u1) - f*u1 = (2+f)u2 # u2 = (2(f-u1) - f*u1)/(2+f) m_hi = logbU(Float64, base) m_lo = logbL(Float64, base) return fma(m_hi, u, fma(m_lo, u, m_hi*fma(fma(-u,f,2(f-u)), g, q))) end # Procedure 1 # XXX we want to mark :consistent-cy here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the access to `t_log_Float32` is really safe here Base.@assume_effects :consistent @inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,base=Val(:โ„ฏ)) jp = unsafe_trunc(Int,128.0f0*F)-127 ## Steps 1 and 2 @inbounds hi = t_log_Float32[jp] l = mf*0.6931471805599453 + hi ## Step 3 # @inbounds u = f*c_invF[jp] # q = u*u*@horner(u, # Float32(-0x1.00006p-1), # Float32(0x1.55546cp-2)) ## Step 3' (alternative) u = (2f0f)/(y+F) v = u*u q = u*v*0.08333351f0 ## Step 4 Float32(logb(Float32, base)*(l + (u + q))) end # Procedure 2 @inline function log_proc2(f::Float32,base=Val(:โ„ฏ)) ## Step 1 # compute in higher precision u64 = Float64(2f0*f)/(2.0+f) u = Float32(u64) v = u*u ## Step 2 q = u*v*@horner(v, 0.08333332f0, 0.012512346f0) ## Step 3: not required ## Step 4 Float32(logb(Float32, base)*(u64 + q)) end log2(x::Float32) = _log(x, Val(2), :log2) log(x::Float32) = _log(x, Val(:โ„ฏ), :log) log10(x::Float32) = _log(x, Val(10), :log10) log2(x::Float64) = _log(x, Val(2), :log2) log(x::Float64) = _log(x, Val(:โ„ฏ), :log) log10(x::Float64) = _log(x, Val(10), :log10) function _log(x::Float64, base, func) if x > 0.0 x == Inf && return x # Step 2 if 0.9394130628134757 < x < 1.0644944589178595 f = x-1.0 return log_proc2(f, base) end # Step 3 xu = reinterpret(UInt64,x) m = Int(xu >> 52) & 0x07ff if m == 0 # x is subnormal x *= 1.8014398509481984e16 # 0x1p54, normalise significand xu = reinterpret(UInt64,x) m = Int(xu >> 52) & 0x07ff - 54 end m -= 1023 y = reinterpret(Float64,(xu & 0x000f_ffff_ffff_ffff) | 0x3ff0_0000_0000_0000) mf = Float64(m) F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y) f = y-F return log_proc1(y,mf,F,f,base) elseif x == 0.0 -Inf elseif isnan(x) NaN else throw_complex_domainerror(func, x) end end function _log(x::Float32, base, func) if x > 0f0 x == Inf32 && return x # Step 2 if 0.939413f0 < x < 1.0644945f0 f = x-1f0 return log_proc2(f, base) end # Step 3 xu = reinterpret(UInt32,x) m = Int(xu >> 23) & 0x00ff if m == 0 # x is subnormal x *= 3.3554432f7 # 0x1p25, normalise significand xu = reinterpret(UInt32,x) m = Int(xu >> 23) & 0x00ff - 25 end m -= 127 y = reinterpret(Float32,(xu & 0x007f_ffff) | 0x3f80_0000) mf = Float32(m) F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y) f = y-F log_proc1(y,mf,F,f,base) elseif x == 0f0 -Inf32 elseif isnan(x) NaN32 else throw_complex_domainerror(func, x) end end function log1p(x::Float64) if x > -1.0 x == Inf && return x if -1.1102230246251565e-16 < x < 1.1102230246251565e-16 return x # Inexact # Step 2 elseif -0.06058693718652422 < x < 0.06449445891785943 return log_proc2(x) end # Step 3 z = 1.0 + x zu = reinterpret(UInt64,z) s = reinterpret(Float64,0x7fe0_0000_0000_0000 - (zu & 0xfff0_0000_0000_0000)) # 2^-m m = Int(zu >> 52) & 0x07ff - 1023 # z cannot be subnormal c = m > 0 ? 1.0-(z-x) : x-(z-1.0) # 1+x = z+c exactly y = reinterpret(Float64,(zu & 0x000f_ffff_ffff_ffff) | 0x3ff0_0000_0000_0000) mf = Float64(m) F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y) f = (y - F) + c*s #2^m(F+f) = 1+x = z+c log_proc1(y,mf,F,f) elseif x == -1.0 -Inf elseif isnan(x) NaN else throw_complex_domainerror_neg1(:log1p, x) end end function log1p(x::Float32) if x > -1f0 x == Inf32 && return x if -5.9604645f-8 < x < 5.9604645f-8 return x # Inexact # Step 2 elseif -0.06058694f0 < x < 0.06449446f0 return log_proc2(x) end # Step 3 z = 1f0 + x zu = reinterpret(UInt32,z) s = reinterpret(Float32,0x7f000000 - (zu & 0xff80_0000)) # 2^-m m = Int(zu >> 23) & 0x00ff - 127 # z cannot be subnormal c = m > 0 ? 1f0-(z-x) : x-(z-1f0) # 1+x = z+c y = reinterpret(Float32,(zu & 0x007f_ffff) | 0x3f80_0000) mf = Float32(m) F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y) f = (y - F) + s*c #2^m(F+f) = 1+x = z+c log_proc1(y,mf,F,f) elseif x == -1f0 -Inf32 elseif isnan(x) NaN32 else throw_complex_domainerror_neg1(:log1p, x) end end #function make_compact_table(N) # table = Tuple{UInt64,Float64}[] # lo, hi = 0x1.69555p-1, 0x1.69555p0 # for i in 0:N-1 # # I am not fully sure why this is the right formula to use, but it apparently is # center = i/(2*N) + lo < 1 ? (i+.5)/(2*N) + lo : (i+.5)/N + hi -1 # invc = Float64(center < 1 ? round(N/center)/N : round(2*N/center)/(N*2)) # c = inv(big(invc)) # logc = Float64(round(0x1p43*log(c))/0x1p43) # logctail = reinterpret(Float64, Float64(log(c) - logc)) # p1 = (reinterpret(UInt64,invc) >> 45) % UInt8 # push!(table, (p1|reinterpret(UInt64,logc),logctail)) # end # return Tuple(table) #end #const t_log_table_compact = make_compact_table(128) const t_log_table_compact = ( (0xbfd62c82f2b9c8b5, 5.929407345889625e-15), (0xbfd5d1bdbf5808b4, -2.544157440035963e-14), (0xbfd57677174558b3, -3.443525940775045e-14), (0xbfd51aad872df8b2, -2.500123826022799e-15), (0xbfd4be5f957778b1, -8.929337133850617e-15), (0xbfd4618bc21c60b0, 1.7625431312172662e-14), (0xbfd404308686a8af, 1.5688303180062087e-15), (0xbfd3a64c556948ae, 2.9655274673691784e-14), (0xbfd347dd9a9880ad, 3.7923164802093147e-14), (0xbfd2e8e2bae120ac, 3.993416384387844e-14), (0xbfd2895a13de88ab, 1.9352855826489123e-14), (0xbfd2895a13de88ab, 1.9352855826489123e-14), (0xbfd22941fbcf78aa, -1.9852665484979036e-14), (0xbfd1c898c16998a9, -2.814323765595281e-14), (0xbfd1675cababa8a8, 2.7643769993528702e-14), (0xbfd1058bf9ae48a7, -4.025092402293806e-14), (0xbfd0a324e27390a6, -1.2621729398885316e-14), (0xbfd0402594b4d0a5, -3.600176732637335e-15), (0xbfd0402594b4d0a5, -3.600176732637335e-15), (0xbfcfb9186d5e40a4, 1.3029797173308663e-14), (0xbfcef0adcbdc60a3, 4.8230289429940886e-14), (0xbfce27076e2af0a2, -2.0592242769647135e-14), (0xbfcd5c216b4fc0a1, 3.149265065191484e-14), (0xbfcc8ff7c79aa0a0, 4.169796584527195e-14), (0xbfcc8ff7c79aa0a0, 4.169796584527195e-14), (0xbfcbc286742d909f, 2.2477465222466186e-14), (0xbfcaf3c94e80c09e, 3.6507188831790577e-16), (0xbfca23bc1fe2b09d, -3.827767260205414e-14), (0xbfca23bc1fe2b09d, -3.827767260205414e-14), (0xbfc9525a9cf4509c, -4.7641388950792196e-14), (0xbfc87fa06520d09b, 4.9278276214647115e-14), (0xbfc7ab890210e09a, 4.9485167661250996e-14), (0xbfc7ab890210e09a, 4.9485167661250996e-14), (0xbfc6d60fe719d099, -1.5003333854266542e-14), (0xbfc5ff3070a79098, -2.7194441649495324e-14), (0xbfc5ff3070a79098, -2.7194441649495324e-14), (0xbfc526e5e3a1b097, -2.99659267292569e-14), (0xbfc44d2b6ccb8096, 2.0472357800461955e-14), (0xbfc44d2b6ccb8096, 2.0472357800461955e-14), (0xbfc371fc201e9095, 3.879296723063646e-15), (0xbfc29552f81ff094, -3.6506824353335045e-14), (0xbfc1b72ad52f6093, -5.4183331379008994e-14), (0xbfc1b72ad52f6093, -5.4183331379008994e-14), (0xbfc0d77e7cd09092, 1.1729485484531301e-14), (0xbfc0d77e7cd09092, 1.1729485484531301e-14), (0xbfbfec9131dbe091, -3.811763084710266e-14), (0xbfbe27076e2b0090, 4.654729747598445e-14), (0xbfbe27076e2b0090, 4.654729747598445e-14), (0xbfbc5e548f5bc08f, -2.5799991283069902e-14), (0xbfba926d3a4ae08e, 3.7700471749674615e-14), (0xbfba926d3a4ae08e, 3.7700471749674615e-14), (0xbfb8c345d631a08d, 1.7306161136093256e-14), (0xbfb8c345d631a08d, 1.7306161136093256e-14), (0xbfb6f0d28ae5608c, -4.012913552726574e-14), (0xbfb51b073f06208b, 2.7541708360737882e-14), (0xbfb51b073f06208b, 2.7541708360737882e-14), (0xbfb341d7961be08a, 5.0396178134370583e-14), (0xbfb341d7961be08a, 5.0396178134370583e-14), (0xbfb16536eea38089, 1.8195060030168815e-14), (0xbfaf0a30c0118088, 5.213620639136504e-14), (0xbfaf0a30c0118088, 5.213620639136504e-14), (0xbfab42dd71198087, 2.532168943117445e-14), (0xbfab42dd71198087, 2.532168943117445e-14), (0xbfa77458f632c086, -5.148849572685811e-14), (0xbfa77458f632c086, -5.148849572685811e-14), (0xbfa39e87b9fec085, 4.6652946995830086e-15), (0xbfa39e87b9fec085, 4.6652946995830086e-15), (0xbf9f829b0e780084, -4.529814257790929e-14), (0xbf9f829b0e780084, -4.529814257790929e-14), (0xbf97b91b07d58083, -4.361324067851568e-14), (0xbf8fc0a8b0fc0082, -1.7274567499706107e-15), (0xbf8fc0a8b0fc0082, -1.7274567499706107e-15), (0xbf7fe02a6b100081, -2.298941004620351e-14), (0xbf7fe02a6b100081, -2.298941004620351e-14), (0x0000000000000080, 0.0), (0x0000000000000080, 0.0), (0x3f8010157589007e, -1.4902732911301337e-14), (0x3f9020565893807c, -3.527980389655325e-14), (0x3f98492528c9007a, -4.730054772033249e-14), (0x3fa0415d89e74078, 7.580310369375161e-15), (0x3fa466aed42e0076, -4.9893776716773285e-14), (0x3fa894aa149fc074, -2.262629393030674e-14), (0x3faccb73cdddc072, -2.345674491018699e-14), (0x3faeea31c006c071, -1.3352588834854848e-14), (0x3fb1973bd146606f, -3.765296820388875e-14), (0x3fb3bdf5a7d1e06d, 5.1128335719851986e-14), (0x3fb5e95a4d97a06b, -5.046674438470119e-14), (0x3fb700d30aeac06a, 3.1218748807418837e-15), (0x3fb9335e5d594068, 3.3871241029241416e-14), (0x3fbb6ac88dad6066, -1.7376727386423858e-14), (0x3fbc885801bc4065, 3.957125899799804e-14), (0x3fbec739830a2063, -5.2849453521890294e-14), (0x3fbfe89139dbe062, -3.767012502308738e-14), (0x3fc1178e8227e060, 3.1859736349078334e-14), (0x3fc1aa2b7e23f05f, 5.0900642926060466e-14), (0x3fc2d1610c86805d, 8.710783796122478e-15), (0x3fc365fcb015905c, 6.157896229122976e-16), (0x3fc4913d8333b05a, 3.821577743916796e-14), (0x3fc527e5e4a1b059, 3.9440046718453496e-14), (0x3fc6574ebe8c1057, 2.2924522154618074e-14), (0x3fc6f0128b757056, -3.742530094732263e-14), (0x3fc7898d85445055, -2.5223102140407338e-14), (0x3fc8beafeb390053, -1.0320443688698849e-14), (0x3fc95a5adcf70052, 1.0634128304268335e-14), (0x3fca93ed3c8ae050, -4.3425422595242564e-14), (0x3fcb31d8575bd04f, -1.2527395755711364e-14), (0x3fcbd087383be04e, -5.204008743405884e-14), (0x3fcc6ffbc6f0104d, -3.979844515951702e-15), (0x3fcdb13db0d4904b, -4.7955860343296286e-14), (0x3fce530effe7104a, 5.015686013791602e-16), (0x3fcef5ade4dd0049, -7.252318953240293e-16), (0x3fcf991c6cb3b048, 2.4688324156011588e-14), (0x3fd07138604d5846, 5.465121253624792e-15), (0x3fd0c42d67616045, 4.102651071698446e-14), (0x3fd1178e8227e844, -4.996736502345936e-14), (0x3fd16b5ccbacf843, 4.903580708156347e-14), (0x3fd1bf99635a6842, 5.089628039500759e-14), (0x3fd214456d0eb841, 1.1782016386565151e-14), (0x3fd2bef07cdc903f, 4.727452940514406e-14), (0x3fd314f1e1d3603e, -4.4204083338755686e-14), (0x3fd36b6776be103d, 1.548345993498083e-14), (0x3fd3c2527733303c, 2.1522127491642888e-14), (0x3fd419b423d5e83b, 1.1054030169005386e-14), (0x3fd4718dc271c83a, -5.534326352070679e-14), (0x3fd4c9e09e173039, -5.351646604259541e-14), (0x3fd522ae0738a038, 5.4612144489920215e-14), (0x3fd57bf753c8d037, 2.8136969901227338e-14), (0x3fd5d5bddf596036, -1.156568624616423e-14)) @inline function log_tab_unpack(t::UInt64) invc = UInt64(t&UInt64(0xff)|0x1ff00)<<45 logc = t&(~UInt64(0xff)) return (reinterpret(Float64, invc), reinterpret(Float64, logc)) end # Log implementation that returns 2 numbers which sum to give true value with about 68 bits of precision # Since `log` only makes sense for positive exponents, we speed up the implementation by stealing the sign bit # of the input for an extra bit of the exponent which is used to normalize subnormal inputs. # Does not normalize results. # Adapted and modified from https://github.com/ARM-software/optimized-routines/blob/master/math/pow.c # Copyright (c) 2018-2020, Arm Limited. (which is also MIT licensed) # note that this isn't an exact translation as this version compacts the table to reduce cache pressure. function _log_ext(xu) # x = 2^k z; where z is in range [0x1.69555p-1,0x1.69555p-0) and exact. # The range is split into N subintervals. # The ith subinterval contains z and c is near the center of the interval. tmp = reinterpret(Int64, xu - 0x3fe6955500000000) #0x1.69555p-1 i = (tmp >> 45) & 127 z = reinterpret(Float64, xu - (tmp & 0xfff0000000000000)) k = Float64(tmp >> 52) # log(x) = k*Ln2 + log(c) + log1p(z/c-1). # getfield instead of getindex to satisfy effect analysis not knowing whether this is inbounds t, logctail = getfield(t_log_table_compact, Int(i+1)) invc, logc = log_tab_unpack(t) # Note: invc is j/N or j/N/2 where j is an integer in [N,2N) and # |z/c - 1| < 1/N, so r = z/c - 1 is exactly representable. r = fma(z, invc, -1.0) # k*Ln2 + log(c) + r. t1 = muladd(k, 0.6931471805598903, logc) #ln(2) hi part t2 = t1 + r lo1 = muladd(k, 5.497923018708371e-14, logctail) #ln(2) lo part lo2 = t1 - t2 + r ar = -0.5 * r ar2, lo3 = two_mul(r, ar) # k*Ln2 + log(c) + r + .5*r*r. hi = t2 + ar2 lo4 = t2 - hi + ar2 p = evalpoly(r, (-0x1.555555555556p-1, 0x1.0000000000006p-1, -0x1.999999959554ep-2, 0x1.555555529a47ap-2, -0x1.2495b9b4845e9p-2, 0x1.0002b8b263fc3p-2)) lo = lo1 + lo2 + lo3 + muladd(r*ar2, p, lo4) return hi, lo end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/reducedim.jl่‡# This file is a part of Julia. License is MIT: https://julialang.org/license ## Functions to compute the reduced shape # for reductions that expand 0 dims to 1 reduced_index(i::OneTo{T}) where {T} = OneTo(one(T)) reduced_index(i::Union{Slice, IdentityUnitRange}) = oftype(i, first(i):first(i)) reduced_index(i::AbstractUnitRange) = throw(ArgumentError( """ No method is implemented for reducing index range of type $(typeof(i)). Please implement reduced_index for this index type or report this as an issue. """ )) reduced_indices(a::AbstractArrayOrBroadcasted, region) = reduced_indices(axes(a), region) # for reductions that keep 0 dims as 0 reduced_indices0(a::AbstractArray, region) = reduced_indices0(axes(a), region) function reduced_indices(inds::Indices{N}, d::Int) where N d < 1 && throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) if d == 1 return (reduced_index(inds[1]), tail(inds)...)::typeof(inds) elseif 1 < d <= N return tuple(inds[1:d-1]..., oftype(inds[d], reduced_index(inds[d])), inds[d+1:N]...)::typeof(inds) else return inds end end function reduced_indices0(inds::Indices{N}, d::Int) where N d < 1 && throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) if d <= N ind = inds[d] rd = isempty(ind) ? ind : reduced_index(inds[d]) if d == 1 return (rd, tail(inds)...)::typeof(inds) else return tuple(inds[1:d-1]..., oftype(inds[d], rd), inds[d+1:N]...)::typeof(inds) end else return inds end end function reduced_indices(inds::Indices{N}, region) where N rinds = collect(inds) for i in region isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) d = Int(i) if d < 1 throw(ArgumentError("region dimension(s) must be โ‰ฅ 1, got $d")) elseif d <= N rinds[d] = reduced_index(rinds[d]) end end tuple(rinds...)::typeof(inds) end function reduced_indices0(inds::Indices{N}, region) where N rinds = collect(inds) for i in region isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) d = Int(i) if d < 1 throw(ArgumentError("region dimension(s) must be โ‰ฅ 1, got $d")) elseif d <= N rind = rinds[d] rinds[d] = isempty(rind) ? rind : reduced_index(rind) end end tuple(rinds...)::typeof(inds) end ###### Generic reduction functions ##### ## initialization # initarray! is only called by sum!, prod!, etc. for (Op, initfun) in ((:(typeof(add_sum)), :zero), (:(typeof(mul_prod)), :one)) @eval initarray!(a::AbstractArray{T}, ::Any, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a) end initarray!(a::AbstractArray{T}, f, ::Union{typeof(min),typeof(max),typeof(_extrema_rf)}, init::Bool, src::AbstractArray) where {T} = (init && mapfirst!(f, a, src); a) for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false)) @eval initarray!(a::AbstractArray, ::Any, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a) end # reducedim_initarray is called by reducedim_initarray(A::AbstractArrayOrBroadcasted, region, init, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), init) reducedim_initarray(A::AbstractArrayOrBroadcasted, region, init::T) where {T} = reducedim_initarray(A, region, init, T) # TODO: better way to handle reducedim initialization # # The current scheme is basically following Steven G. Johnson's original implementation # promote_union(T::Union) = promote_type(promote_union(T.a), promote_union(T.b)) promote_union(T) = T _realtype(::Type{<:Complex}) = Real _realtype(::Type{Complex{T}}) where T<:Real = T _realtype(T::Type) = T _realtype(::Union{typeof(abs),typeof(abs2)}, T) = _realtype(T) _realtype(::Any, T) = T function reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::AbstractArray, region) _reducedim_init(f, op, zero, sum, A, region) end function reducedim_init(f, op::Union{typeof(*),typeof(mul_prod)}, A::AbstractArray, region) _reducedim_init(f, op, one, prod, A, region) end function _reducedim_init(f, op, fv, fop, A, region) T = _realtype(f, promote_union(eltype(A))) if T !== Any && applicable(zero, T) x = f(zero(T)) z = op(fv(x), fv(x)) Tr = z isa T ? T : typeof(z) else z = fv(fop(f, A)) Tr = typeof(z) end return reducedim_initarray(A, region, z, Tr) end # initialization when computing minima and maxima requires a little care for (f1, f2, initval, typeextreme) in ((:min, :max, :Inf, :typemax), (:max, :min, :(-Inf), :typemin)) @eval function reducedim_init(f, op::typeof($f1), A::AbstractArray, region) # First compute the reduce indices. This will throw an ArgumentError # if any region is invalid ri = reduced_indices(A, region) # Next, throw if reduction is over a region with length zero any(i -> isempty(axes(A, i)), region) && _empty_reduce_error() # Make a view of the first slice of the region A1 = view(A, ri...) if isempty(A1) # If the slice is empty just return non-view version as the initial array return map(f, A1) else # otherwise use the min/max of the first slice as initial value v0 = mapreduce(f, $f2, A1) T = _realtype(f, promote_union(eltype(A))) Tr = v0 isa T ? T : typeof(v0) # but NaNs and missing need to be avoided as initial values if v0 isa Number && isnan(v0) # v0 is NaN v0 = oftype(v0, $initval) elseif isunordered(v0) # v0 is missing or a third-party unordered value Tnm = nonmissingtype(Tr) # TODO: Some types, like BigInt, don't support typemin/typemax. # So a Matrix{Union{BigInt, Missing}} can still error here. v0 = $typeextreme(Tnm) end # v0 may have changed type. Tr = v0 isa T ? T : typeof(v0) return reducedim_initarray(A, region, v0, Tr) end end end function reducedim_init(f::ExtremaMap, op::typeof(_extrema_rf), A::AbstractArray, region) # First compute the reduce indices. This will throw an ArgumentError # if any region is invalid ri = reduced_indices(A, region) # Next, throw if reduction is over a region with length zero any(i -> isempty(axes(A, i)), region) && _empty_reduce_error() # Make a view of the first slice of the region A1 = view(A, ri...) isempty(A1) && return map(f, A1) # use the max/min of the first slice as initial value for non-empty cases v0 = reverse(mapreduce(f, op, A1)) # turn minmax to maxmin T = _realtype(f.f, promote_union(eltype(A))) Tmin = v0[1] isa T ? T : typeof(v0[1]) Tmax = v0[2] isa T ? T : typeof(v0[2]) # but NaNs and missing need to be avoided as initial values if v0[1] isa Number && isnan(v0[1]) v0 = oftype(v0[1], Inf), oftype(v0[2], -Inf) elseif isunordered(v0[1]) # v0 is missing or a third-party unordered value # TODO: Some types, like BigInt, don't support typemin/typemax. # So a Matrix{Union{BigInt, Missing}} can still error here. v0 = typemax(nonmissingtype(Tmin)), typemin(nonmissingtype(Tmax)) end # v0 may have changed type. Tmin = v0[1] isa T ? T : typeof(v0[1]) Tmax = v0[2] isa T ? T : typeof(v0[2]) return reducedim_initarray(A, region, v0, Tuple{Tmin,Tmax}) end reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(max), A::AbstractArray{T}, region) where {T} = reducedim_initarray(A, region, zero(f(zero(T))), _realtype(f, T)) reducedim_init(f, op::typeof(&), A::AbstractArrayOrBroadcasted, region) = reducedim_initarray(A, region, true) reducedim_init(f, op::typeof(|), A::AbstractArrayOrBroadcasted, region) = reducedim_initarray(A, region, false) # specialize to make initialization more efficient for common cases let BitIntFloat = Union{BitInteger, IEEEFloat} T = Union{ Any[AbstractArray{t} for t in uniontypes(BitIntFloat)]..., Any[AbstractArray{Complex{t}} for t in uniontypes(BitIntFloat)]...} global function reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::T, region) z = zero(f(zero(eltype(A)))) reducedim_initarray(A, region, op(z, z)) end global function reducedim_init(f, op::Union{typeof(*),typeof(mul_prod)}, A::T, region) u = one(f(one(eltype(A)))) reducedim_initarray(A, region, op(u, u)) end end ## generic (map)reduction has_fast_linear_indexing(a::AbstractArrayOrBroadcasted) = false has_fast_linear_indexing(a::Array) = true has_fast_linear_indexing(::Union{Number,Ref,AbstractChar}) = true # 0d objects, for Broadcasted has_fast_linear_indexing(bc::Broadcast.Broadcasted) = all(has_fast_linear_indexing, bc.args) function check_reducedims(R, A) # Check whether R has compatible dimensions w.r.t. A for reduction # # It returns an integer value (useful for choosing implementation) # - If it reduces only along leading dimensions, e.g. sum(A, dims=1) or sum(A, dims=(1,2)), # it returns the length of the leading slice. For the two examples above, # it will be size(A, 1) or size(A, 1) * size(A, 2). # - Otherwise, e.g. sum(A, dims=2) or sum(A, dims=(1,3)), it returns 0. # ndims(R) <= ndims(A) || throw(DimensionMismatch("cannot reduce $(ndims(A))-dimensional array to $(ndims(R)) dimensions")) lsiz = 1 had_nonreduc = false for i = 1:ndims(A) Ri, Ai = axes(R, i), axes(A, i) sRi, sAi = length(Ri), length(Ai) if sRi == 1 if sAi > 1 if had_nonreduc lsiz = 0 # to reduce along i, but some previous dimensions were non-reducing else lsiz *= sAi # if lsiz was set to zero, it will stay to be zero end end else Ri == Ai || throw(DimensionMismatch("reduction on array with indices $(axes(A)) with output with indices $(axes(R))")) had_nonreduc = true end end return lsiz end """ Extract first entry of slices of array A into existing array R. """ copyfirst!(R::AbstractArray, A::AbstractArray) = mapfirst!(identity, R, A) function mapfirst!(f::F, R::AbstractArray, A::AbstractArray{<:Any,N}) where {N, F} lsiz = check_reducedims(R, A) t = _firstreducedslice(axes(R), axes(A)) map!(f, R, view(A, t...)) end # We know that the axes of R and A are compatible, but R might have a different number of # dimensions than A, which is trickier than it seems due to offset arrays and type stability _firstreducedslice(::Tuple{}, a::Tuple{}) = () _firstreducedslice(::Tuple, ::Tuple{}) = () @inline _firstreducedslice(::Tuple{}, a::Tuple) = (_firstslice(a[1]), _firstreducedslice((), tail(a))...) @inline _firstreducedslice(r::Tuple, a::Tuple) = (length(r[1])==1 ? _firstslice(a[1]) : r[1], _firstreducedslice(tail(r), tail(a))...) _firstslice(i::OneTo) = OneTo(1) _firstslice(i::Slice) = Slice(_firstslice(i.indices)) _firstslice(i) = i[firstindex(i):firstindex(i)] function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArrayOrBroadcasted) lsiz = check_reducedims(R,A) isempty(A) && return R if has_fast_linear_indexing(A) && lsiz > 16 # use mapreduce_impl, which is probably better tuned to achieve higher performance nslices = div(length(A), lsiz) ibase = first(LinearIndices(A))-1 for i = 1:nslices @inbounds R[i] = op(R[i], mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) ibase += lsiz end return R end indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(R)) # handle d=1 manually keep, Idefault = Broadcast.shapeindexer(indsRt) if reducedim1(R, A) # keep the accumulator as a local variable when reducing along the first dimension i1 = first(axes1(R)) @inbounds for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) r = R[i1,IR] @simd for i in axes(A, 1) r = op(r, f(A[i, IA])) end R[i1,IR] = r end else @inbounds for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) @simd for i in axes(A, 1) R[i,IR] = op(R[i,IR], f(A[i,IA])) end end end return R end mapreducedim!(f, op, R::AbstractArray, A::AbstractArrayOrBroadcasted) = (_mapreducedim!(f, op, R, A); R) reducedim!(op, R::AbstractArray{RT}, A::AbstractArrayOrBroadcasted) where {RT} = mapreducedim!(identity, op, R, A) """ mapreduce(f, op, A::AbstractArray...; dims=:, [init]) Evaluates to the same as `reduce(op, map(f, A...); dims=dims, init=init)`, but is generally faster because the intermediate array is avoided. !!! compat "Julia 1.2" `mapreduce` with multiple iterators requires Julia 1.2 or later. # Examples ```jldoctest julia> a = reshape(Vector(1:16), (4,4)) 4ร—4 Matrix{Int64}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 julia> mapreduce(isodd, *, a, dims=1) 1ร—4 Matrix{Bool}: 0 0 0 0 julia> mapreduce(isodd, |, a, dims=1) 1ร—4 Matrix{Bool}: 1 1 1 1 ``` """ mapreduce(f, op, A::AbstractArrayOrBroadcasted; dims=:, init=_InitialValue()) = _mapreduce_dim(f, op, init, A, dims) mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) = reduce(op, map(f, A...); kw...) _mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, ::Colon) = mapfoldl_impl(f, op, nt, A) _mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) _mapreduce_dim(f, op, nt, A::AbstractArrayOrBroadcasted, dims) = mapreducedim!(f, op, reducedim_initarray(A, dims, nt), A) _mapreduce_dim(f, op, ::_InitialValue, A::AbstractArrayOrBroadcasted, dims) = mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) """ reduce(f, A::AbstractArray; dims=:, [init]) Reduce 2-argument function `f` along dimensions of `A`. `dims` is a vector specifying the dimensions to reduce, and the keyword argument `init` is the initial value to use in the reductions. For `+`, `*`, `max` and `min` the `init` argument is optional. The associativity of the reduction is implementation-dependent; if you need a particular associativity, e.g. left-to-right, you should write your own loop or consider using [`foldl`](@ref) or [`foldr`](@ref). See documentation for [`reduce`](@ref). # Examples ```jldoctest julia> a = reshape(Vector(1:16), (4,4)) 4ร—4 Matrix{Int64}: 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 16 julia> reduce(max, a, dims=2) 4ร—1 Matrix{Int64}: 13 14 15 16 julia> reduce(max, a, dims=1) 1ร—4 Matrix{Int64}: 4 8 12 16 ``` """ reduce(op, A::AbstractArray; kw...) = mapreduce(identity, op, A; kw...) ##### Specific reduction functions ##### """ count([f=identity,] A::AbstractArray; dims=:) Count the number of elements in `A` for which `f` returns `true` over the given dimensions. !!! compat "Julia 1.5" `dims` keyword was added in Julia 1.5. !!! compat "Julia 1.6" `init` keyword was added in Julia 1.6. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> count(<=(2), A, dims=1) 1ร—2 Matrix{Int64}: 1 1 julia> count(<=(2), A, dims=2) 2ร—1 Matrix{Int64}: 2 0 ``` """ count(A::AbstractArrayOrBroadcasted; dims=:, init=0) = count(identity, A; dims, init) count(f, A::AbstractArrayOrBroadcasted; dims=:, init=0) = _count(f, A, dims, init) _count(f, A::AbstractArrayOrBroadcasted, dims::Colon, init) = _simple_count(f, A, init) _count(f, A::AbstractArrayOrBroadcasted, dims, init) = mapreduce(_bool(f), add_sum, A; dims, init) """ count!([f=identity,] r, A) Count the number of elements in `A` for which `f` returns `true` over the singleton dimensions of `r`, writing the result into `r` in-place. $(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.5" inplace `count!` was added in Julia 1.5. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> count!(<=(2), [1 1], A) 1ร—2 Matrix{Int64}: 1 1 julia> count!(<=(2), [1; 1], A) 2-element Vector{Int64}: 2 0 ``` """ count!(r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) = count!(identity, r, A; init=init) count!(f, r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) = mapreducedim!(_bool(f), add_sum, initarray!(r, f, add_sum, init, A), A) """ sum(A::AbstractArray; dims) Sum elements of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> sum(A, dims=1) 1ร—2 Matrix{Int64}: 4 6 julia> sum(A, dims=2) 2ร—1 Matrix{Int64}: 3 7 ``` """ sum(A::AbstractArray; dims) """ sum(f, A::AbstractArray; dims) Sum the results of calling function `f` on each element of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> sum(abs2, A, dims=1) 1ร—2 Matrix{Int64}: 10 20 julia> sum(abs2, A, dims=2) 2ร—1 Matrix{Int64}: 5 25 ``` """ sum(f, A::AbstractArray; dims) """ sum!(r, A) Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> sum!([1; 1], A) 2-element Vector{Int64}: 3 7 julia> sum!([1 1], A) 1ร—2 Matrix{Int64}: 4 6 ``` """ sum!(r, A) """ prod(A::AbstractArray; dims) Multiply elements of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> prod(A, dims=1) 1ร—2 Matrix{Int64}: 3 8 julia> prod(A, dims=2) 2ร—1 Matrix{Int64}: 2 12 ``` """ prod(A::AbstractArray; dims) """ prod(f, A::AbstractArray; dims) Multiply the results of calling the function `f` on each element of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> prod(abs2, A, dims=1) 1ร—2 Matrix{Int64}: 9 64 julia> prod(abs2, A, dims=2) 2ร—1 Matrix{Int64}: 4 144 ``` """ prod(f, A::AbstractArray; dims) """ prod!(r, A) Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> prod!([1; 1], A) 2-element Vector{Int64}: 2 12 julia> prod!([1 1], A) 1ร—2 Matrix{Int64}: 3 8 ``` """ prod!(r, A) """ maximum(A::AbstractArray; dims) Compute the maximum value of an array over the given dimensions. See also the [`max(a,b)`](@ref) function to take the maximum of two or more arguments, which can be applied elementwise to arrays via `max.(a,b)`. See also: [`maximum!`](@ref), [`extrema`](@ref), [`findmax`](@ref), [`argmax`](@ref). # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> maximum(A, dims=1) 1ร—2 Matrix{Int64}: 3 4 julia> maximum(A, dims=2) 2ร—1 Matrix{Int64}: 2 4 ``` """ maximum(A::AbstractArray; dims) """ maximum(f, A::AbstractArray; dims) Compute the maximum value by calling the function `f` on each element of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> maximum(abs2, A, dims=1) 1ร—2 Matrix{Int64}: 9 16 julia> maximum(abs2, A, dims=2) 2ร—1 Matrix{Int64}: 4 16 ``` """ maximum(f, A::AbstractArray; dims) """ maximum!(r, A) Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> maximum!([1; 1], A) 2-element Vector{Int64}: 2 4 julia> maximum!([1 1], A) 1ร—2 Matrix{Int64}: 3 4 ``` """ maximum!(r, A) """ minimum(A::AbstractArray; dims) Compute the minimum value of an array over the given dimensions. See also the [`min(a,b)`](@ref) function to take the minimum of two or more arguments, which can be applied elementwise to arrays via `min.(a,b)`. See also: [`minimum!`](@ref), [`extrema`](@ref), [`findmin`](@ref), [`argmin`](@ref). # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> minimum(A, dims=1) 1ร—2 Matrix{Int64}: 1 2 julia> minimum(A, dims=2) 2ร—1 Matrix{Int64}: 1 3 ``` """ minimum(A::AbstractArray; dims) """ minimum(f, A::AbstractArray; dims) Compute the minimum value by calling the function `f` on each element of an array over the given dimensions. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> minimum(abs2, A, dims=1) 1ร—2 Matrix{Int64}: 1 4 julia> minimum(abs2, A, dims=2) 2ร—1 Matrix{Int64}: 1 9 ``` """ minimum(f, A::AbstractArray; dims) """ minimum!(r, A) Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> minimum!([1; 1], A) 2-element Vector{Int64}: 1 3 julia> minimum!([1 1], A) 1ร—2 Matrix{Int64}: 1 2 ``` """ minimum!(r, A) """ extrema(A::AbstractArray; dims) -> Array{Tuple} Compute the minimum and maximum elements of an array over the given dimensions. See also: [`minimum`](@ref), [`maximum`](@ref), [`extrema!`](@ref). # Examples ```jldoctest julia> A = reshape(Vector(1:2:16), (2,2,2)) 2ร—2ร—2 Array{Int64, 3}: [:, :, 1] = 1 5 3 7 [:, :, 2] = 9 13 11 15 julia> extrema(A, dims = (1,2)) 1ร—1ร—2 Array{Tuple{Int64, Int64}, 3}: [:, :, 1] = (1, 7) [:, :, 2] = (9, 15) ``` """ extrema(A::AbstractArray; dims) """ extrema(f, A::AbstractArray; dims) -> Array{Tuple} Compute the minimum and maximum of `f` applied to each element in the given dimensions of `A`. !!! compat "Julia 1.2" This method requires Julia 1.2 or later. """ extrema(f, A::AbstractArray; dims) """ extrema!(r, A) Compute the minimum and maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. $(_DOCS_ALIASING_WARNING) !!! compat "Julia 1.8" This method requires Julia 1.8 or later. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> extrema!([(1, 1); (1, 1)], A) 2-element Vector{Tuple{Int64, Int64}}: (1, 2) (3, 4) julia> extrema!([(1, 1);; (1, 1)], A) 1ร—2 Matrix{Tuple{Int64, Int64}}: (1, 3) (2, 4) ``` """ extrema!(r, A) """ all(A; dims) Test whether all values along the given dimensions of an array are `true`. # Examples ```jldoctest julia> A = [true false; true true] 2ร—2 Matrix{Bool}: 1 0 1 1 julia> all(A, dims=1) 1ร—2 Matrix{Bool}: 1 0 julia> all(A, dims=2) 2ร—1 Matrix{Bool}: 0 1 ``` """ all(A::AbstractArray; dims) """ all(p, A; dims) Determine whether predicate `p` returns `true` for all elements along the given dimensions of an array. # Examples ```jldoctest julia> A = [1 -1; 2 2] 2ร—2 Matrix{Int64}: 1 -1 2 2 julia> all(i -> i > 0, A, dims=1) 1ร—2 Matrix{Bool}: 1 0 julia> all(i -> i > 0, A, dims=2) 2ร—1 Matrix{Bool}: 0 1 ``` """ all(::Function, ::AbstractArray; dims) """ all!(r, A) Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [true false; true false] 2ร—2 Matrix{Bool}: 1 0 1 0 julia> all!([1; 1], A) 2-element Vector{Int64}: 0 0 julia> all!([1 1], A) 1ร—2 Matrix{Int64}: 1 0 ``` """ all!(r, A) """ any(A; dims) Test whether any values along the given dimensions of an array are `true`. # Examples ```jldoctest julia> A = [true false; true false] 2ร—2 Matrix{Bool}: 1 0 1 0 julia> any(A, dims=1) 1ร—2 Matrix{Bool}: 1 0 julia> any(A, dims=2) 2ร—1 Matrix{Bool}: 1 1 ``` """ any(::AbstractArray; dims) """ any(p, A; dims) Determine whether predicate `p` returns `true` for any elements along the given dimensions of an array. # Examples ```jldoctest julia> A = [1 -1; 2 -2] 2ร—2 Matrix{Int64}: 1 -1 2 -2 julia> any(i -> i > 0, A, dims=1) 1ร—2 Matrix{Bool}: 1 0 julia> any(i -> i > 0, A, dims=2) 2ร—1 Matrix{Bool}: 1 1 ``` """ any(::Function, ::AbstractArray; dims) """ any!(r, A) Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [true false; true false] 2ร—2 Matrix{Bool}: 1 0 1 0 julia> any!([1; 1], A) 2-element Vector{Int64}: 1 1 julia> any!([1 1], A) 1ร—2 Matrix{Int64}: 1 0 ``` """ any!(r, A) for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, :mul_prod), (:maximum, :_maximum, :max), (:minimum, :_minimum, :min), (:extrema, :_extrema, :_extrema_rf)] mapf = fname === :extrema ? :(ExtremaMap(f)) : :f @eval begin # User-facing methods with keyword arguments @inline ($fname)(a::AbstractArray; dims=:, kw...) = ($_fname)(a, dims; kw...) @inline ($fname)(f, a::AbstractArray; dims=:, kw...) = ($_fname)(f, a, dims; kw...) # Underlying implementations using dispatch ($_fname)(a, ::Colon; kw...) = ($_fname)(identity, a, :; kw...) ($_fname)(f, a, ::Colon; kw...) = mapreduce($mapf, $op, a; kw...) end end any(a::AbstractArray; dims=:) = _any(a, dims) any(f::Function, a::AbstractArray; dims=:) = _any(f, a, dims) _any(a, ::Colon) = _any(identity, a, :) all(a::AbstractArray; dims=:) = _all(a, dims) all(f::Function, a::AbstractArray; dims=:) = _all(f, a, dims) _all(a, ::Colon) = _all(identity, a, :) for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod), (:maximum, :max), (:minimum, :min), (:all, :&), (:any, :|), (:extrema, :_extrema_rf)] fname! = Symbol(fname, '!') _fname = Symbol('_', fname) mapf = fname === :extrema ? :(ExtremaMap(f)) : :f @eval begin $(fname!)(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = mapreducedim!($mapf, $(op), initarray!(r, $mapf, $(op), init, A), A) $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) $(_fname)(A, dims; kw...) = $(_fname)(identity, A, dims; kw...) $(_fname)(f, A, dims; kw...) = mapreduce($mapf, $(op), A; dims=dims, kw...) end end ##### findmin & findmax ##### # The initial values of Rval are not used if the corresponding indices in Rind are 0. # function findminmax!(f, op, Rval, Rind, A::AbstractArray{T,N}) where {T,N} (isempty(Rval) || isempty(A)) && return Rval, Rind lsiz = check_reducedims(Rval, A) for i = 1:N axes(Rval, i) == axes(Rind, i) || throw(DimensionMismatch("Find-reduction: outputs must have the same indices")) end # If we're reducing along dimension 1, for efficiency we can make use of a temporary. # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(Rval)) keep, Idefault = Broadcast.shapeindexer(indsRt) ks = keys(A) y = iterate(ks) zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(axes1(Rval)) @inbounds for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) tmpRv = Rval[i1,IR] tmpRi = Rind[i1,IR] for i in axes(A,1) k, kss = y::Tuple tmpAv = f(A[i,IA]) if tmpRi == zi || op(tmpRv, tmpAv) tmpRv = tmpAv tmpRi = k end y = iterate(ks, kss) end Rval[i1,IR] = tmpRv Rind[i1,IR] = tmpRi end else @inbounds for IA in CartesianIndices(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in axes(A, 1) k, kss = y::Tuple tmpAv = f(A[i,IA]) tmpRv = Rval[i,IR] tmpRi = Rind[i,IR] if tmpRi == zi || op(tmpRv, tmpAv) Rval[i,IR] = tmpAv Rind[i,IR] = k end y = iterate(ks, kss) end end end Rval, Rind end """ findmin!(rval, rind, A) -> (minval, index) Find the minimum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. `NaN` is treated as less than all other values except `missing`. $(_DOCS_ALIASING_WARNING) """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) findminmax!(identity, isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ findmin(A; dims) -> (minval, index) For an array input, returns the value and index of the minimum over the given dimensions. `NaN` is treated as less than all other values except `missing`. # Examples ```jldoctest julia> A = [1.0 2; 3 4] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> findmin(A, dims=1) ([1.0 2.0], CartesianIndex{2}[CartesianIndex(1, 1) CartesianIndex(1, 2)]) julia> findmin(A, dims=2) ([1.0; 3.0;;], CartesianIndex{2}[CartesianIndex(1, 1); CartesianIndex(2, 1);;]) ``` """ findmin(A::AbstractArray; dims=:) = _findmin(A, dims) _findmin(A, dims) = _findmin(identity, A, dims) """ findmin(f, A; dims) -> (f(x), index) For an array input, returns the value in the codomain and index of the corresponding value which minimize `f` over the given dimensions. # Examples ```jldoctest julia> A = [-1.0 1; -0.5 2] 2ร—2 Matrix{Float64}: -1.0 1.0 -0.5 2.0 julia> findmin(abs2, A, dims=1) ([0.25 1.0], CartesianIndex{2}[CartesianIndex(2, 1) CartesianIndex(1, 2)]) julia> findmin(abs2, A, dims=2) ([1.0; 0.25;;], CartesianIndex{2}[CartesianIndex(1, 1); CartesianIndex(2, 1);;]) ``` """ findmin(f, A::AbstractArray; dims=:) = _findmin(f, A, dims) function _findmin(f, A, region) ri = reduced_indices0(A, region) if isempty(A) if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end similar(A, promote_op(f, eltype(A)), ri), zeros(eltype(keys(A)), ri) else fA = f(first(A)) findminmax!(f, isgreater, fill!(similar(A, _findminmax_inittype(f, A), ri), fA), zeros(eltype(keys(A)), ri), A) end end """ findmax!(rval, rind, A) -> (maxval, index) Find the maximum of `A` and the corresponding linear index along singleton dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. `NaN` is treated as greater than all other values except `missing`. $(_DOCS_ALIASING_WARNING) """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) findminmax!(identity, isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ findmax(A; dims) -> (maxval, index) For an array input, returns the value and index of the maximum over the given dimensions. `NaN` is treated as greater than all other values except `missing`. # Examples ```jldoctest julia> A = [1.0 2; 3 4] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> findmax(A, dims=1) ([3.0 4.0], CartesianIndex{2}[CartesianIndex(2, 1) CartesianIndex(2, 2)]) julia> findmax(A, dims=2) ([2.0; 4.0;;], CartesianIndex{2}[CartesianIndex(1, 2); CartesianIndex(2, 2);;]) ``` """ findmax(A::AbstractArray; dims=:) = _findmax(A, dims) _findmax(A, dims) = _findmax(identity, A, dims) """ findmax(f, A; dims) -> (f(x), index) For an array input, returns the value in the codomain and index of the corresponding value which maximize `f` over the given dimensions. # Examples ```jldoctest julia> A = [-1.0 1; -0.5 2] 2ร—2 Matrix{Float64}: -1.0 1.0 -0.5 2.0 julia> findmax(abs2, A, dims=1) ([1.0 4.0], CartesianIndex{2}[CartesianIndex(1, 1) CartesianIndex(2, 2)]) julia> findmax(abs2, A, dims=2) ([1.0; 4.0;;], CartesianIndex{2}[CartesianIndex(1, 1); CartesianIndex(2, 2);;]) ``` """ findmax(f, A::AbstractArray; dims=:) = _findmax(f, A, dims) function _findmax(f, A, region) ri = reduced_indices0(A, region) if isempty(A) if prod(map(length, reduced_indices(A, region))) != 0 throw(ArgumentError("collection slices must be non-empty")) end similar(A, promote_op(f, eltype(A)), ri), zeros(eltype(keys(A)), ri) else fA = f(first(A)) findminmax!(f, isless, fill!(similar(A, _findminmax_inittype(f, A), ri), fA), zeros(eltype(keys(A)), ri), A) end end function _findminmax_inittype(f, A::AbstractArray) T = _realtype(f, promote_union(eltype(A))) v0 = f(first(A)) # First conditional: T is >: typeof(v0), so return it # Second conditional: handle missing specifically, as most often, f(missing) = missing; # certainly, some predicate functions return Bool, but not all. # Else, return the type of the transformation. Tr = v0 isa T ? T : Missing <: eltype(A) ? Union{Missing, typeof(v0)} : typeof(v0) end reducedim1(R, A) = length(axes1(R)) == 1 """ argmin(A; dims) -> indices For an array input, return the indices of the minimum elements over the given dimensions. `NaN` is treated as less than all other values except `missing`. # Examples ```jldoctest julia> A = [1.0 2; 3 4] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> argmin(A, dims=1) 1ร—2 Matrix{CartesianIndex{2}}: CartesianIndex(1, 1) CartesianIndex(1, 2) julia> argmin(A, dims=2) 2ร—1 Matrix{CartesianIndex{2}}: CartesianIndex(1, 1) CartesianIndex(2, 1) ``` """ argmin(A::AbstractArray; dims=:) = findmin(A; dims=dims)[2] """ argmax(A; dims) -> indices For an array input, return the indices of the maximum elements over the given dimensions. `NaN` is treated as greater than all other values except `missing`. # Examples ```jldoctest julia> A = [1.0 2; 3 4] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> argmax(A, dims=1) 1ร—2 Matrix{CartesianIndex{2}}: CartesianIndex(2, 1) CartesianIndex(2, 2) julia> argmax(A, dims=2) 2ร—1 Matrix{CartesianIndex{2}}: CartesianIndex(1, 2) CartesianIndex(2, 2) ``` """ argmax(A::AbstractArray; dims=:) = findmax(A; dims=dims)[2] Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/accumulate.jlŽ/# This file is a part of Julia. License is MIT: https://julialang.org/license # accumulate_pairwise slightly slower then accumulate, but more numerically # stable in certain situations (e.g. sums). # it does double the number of operations compared to accumulate, # though for cheap operations like + this does not have much impact (20%) function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} @inbounds if n < 128 s_ = v[i1] c[i1] = op(s, s_) for i = i1+1:i1+n-1 s_ = op(s_, v[i]) c[i] = op(s, s_) end else n2 = n >> 1 s_ = _accumulate_pairwise!(op, c, v, s, i1, n2) s_ = op(s_, _accumulate_pairwise!(op, c, v, op(s, s_), i1+n2, n-n2)) end return s_ end function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) where Op li = LinearIndices(v) li != LinearIndices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) n = length(li) n == 0 && return result i1 = first(li) @inbounds result[i1] = v1 = reduce_first(op,v[i1]) n == 1 && return result _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) return result end function accumulate_pairwise(op, v::AbstractVector{T}) where T out = similar(v, promote_op(op, T, T)) return accumulate_pairwise!(op, out, v) end """ cumsum!(B, A; dims::Integer) Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). $(_DOCS_ALIASING_WARNING) """ cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(add_sum, B, A, dims=dims) function cumsum!(out::AbstractArray, v::AbstractVector; dims::Integer=1) # we dispatch on the possibility of numerical stability issues _cumsum!(out, v, dims, ArithmeticStyle(eltype(out))) end function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticRounds) where {T} dim == 1 ? accumulate_pairwise!(add_sum, out, v) : copyto!(out, v) end function _cumsum!(out::AbstractArray, v, dim, ::ArithmeticUnknown) _cumsum!(out, v, dim, ArithmeticRounds()) end function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticStyle) where {T} dim == 1 ? accumulate!(add_sum, out, v) : copyto!(out, v) end """ cumsum(A; dims::Integer) Cumulative sum along the dimension `dims`. See also [`cumsum!`](@ref) to use a preallocated output array, both for performance and to control the precision of the output (e.g. to avoid overflow). # Examples ```jldoctest julia> a = [1 2 3; 4 5 6] 2ร—3 Matrix{Int64}: 1 2 3 4 5 6 julia> cumsum(a, dims=1) 2ร—3 Matrix{Int64}: 1 2 3 5 7 9 julia> cumsum(a, dims=2) 2ร—3 Matrix{Int64}: 1 3 6 4 9 15 ``` !!! note The return array's `eltype` is `Int` for signed integers of less than system word size and `UInt` for unsigned integers of less than system word size. To preserve `eltype` of arrays with small signed or unsigned integer `accumulate(+, A)` should be used. ```jldoctest julia> cumsum(Int8[100, 28]) 2-element Vector{Int64}: 100 128 julia> accumulate(+,Int8[100, 28]) 2-element Vector{Int8}: 100 -128 ``` In the former case, the integers are widened to system word size and therefore the result is `Int64[100, 128]`. In the latter case, no such widening happens and integer overflow results in `Int8[100, -128]`. """ function cumsum(A::AbstractArray{T}; dims::Integer) where T out = similar(A, promote_op(add_sum, T, T)) cumsum!(out, A, dims=dims) end """ cumsum(itr) Cumulative sum of an iterator. See also [`accumulate`](@ref) to apply functions other than `+`. !!! compat "Julia 1.5" `cumsum` on a non-array iterator requires at least Julia 1.5. # Examples ```jldoctest julia> cumsum(1:3) 3-element Vector{Int64}: 1 3 6 julia> cumsum((true, false, true, false, true)) (1, 1, 2, 2, 3) julia> cumsum(fill(1, 2) for i in 1:3) 3-element Vector{Vector{Int64}}: [1, 1] [2, 2] [3, 3] ``` """ cumsum(x::AbstractVector) = cumsum(x, dims=1) cumsum(itr) = accumulate(add_sum, itr) """ cumprod!(B, A; dims::Integer) Cumulative product of `A` along the dimension `dims`, storing the result in `B`. See also [`cumprod`](@ref). $(_DOCS_ALIASING_WARNING) """ cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = accumulate!(mul_prod, B, A, dims=dims) """ cumprod!(y::AbstractVector, x::AbstractVector) Cumulative product of a vector `x`, storing the result in `y`. See also [`cumprod`](@ref). $(_DOCS_ALIASING_WARNING) """ cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) """ cumprod(A; dims::Integer) Cumulative product along the dimension `dim`. See also [`cumprod!`](@ref) to use a preallocated output array, both for performance and to control the precision of the output (e.g. to avoid overflow). # Examples ```jldoctest julia> a = Int8[1 2 3; 4 5 6]; julia> cumprod(a, dims=1) 2ร—3 Matrix{Int64}: 1 2 3 4 10 18 julia> cumprod(a, dims=2) 2ร—3 Matrix{Int64}: 1 2 6 4 20 120 ``` """ function cumprod(A::AbstractArray; dims::Integer) return accumulate(mul_prod, A, dims=dims) end """ cumprod(itr) Cumulative product of an iterator. See also [`cumprod!`](@ref), [`accumulate`](@ref), [`cumsum`](@ref). !!! compat "Julia 1.5" `cumprod` on a non-array iterator requires at least Julia 1.5. # Examples ```jldoctest julia> cumprod(fill(1//2, 3)) 3-element Vector{Rational{Int64}}: 1//2 1//4 1//8 julia> cumprod((1, 2, 1, 3, 1)) (1, 2, 2, 6, 6) julia> cumprod("julia") 5-element Vector{String}: "j" "ju" "jul" "juli" "julia" ``` """ cumprod(x::AbstractVector) = cumprod(x, dims=1) cumprod(itr) = accumulate(mul_prod, itr) """ accumulate(op, A; dims::Integer, [init]) Cumulative operation `op` along the dimension `dims` of `A` (providing `dims` is optional for vectors). An initial value `init` may optionally be provided by a keyword argument. See also [`accumulate!`](@ref) to use a preallocated output array, both for performance and to control the precision of the output (e.g. to avoid overflow). For common operations there are specialized variants of `accumulate`, see [`cumsum`](@ref), [`cumprod`](@ref). For a lazy version, see [`Iterators.accumulate`](@ref). !!! compat "Julia 1.5" `accumulate` on a non-array iterator requires at least Julia 1.5. # Examples ```jldoctest julia> accumulate(+, [1,2,3]) 3-element Vector{Int64}: 1 3 6 julia> accumulate(min, (1, -2, 3, -4, 5), init=0) (0, -2, -2, -4, -4) julia> accumulate(/, (2, 4, Inf), init=100) (50.0, 12.5, 0.0) julia> accumulate(=>, i^2 for i in 1:3) 3-element Vector{Any}: 1 1 => 4 (1 => 4) => 9 julia> accumulate(+, fill(1, 3, 4)) 3ร—4 Matrix{Int64}: 1 4 7 10 2 5 8 11 3 6 9 12 julia> accumulate(+, fill(1, 2, 5), dims=2, init=100.0) 2ร—5 Matrix{Float64}: 101.0 102.0 103.0 104.0 105.0 101.0 102.0 103.0 104.0 105.0 ``` """ function accumulate(op, A; dims::Union{Nothing,Integer}=nothing, kw...) if dims === nothing && !(A isa AbstractVector) # This branch takes care of the cases not handled by `_accumulate!`. return collect(Iterators.accumulate(op, A; kw...)) end nt = values(kw) if isempty(kw) out = similar(A, promote_op(op, eltype(A), eltype(A))) elseif keys(nt) === (:init,) out = similar(A, promote_op(op, typeof(nt.init), eltype(A))) else throw(ArgumentError("accumulate does not support the keyword arguments $(setdiff(keys(nt), (:init,)))")) end accumulate!(op, out, A; dims=dims, kw...) end function accumulate(op, xs::Tuple; init = _InitialValue()) rf = BottomRF(op) ys, = afoldl(((), init), xs...) do (ys, acc), x acc = rf(acc, x) (ys..., acc), acc end return ys end """ accumulate!(op, B, A; [dims], [init]) Cumulative operation `op` on `A` along the dimension `dims`, storing the result in `B`. Providing `dims` is optional for vectors. If the keyword argument `init` is given, its value is used to instantiate the accumulation. $(_DOCS_ALIASING_WARNING) See also [`accumulate`](@ref), [`cumsum!`](@ref), [`cumprod!`](@ref). # Examples ```jldoctest julia> x = [1, 0, 2, 0, 3]; julia> y = rand(5); julia> accumulate!(+, y, x); julia> y 5-element Vector{Float64}: 1.0 1.0 3.0 3.0 6.0 julia> A = [1 2 3; 4 5 6]; julia> B = similar(A); julia> accumulate!(-, B, A, dims=1) 2ร—3 Matrix{Int64}: 1 2 3 -3 -3 -3 julia> accumulate!(*, B, A, dims=2, init=10) 2ร—3 Matrix{Int64}: 10 20 60 40 200 1200 ``` """ function accumulate!(op, B, A; dims::Union{Integer, Nothing} = nothing, kw...) nt = values(kw) if isempty(kw) _accumulate!(op, B, A, dims, nothing) elseif keys(kw) === (:init,) _accumulate!(op, B, A, dims, Some(nt.init)) else throw(ArgumentError("accumulate! does not support the keyword arguments $(setdiff(keys(nt), (:init,)))")) end end function _accumulate!(op, B, A, dims::Nothing, init::Union{Nothing, Some}) throw(ArgumentError("Keyword argument dims must be provided for multidimensional arrays")) end function _accumulate!(op, B, A::AbstractVector, dims::Nothing, init::Nothing) isempty(A) && return B v1 = reduce_first(op, first(A)) _accumulate1!(op, B, v1, A, 1) end function _accumulate!(op, B, A::AbstractVector, dims::Nothing, init::Some) isempty(A) && return B v1 = op(something(init), first(A)) _accumulate1!(op, B, v1, A, 1) end function _accumulate!(op, B, A, dims::Integer, init::Union{Nothing, Some}) dims > 0 || throw(ArgumentError("dims must be a positive integer")) inds_t = axes(A) axes(B) == inds_t || throw(DimensionMismatch("shape of B must match A")) dims > ndims(A) && return copyto!(B, A) isempty(inds_t[dims]) && return B if dims == 1 # We can accumulate to a temporary variable, which allows # register usage and will be slightly faster ind1 = inds_t[1] @inbounds for I in CartesianIndices(tail(inds_t)) if init === nothing tmp = reduce_first(op, A[first(ind1), I]) else tmp = op(something(init), A[first(ind1), I]) end B[first(ind1), I] = tmp for i_1 = first(ind1)+1:last(ind1) tmp = op(tmp, A[i_1, I]) B[i_1, I] = tmp end end else R1 = CartesianIndices(axes(A)[1:dims-1]) # not type-stable R2 = CartesianIndices(axes(A)[dims+1:end]) _accumulaten!(op, B, A, R1, inds_t[dims], R2, init) # use function barrier end return B end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Nothing) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) @inbounds for J in R2, I in R1 B[I, ii, J] = reduce_first(op, A[I, ii, J]) end # Accumulate @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) end B end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Some) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) @inbounds for J in R2, I in R1 B[I, ii, J] = op(something(init), A[I, ii, J]) end # Accumulate @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) end B end function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) dim > 0 || throw(ArgumentError("dim must be a positive integer")) inds = LinearIndices(A) inds == LinearIndices(B) || throw(DimensionMismatch("LinearIndices of A and B don't match")) dim > 1 && return copyto!(B, A) (i1, state) = iterate(inds)::NTuple{2,Any} # We checked earlier that A isn't empty cur_val = v1 B[i1] = cur_val next = iterate(inds, state) @inbounds while next !== nothing (i, state) = next cur_val = op(cur_val, A[i]) B[i] = cur_val next = iterate(inds, state) end return B end X/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/permuteddimsarray.jl๗'# This file is a part of Julia. License is MIT: https://julialang.org/license module PermutedDimsArrays import Base: permutedims, permutedims! export PermutedDimsArray # Some day we will want storage-order-aware iteration, so put perm in the parameters struct PermutedDimsArray{T,N,perm,iperm,AA<:AbstractArray} <: AbstractArray{T,N} parent::AA function PermutedDimsArray{T,N,perm,iperm,AA}(data::AA) where {T,N,perm,iperm,AA<:AbstractArray} (isa(perm, NTuple{N,Int}) && isa(iperm, NTuple{N,Int})) || error("perm and iperm must both be NTuple{$N,Int}") isperm(perm) || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N))) all(map(d->iperm[perm[d]]==d, 1:N)) || throw(ArgumentError(string(perm, " and ", iperm, " must be inverses"))) new(data) end end """ PermutedDimsArray(A, perm) -> B Given an AbstractArray `A`, create a view `B` such that the dimensions appear to be permuted. Similar to `permutedims`, except that no copying occurs (`B` shares storage with `A`). See also [`permutedims`](@ref), [`invperm`](@ref). # Examples ```jldoctest julia> A = rand(3,5,4); julia> B = PermutedDimsArray(A, (3,1,2)); julia> size(B) (4, 3, 5) julia> B[3,1,2] == A[1,2,3] true ``` """ function PermutedDimsArray(data::AbstractArray{T,N}, perm) where {T,N} length(perm) == N || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N))) iperm = invperm(perm) PermutedDimsArray{T,N,(perm...,),(iperm...,),typeof(data)}(data) end Base.parent(A::PermutedDimsArray) = A.parent Base.size(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(size(parent(A)), perm) Base.axes(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(axes(parent(A)), perm) Base.has_offset_axes(A::PermutedDimsArray) = Base.has_offset_axes(A.parent) Base.similar(A::PermutedDimsArray, T::Type, dims::Base.Dims) = similar(parent(A), T, dims) Base.unsafe_convert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, parent(A)) # It's OK to return a pointer to the first element, and indeed quite # useful for wrapping C routines that require a different storage # order than used by Julia. But for an array with unconventional # storage order, a linear offset is ambiguous---is it a memory offset # or a linear index? Base.pointer(A::PermutedDimsArray, i::Integer) = throw(ArgumentError("pointer(A, i) is deliberately unsupported for PermutedDimsArray")) function Base.strides(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} s = strides(parent(A)) ntuple(d->s[perm[d]], Val(N)) end Base.elsize(::Type{<:PermutedDimsArray{<:Any, <:Any, <:Any, <:Any, P}}) where {P} = Base.elsize(P) @inline function Base.getindex(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm} @boundscheck checkbounds(A, I...) @inbounds val = getindex(A.parent, genperm(I, iperm)...) val end @inline function Base.setindex!(A::PermutedDimsArray{T,N,perm,iperm}, val, I::Vararg{Int,N}) where {T,N,perm,iperm} @boundscheck checkbounds(A, I...) @inbounds setindex!(A.parent, val, genperm(I, iperm)...) val end function Base.isassigned(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm} @boundscheck checkbounds(Bool, A, I...) || return false @inbounds x = isassigned(A.parent, genperm(I, iperm)...) x end @inline genperm(I::NTuple{N,Any}, perm::Dims{N}) where {N} = ntuple(d -> I[perm[d]], Val(N)) @inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,)) """ permutedims(A::AbstractArray, perm) Permute the dimensions of array `A`. `perm` is a vector or a tuple of length `ndims(A)` specifying the permutation. See also [`permutedims!`](@ref), [`PermutedDimsArray`](@ref), [`transpose`](@ref), [`invperm`](@ref). # Examples ```jldoctest julia> A = reshape(Vector(1:8), (2,2,2)) 2ร—2ร—2 Array{Int64, 3}: [:, :, 1] = 1 3 2 4 [:, :, 2] = 5 7 6 8 julia> perm = (3, 1, 2); # put the last dimension first julia> B = permutedims(A, perm) 2ร—2ร—2 Array{Int64, 3}: [:, :, 1] = 1 2 5 6 [:, :, 2] = 3 4 7 8 julia> A == permutedims(B, invperm(perm)) # the inverse permutation true ``` For each dimension `i` of `B = permutedims(A, perm)`, its corresponding dimension of `A` will be `perm[i]`. This means the equality `size(B, i) == size(A, perm[i])` holds. ```jldoctest julia> A = randn(5, 7, 11, 13); julia> perm = [4, 1, 3, 2]; julia> B = permutedims(A, perm); julia> size(B) (13, 5, 11, 7) julia> size(A)[perm] == ans true ``` """ function permutedims(A::AbstractArray, perm) dest = similar(A, genperm(axes(A), perm)) permutedims!(dest, A, perm) end """ permutedims(m::AbstractMatrix) Permute the dimensions of the matrix `m`, by flipping the elements across the diagonal of the matrix. Differs from `LinearAlgebra`'s [`transpose`](@ref) in that the operation is not recursive. # Examples ```jldoctest; setup = :(using LinearAlgebra) julia> a = [1 2; 3 4]; julia> b = [5 6; 7 8]; julia> c = [9 10; 11 12]; julia> d = [13 14; 15 16]; julia> X = [[a] [b]; [c] [d]] 2ร—2 Matrix{Matrix{Int64}}: [1 2; 3 4] [5 6; 7 8] [9 10; 11 12] [13 14; 15 16] julia> permutedims(X) 2ร—2 Matrix{Matrix{Int64}}: [1 2; 3 4] [9 10; 11 12] [5 6; 7 8] [13 14; 15 16] julia> transpose(X) 2ร—2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}: [1 3; 2 4] [9 11; 10 12] [5 7; 6 8] [13 15; 14 16] ``` """ permutedims(A::AbstractMatrix) = permutedims(A, (2,1)) """ permutedims(v::AbstractVector) Reshape vector `v` into a `1 ร— length(v)` row matrix. Differs from `LinearAlgebra`'s [`transpose`](@ref) in that the operation is not recursive. # Examples ```jldoctest; setup = :(using LinearAlgebra) julia> permutedims([1, 2, 3, 4]) 1ร—4 Matrix{Int64}: 1 2 3 4 julia> V = [[[1 2; 3 4]]; [[5 6; 7 8]]] 2-element Vector{Matrix{Int64}}: [1 2; 3 4] [5 6; 7 8] julia> permutedims(V) 1ร—2 Matrix{Matrix{Int64}}: [1 2; 3 4] [5 6; 7 8] julia> transpose(V) 1ร—2 transpose(::Vector{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}: [1 3; 2 4] [5 7; 6 8] ``` """ permutedims(v::AbstractVector) = reshape(v, (1, length(v))) """ permutedims!(dest, src, perm) Permute the dimensions of array `src` and store the result in the array `dest`. `perm` is a vector specifying a permutation of length `ndims(src)`. The preallocated array `dest` should have `size(dest) == size(src)[perm]` and is completely overwritten. No in-place permutation is supported and unexpected results will happen if `src` and `dest` have overlapping memory regions. See also [`permutedims`](@ref). """ function permutedims!(dest, src::AbstractArray, perm) Base.checkdims_perm(dest, src, perm) P = PermutedDimsArray(dest, invperm(perm)) _copy!(P, src) return dest end function Base.copyto!(dest::PermutedDimsArray{T,N}, src::AbstractArray{T,N}) where {T,N} checkbounds(dest, axes(src)...) _copy!(dest, src) end Base.copyto!(dest::PermutedDimsArray, src::AbstractArray) = _copy!(dest, src) function _copy!(P::PermutedDimsArray{T,N,perm}, src) where {T,N,perm} # If dest/src are "close to dense," then it pays to be cache-friendly. # Determine the first permuted dimension d = 0 # d+1 will hold the first permuted dimension of src while d < ndims(src) && perm[d+1] == d+1 d += 1 end if d == ndims(src) copyto!(parent(P), src) # it's not permuted else R1 = CartesianIndices(axes(src)[1:d]) d1 = findfirst(isequal(d+1), perm)::Int # first permuted dim of dest R2 = CartesianIndices(axes(src)[d+2:d1-1]) R3 = CartesianIndices(axes(src)[d1+1:end]) _permutedims!(P, src, R1, R2, R3, d+1, d1) end return P end @noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianIndices{0}, R2, R3, ds, dp) ip, is = axes(src, dp), axes(src, ds) for jo in first(ip):8:last(ip), io in first(is):8:last(is) for I3 in R3, I2 in R2 for j in jo:min(jo+7, last(ip)) for i in io:min(io+7, last(is)) @inbounds P[i, I2, j, I3] = src[i, I2, j, I3] end end end end P end @noinline function _permutedims!(P::PermutedDimsArray, src, R1, R2, R3, ds, dp) ip, is = axes(src, dp), axes(src, ds) for jo in first(ip):8:last(ip), io in first(is):8:last(is) for I3 in R3, I2 in R2 for j in jo:min(jo+7, last(ip)) for i in io:min(io+7, last(is)) for I1 in R1 @inbounds P[I1, i, I2, j, I3] = src[I1, i, I2, j, I3] end end end end end P end const CommutativeOps = Union{typeof(+),typeof(Base.add_sum),typeof(min),typeof(max),typeof(Base._extrema_rf),typeof(|),typeof(&)} function Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::PermutedDimsArray, dims::Colon) Base._mapreduce_dim(f, op, init, parent(A), dims) end function Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(Base.mul_prod),typeof(*)}, init::Base._InitialValue, A::PermutedDimsArray{<:Union{Real,Complex}}, dims::Colon) Base._mapreduce_dim(f, op, init, parent(A), dims) end function Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray{T,N}, A::PermutedDimsArray{S,N,perm,iperm}) where {T,S,N,perm,iperm} C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output Base.mapreducedim!(f, op, C, parent(A)) B end function Base.mapreducedim!(f::typeof(identity), op::Union{typeof(Base.mul_prod),typeof(*)}, B::AbstractArray{T,N}, A::PermutedDimsArray{<:Union{Real,Complex},N,perm,iperm}) where {T,N,perm,iperm} C = PermutedDimsArray{T,N,iperm,perm,typeof(B)}(B) # make the inverse permutation for the output Base.mapreducedim!(f, op, C, parent(A)) B end function Base.showarg(io::IO, A::PermutedDimsArray{T,N,perm}, toplevel) where {T,N,perm} print(io, "PermutedDimsArray(") Base.showarg(io, parent(A), false) print(io, ", ", perm, ')') toplevel && print(io, " with eltype ", eltype(A)) return nothing end end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ordering.jl฿# This file is a part of Julia. License is MIT: https://julialang.org/license module Order import ..@__MODULE__, ..parentmodule const Base = parentmodule(@__MODULE__) import .Base: AbstractVector, @propagate_inbounds, isless, identity, getindex, reverse, +, -, !, &, <, | ## notions of element ordering ## export # not exported by Base Ordering, Forward, Reverse, By, Lt, Perm, ReverseOrdering, ForwardOrdering, DirectOrdering, lt, ord, ordtype """ Base.Order.Ordering Abstract type which represents a total order on some set of elements. Use [`Base.Order.lt`](@ref) to compare two elements according to the ordering. """ abstract type Ordering end struct ForwardOrdering <: Ordering end """ ReverseOrdering(fwd::Ordering=Forward) A wrapper which reverses an ordering. For a given `Ordering` `o`, the following holds for all `a`, `b`: lt(ReverseOrdering(o), a, b) == lt(o, b, a) """ struct ReverseOrdering{Fwd<:Ordering} <: Ordering fwd::Fwd end ReverseOrdering(rev::ReverseOrdering) = rev.fwd ReverseOrdering(fwd::Fwd) where {Fwd} = ReverseOrdering{Fwd}(fwd) ReverseOrdering() = ReverseOrdering(ForwardOrdering()) """ reverse(o::Base.Ordering) reverses ordering specified by `o`. """ reverse(o::Ordering) = ReverseOrdering(o) const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}} """ Base.Order.Forward Default ordering according to [`isless`](@ref). """ const Forward = ForwardOrdering() """ Base.Order.Reverse Reverse ordering according to [`isless`](@ref). """ const Reverse = ReverseOrdering() """ By(by, order::Ordering=Forward) `Ordering` which applies `order` to elements after they have been transformed by the function `by`. """ struct By{T, O} <: Ordering by::T order::O end # backwards compatibility with VERSION < v"1.5-" By(by) = By(by, Forward) """ Lt(lt) `Ordering` that calls `lt(a, b)` to compare elements. `lt` must obey the same rules as the `lt` parameter of [`sort!`](@ref). """ struct Lt{T} <: Ordering lt::T end """ Perm(order::Ordering, data::AbstractVector) `Ordering` on the indices of `data` where `i` is less than `j` if `data[i]` is less than `data[j]` according to `order`. In the case that `data[i]` and `data[j]` are equal, `i` and `j` are compared by numeric value. """ struct Perm{O<:Ordering,V<:AbstractVector} <: Ordering order::O data::V end ReverseOrdering(by::By) = By(by.by, ReverseOrdering(by.order)) ReverseOrdering(perm::Perm) = Perm(ReverseOrdering(perm.order), perm.data) """ lt(o::Ordering, a, b) Test whether `a` is less than `b` according to the ordering `o`. """ lt(o::ForwardOrdering, a, b) = isless(a,b) lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a) lt(o::By, a, b) = lt(o.order,o.by(a),o.by(b)) lt(o::Lt, a, b) = o.lt(a,b) @propagate_inbounds function lt(p::Perm, a::Integer, b::Integer) da = p.data[a] db = p.data[b] (lt(p.order, da, db)::Bool) | (!(lt(p.order, db, da)::Bool) & (a < b)) end _ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order _ord(lt::typeof(isless), by, order::Ordering) = By(by, order) function _ord(lt, by, order::Ordering) if order === Forward return Lt((x, y) -> lt(by(x), by(y))) elseif order === Reverse return Lt((x, y) -> lt(by(y), by(x))) else error("Passing both lt= and order= arguments is ambiguous; please pass order=Forward or order=Reverse (or leave default)") end end """ ord(lt, by, rev::Union{Bool, Nothing}, order::Ordering=Forward) Construct an [`Ordering`](@ref) object from the same arguments used by [`sort!`](@ref). Elements are first transformed by the function `by` (which may be [`identity`](@ref)) and are then compared according to either the function `lt` or an existing ordering `order`. `lt` should be [`isless`](@ref) or a function that obeys the same rules as the `lt` parameter of [`sort!`](@ref). Finally, the resulting order is reversed if `rev=true`. Passing an `lt` other than `isless` along with an `order` other than [`Base.Order.Forward`](@ref) or [`Base.Order.Reverse`](@ref) is not permitted, otherwise all options are independent and can be used together in all possible combinations. """ ord(lt, by, rev::Nothing, order::Ordering=Forward) = _ord(lt, by, order) function ord(lt, by, rev::Bool, order::Ordering=Forward) o = _ord(lt, by, order) return rev ? ReverseOrdering(o) : o end # This function is not in use anywhere in Base but we observed # use in sorting-related packages (#34719). It's probably best to move # this functionality to those packages in the future; let's remind/force # ourselves to deprecate this in v2.0. # The following clause means `if VERSION < v"2.0-"` but it also works during # bootstrap. For the same reason, we need to write `Int32` instead of `Cint`. if ccall(:jl_ver_major, Int32, ()) < 2 ordtype(o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs) ordtype(o::Perm, vs::AbstractArray) = ordtype(o.order, o.data) # TODO: here, we really want the return type of o.by, without calling it ordtype(o::By, vs::AbstractArray) = try typeof(o.by(vs[1])) catch; Any end ordtype(o::Ordering, vs::AbstractArray) = eltype(vs) end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/sort.jl๕# This file is a part of Julia. License is MIT: https://julialang.org/license module Sort using Base.Order using Base: copymutable, midpoint, require_one_based_indexing, uinttype, sub_with_overflow, add_with_overflow, OneTo, BitSigned, BitIntegerType, top_set_bit import Base: sort, sort!, issorted, sortperm, to_indices export # also exported by Base # order-only: issorted, searchsorted, searchsortedfirst, searchsortedlast, insorted, # order & algorithm: sort, sort!, sortperm, sortperm!, partialsort, partialsort!, partialsortperm, partialsortperm!, # algorithms: InsertionSort, QuickSort, MergeSort, PartialQuickSort export # not exported by Base Algorithm, DEFAULT_UNSTABLE, DEFAULT_STABLE, SMALL_ALGORITHM, SMALL_THRESHOLD abstract type Algorithm end ## functions requiring only ordering ## function issorted(itr, order::Ordering) y = iterate(itr) y === nothing && return true prev, state = y y = iterate(itr, state) while y !== nothing this, state = y lt(order, this, prev) && return false prev = this y = iterate(itr, state) end return true end """ issorted(v, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Test whether a collection is in sorted order. The keywords modify what order is considered sorted, as described in the [`sort!`](@ref) documentation. # Examples ```jldoctest julia> issorted([1, 2, 3]) true julia> issorted([(1, "b"), (2, "a")], by = x -> x[1]) true julia> issorted([(1, "b"), (2, "a")], by = x -> x[2]) false julia> issorted([(1, "b"), (2, "a")], by = x -> x[2], rev=true) true julia> issorted([1, 2, -2, 3], by=abs) true ``` """ issorted(itr; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = issorted(itr, ord(lt,by,rev,order)) function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering) _sort!(v, InitialOptimizations(ScratchQuickSort(k)), o, (;)) maybeview(v, k) end maybeview(v, k) = view(v, k) maybeview(v, k::Integer) = v[k] """ partialsort!(v, k; by=identity, lt=isless, rev=false) Partially sort the vector `v` in place so that the value at index `k` (or range of adjacent values if `k` is a range) occurs at the position where it would appear if the array were fully sorted. If `k` is a single index, that value is returned; if `k` is a range, an array of values at those indices is returned. Note that `partialsort!` may not fully sort the input array. For the keyword arguments, see the documentation of [`sort!`](@ref). # Examples ```jldoctest julia> a = [1, 2, 4, 3, 4] 5-element Vector{Int64}: 1 2 4 3 4 julia> partialsort!(a, 4) 4 julia> a 5-element Vector{Int64}: 1 2 3 4 4 julia> a = [1, 2, 4, 3, 4] 5-element Vector{Int64}: 1 2 4 3 4 julia> partialsort!(a, 4, rev=true) 2 julia> a 5-element Vector{Int64}: 4 4 3 2 1 ``` """ partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = partialsort!(v, k, ord(lt,by,rev,order)) """ partialsort(v, k, by=identity, lt=isless, rev=false) Variant of [`partialsort!`](@ref) that copies `v` before partially sorting it, thereby returning the same thing as `partialsort!` but leaving `v` unmodified. """ partialsort(v::AbstractVector, k::Union{Integer,OrdinalRange}; kws...) = partialsort!(copymutable(v), k; kws...) # reference on sorted binary search: # http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary # index of the first value of vector a that is greater than or equivalent to x; # returns lastindex(v)+1 if x is greater than all values in v. function searchsortedfirst(v::AbstractVector, x, lo::T, hi::T, o::Ordering)::keytype(v) where T<:Integer hi = hi + T(1) len = hi - lo @inbounds while len != 0 half_len = len >>> 0x01 m = lo + half_len if lt(o, v[m], x) lo = m + 1 len -= half_len + 1 else hi = m len = half_len end end return lo end # index of the last value of vector a that is less than or equivalent to x; # returns firstindex(v)-1 if x is less than all values of v. function searchsortedlast(v::AbstractVector, x, lo::T, hi::T, o::Ordering)::keytype(v) where T<:Integer u = T(1) lo = lo - u hi = hi + u @inbounds while lo < hi - u m = midpoint(lo, hi) if lt(o, x, v[m]) hi = m else lo = m end end return lo end # returns the range of indices of v equivalent to x # if v does not contain x, returns a 0-length range # indicating the insertion point of x function searchsorted(v::AbstractVector, x, ilo::T, ihi::T, o::Ordering)::UnitRange{keytype(v)} where T<:Integer u = T(1) lo = ilo - u hi = ihi + u @inbounds while lo < hi - u m = midpoint(lo, hi) if lt(o, v[m], x) lo = m elseif lt(o, x, v[m]) hi = m else a = searchsortedfirst(v, x, max(lo,ilo), m, o) b = searchsortedlast(v, x, m, min(hi,ihi), o) return a : b end end return (lo + 1) : (hi - 1) end function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)::keytype(a) require_one_based_indexing(a) f, h, l = first(a), step(a), last(a) if lt(o, x, f) 0 elseif h == 0 || !lt(o, x, l) length(a) else n = round(Integer, (x - f) / h + 1) lt(o, x, a[n]) ? n - 1 : n end end function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering)::keytype(a) require_one_based_indexing(a) f, h, l = first(a), step(a), last(a) if !lt(o, f, x) 1 elseif h == 0 || lt(o, l, x) length(a) + 1 else n = round(Integer, (x - f) / h + 1) lt(o, a[n], x) ? n + 1 : n end end function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering)::keytype(a) require_one_based_indexing(a) f, h, l = first(a), step(a), last(a) if lt(o, x, f) 0 elseif h == 0 || !lt(o, x, l) length(a) else if o isa ForwardOrdering fld(floor(Integer, x) - f, h) + 1 else fld(ceil(Integer, x) - f, h) + 1 end end end function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering)::keytype(a) require_one_based_indexing(a) f, h, l = first(a), step(a), last(a) if !lt(o, f, x) 1 elseif h == 0 || lt(o, l, x) length(a) + 1 else if o isa ForwardOrdering cld(ceil(Integer, x) - f, h) + 1 else cld(floor(Integer, x) - f, h) + 1 end end end searchsorted(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) = searchsortedfirst(a, x, o) : searchsortedlast(a, x, o) for s in [:searchsortedfirst, :searchsortedlast, :searchsorted] @eval begin $s(v::AbstractVector, x, o::Ordering) = $s(v,x,firstindex(v),lastindex(v),o) $s(v::AbstractVector, x; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = $s(v,x,ord(lt,by,rev,order)) end end """ searchsorted(v, x; by=identity, lt=isless, rev=false) Return the range of indices in `v` where values are equivalent to `x`, or an empty range located at the insertion point if `v` does not contain values equivalent to `x`. The vector `v` must be sorted according to the order defined by the keywords. Refer to [`sort!`](@ref) for the meaning of the keywords and the definition of equivalence. Note that the `by` function is applied to the searched value `x` as well as the values in `v`. The range is generally found using binary search, but there are optimized implementations for some inputs. See also: [`searchsortedfirst`](@ref), [`sort!`](@ref), [`insorted`](@ref), [`findall`](@ref). # Examples ```jldoctest julia> searchsorted([1, 2, 4, 5, 5, 7], 4) # single match 3:3 julia> searchsorted([1, 2, 4, 5, 5, 7], 5) # multiple matches 4:5 julia> searchsorted([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle 3:2 julia> searchsorted([1, 2, 4, 5, 5, 7], 9) # no match, insert at end 7:6 julia> searchsorted([1, 2, 4, 5, 5, 7], 0) # no match, insert at start 1:0 julia> searchsorted([1=>"one", 2=>"two", 2=>"two", 4=>"four"], 2=>"two", by=first) # compare the keys of the pairs 2:3 ``` """ searchsorted """ searchsortedfirst(v, x; by=identity, lt=isless, rev=false) Return the index of the first value in `v` greater than or equivalent to `x`. If `x` is greater than all values in `v`, return `lastindex(v) + 1`. The vector `v` must be sorted according to the order defined by the keywords. `insert!`ing `x` at the returned index will maintain the sorted order. Refer to [`sort!`](@ref) for the meaning of the keywords and the definition of "greater than" and equivalence. Note that the `by` function is applied to the searched value `x` as well as the values in `v`. The index is generally found using binary search, but there are optimized implementations for some inputs. See also: [`searchsortedlast`](@ref), [`searchsorted`](@ref), [`findfirst`](@ref). # Examples ```jldoctest julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 4) # single match 3 julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 5) # multiple matches 4 julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle 3 julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 9) # no match, insert at end 7 julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 0) # no match, insert at start 1 julia> searchsortedfirst([1=>"one", 2=>"two", 4=>"four"], 3=>"three", by=first) # compare the keys of the pairs 3 ``` """ searchsortedfirst """ searchsortedlast(v, x; by=identity, lt=isless, rev=false) Return the index of the last value in `v` less than or equivalent to `x`. If `x` is less than all values in `v` the function returns `firstindex(v) - 1`. The vector `v` must be sorted according to the order defined by the keywords. Refer to [`sort!`](@ref) for the meaning of the keywords and the definition of "less than" and equivalence. Note that the `by` function is applied to the searched value `x` as well as the values in `v`. The index is generally found using binary search, but there are optimized implementations for some inputs # Examples ```jldoctest julia> searchsortedlast([1, 2, 4, 5, 5, 7], 4) # single match 3 julia> searchsortedlast([1, 2, 4, 5, 5, 7], 5) # multiple matches 5 julia> searchsortedlast([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle 2 julia> searchsortedlast([1, 2, 4, 5, 5, 7], 9) # no match, insert at end 6 julia> searchsortedlast([1, 2, 4, 5, 5, 7], 0) # no match, insert at start 0 julia> searchsortedlast([1=>"one", 2=>"two", 4=>"four"], 3=>"three", by=first) # compare the keys of the pairs 2 ``` """ searchsortedlast """ insorted(x, v; by=identity, lt=isless, rev=false) -> Bool Determine whether a vector `v` contains any value equivalent to `x`. The vector `v` must be sorted according to the order defined by the keywords. Refer to [`sort!`](@ref) for the meaning of the keywords and the definition of equivalence. Note that the `by` function is applied to the searched value `x` as well as the values in `v`. The check is generally done using binary search, but there are optimized implementations for some inputs. See also [`in`](@ref). # Examples ```jldoctest julia> insorted(4, [1, 2, 4, 5, 5, 7]) # single match true julia> insorted(5, [1, 2, 4, 5, 5, 7]) # multiple matches true julia> insorted(3, [1, 2, 4, 5, 5, 7]) # no match false julia> insorted(9, [1, 2, 4, 5, 5, 7]) # no match false julia> insorted(0, [1, 2, 4, 5, 5, 7]) # no match false julia> insorted(2=>"TWO", [1=>"one", 2=>"two", 4=>"four"], by=first) # compare the keys of the pairs true ``` !!! compat "Julia 1.6" `insorted` was added in Julia 1.6. """ function insorted end insorted(x, v::AbstractVector; kw...) = !isempty(searchsorted(v, x; kw...)) insorted(x, r::AbstractRange) = in(x, r) ## Alternative keyword management macro getkw(syms...) getters = (getproperty(Sort, Symbol(:_, sym)) for sym in syms) Expr(:block, (:($(esc(:((kw, $sym) = $getter(v, o, kw))))) for (sym, getter) in zip(syms, getters))...) end for (sym, exp, type) in [ (:lo, :(firstindex(v)), Integer), (:hi, :(lastindex(v)), Integer), (:mn, :(throw(ArgumentError("mn is needed but has not been computed"))), :(eltype(v))), (:mx, :(throw(ArgumentError("mx is needed but has not been computed"))), :(eltype(v))), (:scratch, nothing, :(Union{Nothing, Vector})), # could have different eltype (:legacy_dispatch_entry, nothing, Union{Nothing, Algorithm})] usym = Symbol(:_, sym) @eval function $usym(v, o, kw) # using missing instead of nothing because scratch could === nothing. res = get(kw, $(Expr(:quote, sym)), missing) res !== missing && return kw, res::$type $sym = $exp (;kw..., $sym), $sym::$type end end ## Scratch space management """ make_scratch(scratch::Union{Nothing, Vector}, T::Type, len::Integer) Returns `(s, t)` where `t` is an `AbstractVector` of type `T` with length at least `len` that is backed by the `Vector` `s`. If `scratch !== nothing`, then `s === scratch`. This function will allocate a new vector if `scratch === nothing`, `resize!` `scratch` if it is too short, and `reinterpret` `scratch` if its eltype is not `T`. """ function make_scratch(scratch::Nothing, T::Type, len::Integer) s = Vector{T}(undef, len) s, s end function make_scratch(scratch::Vector{T}, ::Type{T}, len::Integer) where T len > length(scratch) && resize!(scratch, len) scratch, scratch end function make_scratch(scratch::Vector, T::Type, len::Integer) len_bytes = len * sizeof(T) len_scratch = div(len_bytes, sizeof(eltype(scratch))) len_scratch > length(scratch) && resize!(scratch, len_scratch) scratch, reinterpret(T, scratch) end ## sorting algorithm components ## """ _sort!(v::AbstractVector, a::Algorithm, o::Ordering, kw; t, offset) An internal function that sorts `v` using the algorithm `a` under the ordering `o`, subject to specifications provided in `kw` (such as `lo` and `hi` in which case it only sorts `view(v, lo:hi)`) Returns a scratch space if provided or constructed during the sort, or `nothing` if no scratch space is present. !!! note `_sort!` modifies but does not return `v`. A returned scratch space will be a `Vector{T}` where `T` is usually the eltype of `v`. There are some exceptions, for example if `eltype(v) == Union{Missing, T}` then the scratch space may be be a `Vector{T}` due to `MissingOptimization` changing the eltype of `v` to `T`. `t` is an appropriate scratch space for the algorithm at hand, to be accessed as `t[i + offset]`. `t` is used for an algorithm to pass a scratch space back to itself in internal or recursive calls. """ function _sort! end """ MissingOptimization(next) <: Algorithm Filter out missing values. Missing values are placed after other values according to `DirectOrdering`s. This pass puts them there and passes on a view into the original vector that excludes the missing values. This pass is triggered for both `sort([1, missing, 3])` and `sortperm([1, missing, 3])`. """ struct MissingOptimization{T <: Algorithm} <: Algorithm next::T end struct WithoutMissingVector{T, U} <: AbstractVector{T} data::U function WithoutMissingVector(data; unsafe=false) if !unsafe && any(ismissing, data) throw(ArgumentError("data must not contain missing values")) end new{nonmissingtype(eltype(data)), typeof(data)}(data) end end Base.@propagate_inbounds function Base.getindex(v::WithoutMissingVector, i::Integer) out = v.data[i] @assert !(out isa Missing) out::eltype(v) end Base.@propagate_inbounds function Base.setindex!(v::WithoutMissingVector, x, i::Integer) v.data[i] = x v end Base.size(v::WithoutMissingVector) = size(v.data) Base.axes(v::WithoutMissingVector) = axes(v.data) """ send_to_end!(f::Function, v::AbstractVector; [lo, hi]) Send every element of `v` for which `f` returns `true` to the end of the vector and return the index of the last element for which `f` returns `false`. `send_to_end!(f, v, lo, hi)` is equivalent to `send_to_end!(f, view(v, lo:hi))+lo-1` Preserves the order of the elements that are not sent to the end. """ function send_to_end!(f::F, v::AbstractVector; lo=firstindex(v), hi=lastindex(v)) where F <: Function i = lo @inbounds while i <= hi && !f(v[i]) i += 1 end j = i + 1 @inbounds while j <= hi if !f(v[j]) v[i], v[j] = v[j], v[i] i += 1 end j += 1 end i - 1 end """ send_to_end!(f::Function, v::AbstractVector, o::DirectOrdering[, end_stable]; lo, hi) Return `(a, b)` where `v[a:b]` are the elements that are not sent to the end. If `o isa ReverseOrdering` then the "end" of `v` is `v[lo]`. If `end_stable` is set, the elements that are sent to the end are stable instead of the elements that are not """ @inline send_to_end!(f::F, v::AbstractVector, ::ForwardOrdering, end_stable=false; lo, hi) where F <: Function = end_stable ? (lo, hi-send_to_end!(!f, view(v, hi:-1:lo))) : (lo, send_to_end!(f, v; lo, hi)) @inline send_to_end!(f::F, v::AbstractVector, ::ReverseOrdering, end_stable=false; lo, hi) where F <: Function = end_stable ? (send_to_end!(!f, v; lo, hi)+1, hi) : (hi-send_to_end!(f, view(v, hi:-1:lo))+1, hi) function _sort!(v::AbstractVector, a::MissingOptimization, o::Ordering, kw) @getkw lo hi if o isa DirectOrdering && eltype(v) >: Missing && nonmissingtype(eltype(v)) != eltype(v) lo, hi = send_to_end!(ismissing, v, o; lo, hi) _sort!(WithoutMissingVector(v, unsafe=true), a.next, o, (;kw..., lo, hi)) elseif o isa Perm && o.order isa DirectOrdering && eltype(v) <: Integer && eltype(o.data) >: Missing && nonmissingtype(eltype(o.data)) != eltype(o.data) && all(i === j for (i,j) in zip(v, eachindex(o.data))) # TODO make this branch known at compile time # This uses a custom function because we need to ensure stability of both sides and # we can assume v is equal to eachindex(o.data) which allows a copying partition # without allocations. lo_i, hi_i = lo, hi cv = eachindex(o.data) # equal to copy(v) for i in lo:hi x = o.data[cv[i]] if ismissing(x) == (o.order == Reverse) # should x go at the beginning/end? v[lo_i] = i lo_i += 1 else v[hi_i] = i hi_i -= 1 end end reverse!(v, lo_i, hi) if o.order == Reverse lo = lo_i else hi = hi_i end _sort!(v, a.next, Perm(o.order, WithoutMissingVector(o.data, unsafe=true)), (;kw..., lo, hi)) else _sort!(v, a.next, o, kw) end end """ IEEEFloatOptimization(next) <: Algorithm Move NaN values to the end, partition by sign, and reinterpret the rest as unsigned integers. IEEE floating point numbers (`Float64`, `Float32`, and `Float16`) compare the same as unsigned integers with the bits with a few exceptions. This pass This pass is triggered for both `sort([1.0, NaN, 3.0])` and `sortperm([1.0, NaN, 3.0])`. """ struct IEEEFloatOptimization{T <: Algorithm} <: Algorithm next::T end after_zero(::ForwardOrdering, x) = !signbit(x) after_zero(::ReverseOrdering, x) = signbit(x) is_concrete_IEEEFloat(T::Type) = T <: Base.IEEEFloat && isconcretetype(T) function _sort!(v::AbstractVector, a::IEEEFloatOptimization, o::Ordering, kw) @getkw lo hi if is_concrete_IEEEFloat(eltype(v)) && o isa DirectOrdering lo, hi = send_to_end!(isnan, v, o, true; lo, hi) iv = reinterpret(uinttype(eltype(v)), v) j = send_to_end!(x -> after_zero(o, x), v; lo, hi) scratch = _sort!(iv, a.next, Reverse, (;kw..., lo, hi=j)) if scratch === nothing # Union split _sort!(iv, a.next, Forward, (;kw..., lo=j+1, hi, scratch)) else _sort!(iv, a.next, Forward, (;kw..., lo=j+1, hi, scratch)) end elseif eltype(v) <: Integer && o isa Perm && o.order isa DirectOrdering && is_concrete_IEEEFloat(eltype(o.data)) lo, hi = send_to_end!(i -> isnan(@inbounds o.data[i]), v, o.order, true; lo, hi) ip = reinterpret(uinttype(eltype(o.data)), o.data) j = send_to_end!(i -> after_zero(o.order, @inbounds o.data[i]), v; lo, hi) scratch = _sort!(v, a.next, Perm(Reverse, ip), (;kw..., lo, hi=j)) if scratch === nothing # Union split _sort!(v, a.next, Perm(Forward, ip), (;kw..., lo=j+1, hi, scratch)) else _sort!(v, a.next, Perm(Forward, ip), (;kw..., lo=j+1, hi, scratch)) end else _sort!(v, a.next, o, kw) end end """ BoolOptimization(next) <: Algorithm Sort `AbstractVector{Bool}`s using a specialized version of counting sort. Accesses each element at most twice (one read and one write), and performs at most two comparisons. """ struct BoolOptimization{T <: Algorithm} <: Algorithm next::T end _sort!(v::AbstractVector, a::BoolOptimization, o::Ordering, kw) = _sort!(v, a.next, o, kw) function _sort!(v::AbstractVector{Bool}, ::BoolOptimization, o::Ordering, kw) first = lt(o, false, true) ? false : lt(o, true, false) ? true : return v @getkw lo hi scratch count = 0 @inbounds for i in lo:hi if v[i] == first count += 1 end end @inbounds v[lo:lo+count-1] .= first @inbounds v[lo+count:hi] .= !first scratch end """ IsUIntMappable(yes, no) <: Algorithm Determines if the elements of a vector can be mapped to unsigned integers while preserving their order under the specified ordering. If they can be, dispatch to the `yes` algorithm and record the unsigned integer type that the elements may be mapped to. Otherwise dispatch to the `no` algorithm. """ struct IsUIntMappable{T <: Algorithm, U <: Algorithm} <: Algorithm yes::T no::U end function _sort!(v::AbstractVector, a::IsUIntMappable, o::Ordering, kw) if UIntMappable(eltype(v), o) !== nothing _sort!(v, a.yes, o, kw) else _sort!(v, a.no, o, kw) end end """ Small{N}(small=SMALL_ALGORITHM, big) <: Algorithm Sort inputs with `length(lo:hi) <= N` using the `small` algorithm. Otherwise use the `big` algorithm. """ struct Small{N, T <: Algorithm, U <: Algorithm} <: Algorithm small::T big::U end Small{N}(small, big) where N = Small{N, typeof(small), typeof(big)}(small, big) Small{N}(big) where N = Small{N}(SMALL_ALGORITHM, big) function _sort!(v::AbstractVector, a::Small{N}, o::Ordering, kw) where N @getkw lo hi if (hi-lo) < N _sort!(v, a.small, o, kw) else _sort!(v, a.big, o, kw) end end struct InsertionSortAlg <: Algorithm end """ InsertionSort Use the insertion sort algorithm. Insertion sort traverses the collection one element at a time, inserting each element into its correct, sorted position in the output vector. Characteristics: * *stable*: preserves the ordering of elements that compare equal (e.g. "a" and "A" in a sort of letters that ignores case). * *in-place* in memory. * *quadratic performance* in the number of elements to be sorted: it is well-suited to small collections but should not be used for large ones. """ const InsertionSort = InsertionSortAlg() const SMALL_ALGORITHM = InsertionSortAlg() function _sort!(v::AbstractVector, ::InsertionSortAlg, o::Ordering, kw) @getkw lo hi scratch lo_plus_1 = (lo + 1)::Integer @inbounds for i = lo_plus_1:hi j = i x = v[i] while j > lo y = v[j-1] if !(lt(o, x, y)::Bool) break end v[j] = y j -= 1 end v[j] = x end scratch end """ CheckSorted(next) <: Algorithm Check if the input is already sorted and for large inputs, also check if it is reverse-sorted. The reverse-sorted check is unstable. """ struct CheckSorted{T <: Algorithm} <: Algorithm next::T end function _sort!(v::AbstractVector, a::CheckSorted, o::Ordering, kw) @getkw lo hi scratch # For most arrays, a presorted check is cheap (overhead < 5%) and for most large # arrays it is essentially free (<1%). _issorted(v, lo, hi, o) && return scratch # For most large arrays, a reverse-sorted check is essentially free (overhead < 1%) if hi-lo >= 500 && _issorted(v, lo, hi, ReverseOrdering(o)) # If reversing is valid, do so. This violates stability. reverse!(v, lo, hi) return scratch end _sort!(v, a.next, o, kw) end """ ComputeExtrema(next) <: Algorithm Compute the extrema of the input under the provided order. If the minimum is no less than the maximum, then the input is already sorted. Otherwise, dispatch to the `next` algorithm. """ struct ComputeExtrema{T <: Algorithm} <: Algorithm next::T end function _sort!(v::AbstractVector, a::ComputeExtrema, o::Ordering, kw) @getkw lo hi scratch mn = mx = v[lo] @inbounds for i in (lo+1):hi vi = v[i] lt(o, vi, mn) && (mn = vi) lt(o, mx, vi) && (mx = vi) end lt(o, mn, mx) || return scratch # all same _sort!(v, a.next, o, (;kw..., mn, mx)) end """ ConsiderCountingSort(counting=CountingSort(), next) <: Algorithm If the input's range is small enough, use the `counting` algorithm. Otherwise, dispatch to the `next` algorithm. For most types, the threshold is if the range is shorter than half the length, but for types larger than Int64, bitshifts are expensive and RadixSort is not viable, so the threshold is much more generous. """ struct ConsiderCountingSort{T <: Algorithm, U <: Algorithm} <: Algorithm counting::T next::U end ConsiderCountingSort(next) = ConsiderCountingSort(CountingSort(), next) function _sort!(v::AbstractVector{<:Integer}, a::ConsiderCountingSort, o::DirectOrdering, kw) @getkw lo hi mn mx range = maybe_unsigned(o === Reverse ? mn-mx : mx-mn) if range < (sizeof(eltype(v)) > 8 ? 5(hi-lo)-100 : div(hi-lo, 2)) _sort!(v, a.counting, o, kw) else _sort!(v, a.next, o, kw) end end _sort!(v::AbstractVector, a::ConsiderCountingSort, o::Ordering, kw) = _sort!(v, a.next, o, kw) """ CountingSort <: Algorithm Use the counting sort algorithm. `CountingSort` is an algorithm for sorting integers that runs in ฮ˜(length + range) time and space. It counts the number of occurrences of each value in the input and then iterates through those counts repopulating the input with the values in sorted order. """ struct CountingSort <: Algorithm end maybe_reverse(o::ForwardOrdering, x) = x maybe_reverse(o::ReverseOrdering, x) = reverse(x) function _sort!(v::AbstractVector{<:Integer}, ::CountingSort, o::DirectOrdering, kw) @getkw lo hi mn mx scratch range = maybe_unsigned(o === Reverse ? mn-mx : mx-mn) offs = 1 - (o === Reverse ? mx : mn) counts = fill(0, range+1) # TODO use scratch (but be aware of type stability) @inbounds for i = lo:hi counts[v[i] + offs] += 1 end idx = lo @inbounds for i = maybe_reverse(o, 1:range+1) lastidx = idx + counts[i] - 1 val = i-offs for j = idx:lastidx v[j] = val isa Unsigned && eltype(v) <: Signed ? signed(val) : val end idx = lastidx + 1 end scratch end """ ConsiderRadixSort(radix=RadixSort(), next) <: Algorithm If the number of bits in the input's range is small enough and the input supports efficient bitshifts, use the `radix` algorithm. Otherwise, dispatch to the `next` algorithm. """ struct ConsiderRadixSort{T <: Algorithm, U <: Algorithm} <: Algorithm radix::T next::U end ConsiderRadixSort(next) = ConsiderRadixSort(RadixSort(), next) function _sort!(v::AbstractVector, a::ConsiderRadixSort, o::DirectOrdering, kw) @getkw lo hi mn mx urange = uint_map(mx, o)-uint_map(mn, o) bits = unsigned(top_set_bit(urange)) if sizeof(eltype(v)) <= 8 && bits+70 < 22log(hi-lo) _sort!(v, a.radix, o, kw) else _sort!(v, a.next, o, kw) end end """ RadixSort <: Algorithm Use the radix sort algorithm. `RadixSort` is a stable least significant bit first radix sort algorithm that runs in `O(length * log(range))` time and linear space. It first sorts the entire vector by the last `chunk_size` bits, then by the second to last `chunk_size` bits, and so on. Stability means that it will not reorder two elements that compare equal. This is essential so that the order introduced by earlier, less significant passes is preserved by later passes. Each pass divides the input into `2^chunk_size == mask+1` buckets. To do this, it * counts the number of entries that fall into each bucket * uses those counts to compute the indices to move elements of those buckets into * moves elements into the computed indices in the swap array * switches the swap and working array `chunk_size` is larger for larger inputs and determined by an empirical heuristic. """ struct RadixSort <: Algorithm end function _sort!(v::AbstractVector, a::RadixSort, o::DirectOrdering, kw) @getkw lo hi mn mx scratch umn = uint_map(mn, o) urange = uint_map(mx, o)-umn bits = unsigned(top_set_bit(urange)) # At this point, we are committed to radix sort. u = uint_map!(v, lo, hi, o) # we subtract umn to avoid radixing over unnecessary bits. For example, # Int32[3, -1, 2] uint_maps to UInt32[0x80000003, 0x7fffffff, 0x80000002] # which uses all 32 bits, but once we subtract umn = 0x7fffffff, we are left with # UInt32[0x00000004, 0x00000000, 0x00000003] which uses only 3 bits, and # Float32[2.012, 400.0, 12.345] uint_maps to UInt32[0x3fff3b63, 0x3c37ffff, 0x414570a4] # which is reduced to UInt32[0x03c73b64, 0x00000000, 0x050d70a5] using only 26 bits. # the overhead for this subtraction is small enough that it is worthwhile in many cases. # this is faster than u[lo:hi] .-= umn as of v1.9.0-DEV.100 @inbounds for i in lo:hi u[i] -= umn end scratch, t = make_scratch(scratch, eltype(v), hi-lo+1) tu = reinterpret(eltype(u), t) if radix_sort!(u, lo, hi, bits, tu, 1-lo) uint_unmap!(v, u, lo, hi, o, umn) else uint_unmap!(v, tu, lo, hi, o, umn, 1-lo) end scratch end """ ScratchQuickSort(next::Algorithm=SMALL_ALGORITHM) <: Algorithm ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}=lo, next::Algorithm=SMALL_ALGORITHM) <: Algorithm Use the `ScratchQuickSort` algorithm with the `next` algorithm as a base case. `ScratchQuickSort` is like `QuickSort`, but utilizes scratch space to operate faster and allow for the possibility of maintaining stability. If `lo` and `hi` are provided, finds and sorts the elements in the range `lo:hi`, reordering but not necessarily sorting other elements in the process. If `lo` or `hi` is `missing`, it is treated as the first or last index of the input, respectively. `lo` and `hi` may be specified together as an `AbstractUnitRange`. Characteristics: * *stable*: preserves the ordering of elements that compare equal (e.g. "a" and "A" in a sort of letters that ignores case). * *not in-place* in memory. * *divide-and-conquer*: sort strategy similar to [`QuickSort`](@ref). * *linear runtime* if `length(lo:hi)` is constant * *quadratic worst case runtime* in pathological cases (vanishingly rare for non-malicious input) """ struct ScratchQuickSort{L<:Union{Integer,Missing}, H<:Union{Integer,Missing}, T<:Algorithm} <: Algorithm lo::L hi::H next::T end ScratchQuickSort(next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(missing, missing, next) ScratchQuickSort(lo::Union{Integer, Missing}, hi::Union{Integer, Missing}) = ScratchQuickSort(lo, hi, SMALL_ALGORITHM) ScratchQuickSort(lo::Union{Integer, Missing}, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(lo, lo, next) ScratchQuickSort(r::OrdinalRange, next::Algorithm=SMALL_ALGORITHM) = ScratchQuickSort(first(r), last(r), next) # select a pivot, partition v[lo:hi] according # to the pivot, and store the result in t[lo:hi]. # # sets `pivot_dest[pivot_index+pivot_index_offset] = pivot` and returns that index. function partition!(t::AbstractVector, lo::Integer, hi::Integer, offset::Integer, o::Ordering, v::AbstractVector, rev::Bool, pivot_dest::AbstractVector, pivot_index_offset::Integer) # Ideally we would use `pivot_index = rand(lo:hi)`, but that requires Random.jl # and would mutate the global RNG in sorting. pivot_index = typeof(hi-lo)(hash(lo) % (hi-lo+1)) + lo @inbounds begin pivot = v[pivot_index] while lo < pivot_index x = v[lo] fx = rev ? !lt(o, x, pivot) : lt(o, pivot, x) t[(fx ? hi : lo) - offset] = x offset += fx lo += 1 end while lo < hi x = v[lo+1] fx = rev ? lt(o, pivot, x) : !lt(o, x, pivot) t[(fx ? hi : lo) - offset] = x offset += fx lo += 1 end pivot_index = lo-offset + pivot_index_offset pivot_dest[pivot_index] = pivot end # t_pivot_index = lo-offset (i.e. without pivot_index_offset) # t[t_pivot_index] is whatever it was before unless t is the pivot_dest # t[t_pivot_index] >* pivot, reverse stable pivot_index end function _sort!(v::AbstractVector, a::ScratchQuickSort, o::Ordering, kw; t=nothing, offset=nothing, swap=false, rev=false) @getkw lo hi scratch if t === nothing scratch, t = make_scratch(scratch, eltype(v), hi-lo+1) offset = 1-lo kw = (;kw..., scratch) end while lo < hi && hi - lo > SMALL_THRESHOLD j = if swap partition!(v, lo+offset, hi+offset, offset, o, t, rev, v, 0) else partition!(t, lo, hi, -offset, o, v, rev, v, -offset) end swap = !swap # For ScratchQuickSort(), a.lo === a.hi === missing, so the first two branches get skipped if !ismissing(a.lo) && j <= a.lo # Skip sorting the lower part swap && copyto!(v, lo, t, lo+offset, j-lo) rev && reverse!(v, lo, j-1) lo = j+1 rev = !rev elseif !ismissing(a.hi) && a.hi <= j # Skip sorting the upper part swap && copyto!(v, j+1, t, j+1+offset, hi-j) rev || reverse!(v, j+1, hi) hi = j-1 elseif j-lo < hi-j # Sort the lower part recursively because it is smaller. Recursing on the # smaller part guarantees O(log(n)) stack space even on pathological inputs. _sort!(v, a, o, (;kw..., lo, hi=j-1); t, offset, swap, rev) lo = j+1 rev = !rev else # Sort the higher part recursively _sort!(v, a, o, (;kw..., lo=j+1, hi); t, offset, swap, rev=!rev) hi = j-1 end end hi < lo && return scratch swap && copyto!(v, lo, t, lo+offset, hi-lo+1) rev && reverse!(v, lo, hi) _sort!(v, a.next, o, (;kw..., lo, hi)) end """ StableCheckSorted(next) <: Algorithm Check if an input is sorted and/or reverse-sorted. The definition of reverse-sorted is that for every pair of adjacent elements, the latter is less than the former. This is stricter than `issorted(v, Reverse(o))` to avoid swapping pairs of elements that compare equal. """ struct StableCheckSorted{T<:Algorithm} <: Algorithm next::T end function _sort!(v::AbstractVector, a::StableCheckSorted, o::Ordering, kw) @getkw lo hi scratch if _issorted(v, lo, hi, o) return scratch elseif _issorted(v, lo, hi, Lt((x, y) -> !lt(o, x, y))) # Reverse only if necessary. Using issorted(..., Reverse(o)) would violate stability. reverse!(v, lo, hi) return scratch end _sort!(v, a.next, o, kw) end # The return value indicates whether v is sorted (true) or t is sorted (false) # This is one of the many reasons radix_sort! is not exported. function radix_sort!(v::AbstractVector{U}, lo::Integer, hi::Integer, bits::Unsigned, t::AbstractVector{U}, offset::Integer, chunk_size=radix_chunk_size_heuristic(lo, hi, bits)) where U <: Unsigned # bits is unsigned for performance reasons. counts = Vector{Int}(undef, 1 << chunk_size + 1) # TODO use scratch for this shift = 0 while true @noinline radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size) # the latest data resides in t shift += chunk_size shift < bits || return false @noinline radix_sort_pass!(v, lo+offset, hi+offset, -offset, counts, t, shift, chunk_size) # the latest data resides in v shift += chunk_size shift < bits || return true end end function radix_sort_pass!(t, lo, hi, offset, counts, v, shift, chunk_size) mask = UInt(1) << chunk_size - 1 # mask is defined in pass so that the compiler @inbounds begin # โ†ณ knows it's shape # counts[2:mask+2] will store the number of elements that fall into each bucket. # if chunk_size = 8, counts[2] is bucket 0x00 and counts[257] is bucket 0xff. counts .= 0 for k in lo:hi x = v[k] # lookup the element i = (x >> shift)&mask + 2 # compute its bucket's index for this pass counts[i] += 1 # increment that bucket's count end counts[1] = lo + offset # set target index for the first bucket cumsum!(counts, counts) # set target indices for subsequent buckets # counts[1:mask+1] now stores indices where the first member of each bucket # belongs, not the number of elements in each bucket. We will put the first element # of bucket 0x00 in t[counts[1]], the next element of bucket 0x00 in t[counts[1]+1], # and the last element of bucket 0x00 in t[counts[2]-1]. for k in lo:hi x = v[k] # lookup the element i = (x >> shift)&mask + 1 # compute its bucket's index for this pass j = counts[i] # lookup the target index t[j] = x # put the element where it belongs counts[i] = j + 1 # increment the target index for the next end # โ†ณ element in this bucket end end function radix_chunk_size_heuristic(lo::Integer, hi::Integer, bits::Unsigned) # chunk_size is the number of bits to radix over at once. # We need to allocate an array of size 2^chunk size, and on the other hand the higher # the chunk size the fewer passes we need. Theoretically, chunk size should be based on # the Lambert W function applied to length. Empirically, we use this heuristic: guess = min(10, log(maybe_unsigned(hi-lo))*3/4+3) # TODO the maximum chunk size should be based on architecture cache size. # We need iterations * chunk size โ‰ฅ bits, and these cld's # make an effort to get iterations * chunk size โ‰ˆ bits UInt8(cld(bits, cld(bits, guess))) end maybe_unsigned(x::Integer) = x # this is necessary to avoid calling unsigned on BigInt maybe_unsigned(x::BitSigned) = unsigned(x) function _issorted(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) @boundscheck checkbounds(v, lo:hi) @inbounds for i in (lo+1):hi lt(o, v[i], v[i-1]) && return false end true end ## default sorting policy ## """ InitialOptimizations(next) <: Algorithm Attempt to apply a suite of low-cost optimizations to the input vector before sorting. These optimizations may be automatically applied by the `sort!` family of functions when `alg=InsertionSort`, `alg=MergeSort`, or `alg=QuickSort` is passed as an argument. `InitialOptimizations` is an implementation detail and subject to change or removal in future versions of Julia. If `next` is stable, then `InitialOptimizations(next)` is also stable. The specific optimizations attempted by `InitialOptimizations` are [`MissingOptimization`](@ref), [`BoolOptimization`](@ref), dispatch to [`InsertionSort`](@ref) for inputs with `length <= 10`, and [`IEEEFloatOptimization`](@ref). """ InitialOptimizations(next) = MissingOptimization( BoolOptimization( Small{10}( IEEEFloatOptimization( next)))) """ DEFAULT_STABLE The default sorting algorithm. This algorithm is guaranteed to be stable (i.e. it will not reorder elements that compare equal). It makes an effort to be fast for most inputs. The algorithms used by `DEFAULT_STABLE` are an implementation detail. See extended help for the current dispatch system. # Extended Help `DEFAULT_STABLE` is composed of two parts: the [`InitialOptimizations`](@ref) and a hybrid of Radix, Insertion, Counting, Quick sorts. We begin with MissingOptimization because it has no runtime cost when it is not triggered and can enable other optimizations to be applied later. For example, BoolOptimization cannot apply to an `AbstractVector{Union{Missing, Bool}}`, but after [`MissingOptimization`](@ref) is applied, that input will be converted into am `AbstractVector{Bool}`. We next apply [`BoolOptimization`](@ref) because it also has no runtime cost when it is not triggered and when it is triggered, it is an incredibly efficient algorithm (sorting `Bool`s is quite easy). Next, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 10`. This dispatch occurs before the [`IEEEFloatOptimization`](@ref) pass because the [`IEEEFloatOptimization`](@ref)s are not beneficial for very small inputs. To conclude the [`InitialOptimizations`](@ref), we apply [`IEEEFloatOptimization`](@ref). After these optimizations, we branch on whether radix sort and related algorithms can be applied to the input vector and ordering. We conduct this branch by testing if `UIntMappable(v, order) !== nothing`. That is, we see if we know of a reversible mapping from `eltype(v)` to `UInt` that preserves the ordering `order`. We perform this check after the initial optimizations because they can change the input vector's type and ordering to make them `UIntMappable`. If the input is not [`UIntMappable`](@ref), then we perform a presorted check and dispatch to [`ScratchQuickSort`](@ref). Otherwise, we dispatch to [`InsertionSort`](@ref) for inputs with `length <= 40` and then perform a presorted check ([`CheckSorted`](@ref)). We check for short inputs before performing the presorted check to avoid the overhead of the check for small inputs. Because the alternate dispatch is to [`InsertionSort`](@ref) which has efficient `O(n)` runtime on presorted inputs, the check is not necessary for small inputs. We check if the input is reverse-sorted for long vectors (more than 500 elements) because the check is essentially free unless the input is almost entirely reverse sorted. Note that once the input is determined to be [`UIntMappable`](@ref), we know the order forms a [total order](wikipedia.org/wiki/Total_order) over the inputs and so it is impossible to perform an unstable sort because no two elements can compare equal unless they _are_ equal, in which case switching them is undetectable. We utilize this fact to perform a more aggressive reverse sorted check that will reverse the vector `[3, 2, 2, 1]`. After these potential fast-paths are tried and failed, we [`ComputeExtrema`](@ref) of the input. This computation has a fairly fast `O(n)` runtime, but we still try to delay it until it is necessary. Next, we [`ConsiderCountingSort`](@ref). If the range the input is small compared to its length, we apply [`CountingSort`](@ref). Next, we [`ConsiderRadixSort`](@ref). This is similar to the dispatch to counting sort, but we conside rthe number of _bits_ in the range, rather than the range itself. Consequently, we apply [`RadixSort`](@ref) for any reasonably long inputs that reach this stage. Finally, if the input has length less than 80, we dispatch to [`InsertionSort`](@ref) and otherwise we dispatch to [`ScratchQuickSort`](@ref). """ const DEFAULT_STABLE = InitialOptimizations( IsUIntMappable( Small{40}( CheckSorted( ComputeExtrema( ConsiderCountingSort( ConsiderRadixSort( Small{80}( ScratchQuickSort())))))), StableCheckSorted( ScratchQuickSort()))) """ DEFAULT_UNSTABLE An efficient sorting algorithm. The algorithms used by `DEFAULT_UNSTABLE` are an implementation detail. They are currently the same as those used by [`DEFAULT_STABLE`](@ref), but this is subject to change in future. """ const DEFAULT_UNSTABLE = DEFAULT_STABLE const SMALL_THRESHOLD = 20 function Base.show(io::IO, alg::Algorithm) print_tree(io, alg, 0) end function print_tree(io::IO, alg::Algorithm, cols::Int) print(io, " "^cols) show_type(io, alg) print(io, '(') for (i, name) in enumerate(fieldnames(typeof(alg))) arg = getproperty(alg, name) i > 1 && print(io, ',') if arg isa Algorithm println(io) print_tree(io, arg, cols+1) else i > 1 && print(io, ' ') print(io, arg) end end print(io, ')') end show_type(io::IO, alg::Algorithm) = Base.show_type_name(io, typeof(alg).name) show_type(io::IO, alg::Small{N}) where N = print(io, "Base.Sort.Small{$N}") defalg(v::AbstractArray) = DEFAULT_STABLE defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE defalg(v::AbstractArray{Missing}) = DEFAULT_UNSTABLE # for method disambiguation defalg(v::AbstractArray{Union{}}) = DEFAULT_UNSTABLE # for method disambiguation """ sort!(v; alg::Algorithm=defalg(v), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Sort the vector `v` in place. A stable algorithm is used by default: the ordering of elements that compare equal is preserved. A specific algorithm can be selected via the `alg` keyword (see [Sorting Algorithms](@ref) for available algorithms). Elements are first transformed with the function `by` and then compared according to either the function `lt` or the ordering `order`. Finally, the resulting order is reversed if `rev=true` (this preserves forward stability: elements that compare equal are not reversed). The current implemention applies the `by` transformation before each comparison rather than once per element. Passing an `lt` other than `isless` along with an `order` other than [`Base.Order.Forward`](@ref) or [`Base.Order.Reverse`](@ref) is not permitted, otherwise all options are independent and can be used together in all possible combinations. Note that `order` can also include a "by" transformation, in which case it is applied after that defined with the `by` keyword. For more information on `order` values see the documentation on [Alternate Orderings](@ref). Relations between two elements are defined as follows (with "less" and "greater" exchanged when `rev=true`): * `x` is less than `y` if `lt(by(x), by(y))` (or `Base.Order.lt(order, by(x), by(y))`) yields true. * `x` is greater than `y` if `y` is less than `x`. * `x` and `y` are equivalent if neither is less than the other ("incomparable" is sometimes used as a synonym for "equivalent"). The result of `sort!` is sorted in the sense that every element is greater than or equivalent to the previous one. The `lt` function must define a strict weak order, that is, it must be * irreflexive: `lt(x, x)` always yields `false`, * asymmetric: if `lt(x, y)` yields `true` then `lt(y, x)` yields `false`, * transitive: `lt(x, y) && lt(y, z)` implies `lt(x, z)`, * transitive in equivalence: `!lt(x, y) && !lt(y, x)` and `!lt(y, z) && !lt(z, y)` together imply `!lt(x, z) && !lt(z, x)`. In words: if `x` and `y` are equivalent and `y` and `z` are equivalent then `x` and `z` must be equivalent. For example `<` is a valid `lt` function for `Int` values but `โ‰ค` is not: it violates irreflexivity. For `Float64` values even `<` is invalid as it violates the fourth condition: `1.0` and `NaN` are equivalent and so are `NaN` and `2.0` but `1.0` and `2.0` are not equivalent. See also [`sort`](@ref), [`sortperm`](@ref), [`sortslices`](@ref), [`partialsort!`](@ref), [`partialsortperm`](@ref), [`issorted`](@ref), [`searchsorted`](@ref), [`insorted`](@ref), [`Base.Order.ord`](@ref). # Examples ```jldoctest julia> v = [3, 1, 2]; sort!(v); v 3-element Vector{Int64}: 1 2 3 julia> v = [3, 1, 2]; sort!(v, rev = true); v 3-element Vector{Int64}: 3 2 1 julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[1]); v 3-element Vector{Tuple{Int64, String}}: (1, "c") (2, "b") (3, "a") julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[2]); v 3-element Vector{Tuple{Int64, String}}: (3, "a") (2, "b") (1, "c") julia> sort(0:3, by=x->x-2, order=Base.Order.By(abs)) # same as sort(0:3, by=abs(x->x-2)) 4-element Vector{Int64}: 2 1 3 0 julia> sort([2, NaN, 1, NaN, 3]) # correct sort with default lt=isless 5-element Vector{Float64}: 1.0 2.0 3.0 NaN NaN julia> sort([2, NaN, 1, NaN, 3], lt=<) # wrong sort due to invalid lt. This behavior is undefined. 5-element Vector{Float64}: 2.0 NaN 1.0 NaN 3.0 ``` """ function sort!(v::AbstractVector{T}; alg::Algorithm=defalg(v), lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, scratch::Union{Vector{T}, Nothing}=nothing) where T _sort!(v, maybe_apply_initial_optimizations(alg), ord(lt,by,rev,order), (;scratch)) v end """ sort(v; alg::Algorithm=defalg(v), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified. # Examples ```jldoctest julia> v = [3, 1, 2]; julia> sort(v) 3-element Vector{Int64}: 1 2 3 julia> v 3-element Vector{Int64}: 3 1 2 ``` """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) ## partialsortperm: the permutation to sort the first k elements of an array ## """ partialsortperm(v, k; by=ientity, lt=isless, rev=false) Return a partial permutation `I` of the vector `v`, so that `v[I]` returns values of a fully sorted version of `v` at index `k`. If `k` is a range, a vector of indices is returned; if `k` is an integer, a single index is returned. The order is specified using the same keywords as `sort!`. The permutation is stable: the indices of equal elements will appear in ascending order. This function is equivalent to, but more efficient than, calling `sortperm(...)[k]`. # Examples ```jldoctest julia> v = [3, 1, 2, 1]; julia> v[partialsortperm(v, 1)] 1 julia> p = partialsortperm(v, 1:3) 3-element view(::Vector{Int64}, 1:3) with eltype Int64: 2 4 3 julia> v[p] 3-element Vector{Int64}: 1 1 2 ``` """ partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = partialsortperm!(similar(Vector{eltype(k)}, axes(v,1)), v, k; kwargs...) """ partialsortperm!(ix, v, k; by=identity, lt=isless, rev=false) Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix` the same size as `v`, which is used to store (a permutation of) the indices of `v`. `ix` is initialized to contain the indices of `v`. (Typically, the indices of `v` will be `1:length(v)`, although if `v` has an alternative array type with non-one-based indices, such as an `OffsetArray`, `ix` must share those same indices) Upon return, `ix` is guaranteed to have the indices `k` in their sorted positions, such that ```julia partialsortperm!(ix, v, k); v[ix[k]] == partialsort(v, k) ``` The return value is the `k`th element of `ix` if `k` is an integer, or view into `ix` if `k` is a range. $(Base._DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> v = [3, 1, 2, 1]; julia> ix = Vector{Int}(undef, 4); julia> partialsortperm!(ix, v, 1) 2 julia> ix = [1:4;]; julia> partialsortperm!(ix, v, 2:3) 2-element view(::Vector{Int64}, 2:3) with eltype Int64: 4 3 ``` """ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, k::Union{Integer, OrdinalRange}; lt::Function=isless, by::Function=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, initialized::Bool=false) if axes(ix,1) != axes(v,1) throw(ArgumentError("The index vector is used as scratch space and must have the " * "same length/indices as the source vector, $(axes(ix,1)) != $(axes(v,1))")) end @inbounds for i in eachindex(ix) ix[i] = i end # do partial quicksort _sort!(ix, InitialOptimizations(ScratchQuickSort(k)), Perm(ord(lt, by, rev, order), v), (;)) maybeview(ix, k) end ## sortperm: the permutation to sort an array ## """ sortperm(A; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward, [dims::Integer]) Return a permutation vector or array `I` that puts `A[I]` in sorted order along the given dimension. If `A` has more than one dimension, then the `dims` keyword argument must be specified. The order is specified using the same keywords as [`sort!`](@ref). The permutation is guaranteed to be stable even if the sorting algorithm is unstable: the indices of equal elements will appear in ascending order. See also [`sortperm!`](@ref), [`partialsortperm`](@ref), [`invperm`](@ref), [`indexin`](@ref). To sort slices of an array, refer to [`sortslices`](@ref). !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. # Examples ```jldoctest julia> v = [3, 1, 2]; julia> p = sortperm(v) 3-element Vector{Int64}: 2 3 1 julia> v[p] 3-element Vector{Int64}: 1 2 3 julia> A = [8 7; 5 6] 2ร—2 Matrix{Int64}: 8 7 5 6 julia> sortperm(A, dims = 1) 2ร—2 Matrix{Int64}: 2 4 1 3 julia> sortperm(A, dims = 2) 2ร—2 Matrix{Int64}: 3 1 2 4 ``` """ function sortperm(A::AbstractArray; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, scratch::Union{Vector{<:Integer}, Nothing}=nothing, dims...) #to optionally specify dims argument if rev === true _sortperm(A; alg, order=ord(lt, by, true, order), scratch, dims...) else _sortperm(A; alg, order=ord(lt, by, nothing, order), scratch, dims...) end end function _sortperm(A::AbstractArray; alg, order, scratch, dims...) if order === Forward && isa(A,Vector) && eltype(A)<:Integer n = length(A) if n > 1 min, max = extrema(A) (diff, o1) = sub_with_overflow(max, min) (rangelen, o2) = add_with_overflow(diff, oneunit(diff)) if !(o1 || o2)::Bool && rangelen < div(n,2) return sortperm_int_range(A, rangelen, min) end end end ix = copymutable(LinearIndices(A)) sort!(ix; alg, order = Perm(order, vec(A)), scratch, dims...) end """ sortperm!(ix, A; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward, [dims::Integer]) Like [`sortperm`](@ref), but accepts a preallocated index vector or array `ix` with the same `axes` as `A`. `ix` is initialized to contain the values `LinearIndices(A)`. $(Base._DOCS_ALIASING_WARNING) !!! compat "Julia 1.9" The method accepting `dims` requires at least Julia 1.9. # Examples ```jldoctest julia> v = [3, 1, 2]; p = zeros(Int, 3); julia> sortperm!(p, v); p 3-element Vector{Int64}: 2 3 1 julia> v[p] 3-element Vector{Int64}: 1 2 3 julia> A = [8 7; 5 6]; p = zeros(Int,2, 2); julia> sortperm!(p, A; dims=1); p 2ร—2 Matrix{Int64}: 2 4 1 3 julia> sortperm!(p, A; dims=2); p 2ร—2 Matrix{Int64}: 3 1 2 4 ``` """ @inline function sortperm!(ix::AbstractArray{T}, A::AbstractArray; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, initialized::Bool=false, scratch::Union{Vector{T}, Nothing}=nothing, dims...) where T <: Integer #to optionally specify dims argument (typeof(A) <: AbstractVector) == (:dims in keys(dims)) && throw(ArgumentError("Dims argument incorrect for type $(typeof(A))")) axes(ix) == axes(A) || throw(ArgumentError("index array must have the same size/axes as the source array, $(axes(ix)) != $(axes(A))")) ix .= LinearIndices(A) if rev === true sort!(ix; alg, order=Perm(ord(lt, by, true, order), vec(A)), scratch, dims...) else sort!(ix; alg, order=Perm(ord(lt, by, nothing, order), vec(A)), scratch, dims...) end end # sortperm for vectors of few unique integers function sortperm_int_range(x::Vector{<:Integer}, rangelen, minval) offs = 1 - minval n = length(x) counts = fill(0, rangelen+1) counts[1] = 1 @inbounds for i = 1:n counts[x[i] + offs + 1] += 1 end #cumsum!(counts, counts) @inbounds for i = 2:length(counts) counts[i] += counts[i-1] end P = Vector{Int}(undef, n) @inbounds for i = 1:n label = x[i] + offs P[counts[label]] = i counts[label] += 1 end return P end ## sorting multi-dimensional arrays ## """ sort(A; dims::Integer, alg::Algorithm=defalg(A), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Sort a multidimensional array `A` along the given dimension. See [`sort!`](@ref) for a description of possible keyword arguments. To sort slices of an array, refer to [`sortslices`](@ref). # Examples ```jldoctest julia> A = [4 3; 1 2] 2ร—2 Matrix{Int64}: 4 3 1 2 julia> sort(A, dims = 1) 2ร—2 Matrix{Int64}: 1 2 4 3 julia> sort(A, dims = 2) 2ร—2 Matrix{Int64}: 3 4 1 2 ``` """ function sort(A::AbstractArray{T}; dims::Integer, alg::Algorithm=defalg(A), lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, scratch::Union{Vector{T}, Nothing}=nothing) where T dim = dims order = ord(lt,by,rev,order) n = length(axes(A, dim)) if dim != 1 pdims = (dim, setdiff(1:ndims(A), dim)...) # put the selected dimension first Ap = permutedims(A, pdims) Av = vec(Ap) sort_chunks!(Av, n, maybe_apply_initial_optimizations(alg), order, scratch) permutedims(Ap, invperm(pdims)) else Av = A[:] sort_chunks!(Av, n, maybe_apply_initial_optimizations(alg), order, scratch) reshape(Av, axes(A)) end end @noinline function sort_chunks!(Av, n, alg, order, scratch) inds = LinearIndices(Av) sort_chunks!(Av, n, alg, order, scratch, first(inds), last(inds)) end @noinline function sort_chunks!(Av, n, alg, order, scratch::Nothing, fst, lst) for lo = fst:n:lst s = _sort!(Av, alg, order, (; lo, hi=lo+n-1, scratch)) s !== nothing && return sort_chunks!(Av, n, alg, order, s, lo+n, lst) end Av end @noinline function sort_chunks!(Av, n, alg, order, scratch::AbstractVector, fst, lst) for lo = fst:n:lst _sort!(Av, alg, order, (; lo, hi=lo+n-1, scratch)) end Av end """ sort!(A; dims::Integer, alg::Algorithm=defalg(A), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Sort the multidimensional array `A` along dimension `dims`. See the one-dimensional version of [`sort!`](@ref) for a description of possible keyword arguments. To sort slices of an array, refer to [`sortslices`](@ref). !!! compat "Julia 1.1" This function requires at least Julia 1.1. # Examples ```jldoctest julia> A = [4 3; 1 2] 2ร—2 Matrix{Int64}: 4 3 1 2 julia> sort!(A, dims = 1); A 2ร—2 Matrix{Int64}: 1 2 4 3 julia> sort!(A, dims = 2); A 2ร—2 Matrix{Int64}: 1 2 3 4 ``` """ function sort!(A::AbstractArray{T}; dims::Integer, alg::Algorithm=defalg(A), lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward, # TODO stop eagerly over-allocating. scratch::Union{Vector{T}, Nothing}=Vector{T}(undef, size(A, dims))) where T __sort!(A, Val(dims), maybe_apply_initial_optimizations(alg), ord(lt, by, rev, order), scratch) end function __sort!(A::AbstractArray{T}, ::Val{K}, alg::Algorithm, order::Ordering, scratch::Union{Vector{T}, Nothing}) where {K,T} nd = ndims(A) 1 <= K <= nd || throw(ArgumentError("dimension out of range")) remdims = ntuple(i -> i == K ? 1 : axes(A, i), nd) for idx in CartesianIndices(remdims) Av = view(A, ntuple(i -> i == K ? Colon() : idx[i], nd)...) sort!(Av; alg, order, scratch) end A end ## uint mapping to allow radix sorting primitives other than UInts ## """ UIntMappable(T::Type, order::Ordering) Return `typeof(uint_map(x::T, order))` if [`uint_map`](@ref) and [`uint_unmap`](@ref) are implemented. If either is not implemented, return `nothing`. """ UIntMappable(T::Type, order::Ordering) = nothing """ uint_map(x, order::Ordering)::Unsigned Map `x` to an un unsigned integer, maintaining sort order. The map should be reversible with [`uint_unmap`](@ref), so `isless(order, a, b)` must be a linear ordering for `a, b <: typeof(x)`. Satisfies `isless(order, a, b) === (uint_map(a, order) < uint_map(b, order))` and `x === uint_unmap(typeof(x), uint_map(x, order), order)` See also: [`UIntMappable`](@ref) [`uint_unmap`](@ref) """ function uint_map end """ uint_unmap(T::Type, u::Unsigned, order::Ordering) Reconstruct the unique value `x::T` that uint_maps to `u`. Satisfies `x === uint_unmap(T, uint_map(x::T, order), order)` for all `x <: T`. See also: [`uint_map`](@ref) [`UIntMappable`](@ref) """ function uint_unmap end ### Primitive Types # Integers uint_map(x::Unsigned, ::ForwardOrdering) = x uint_unmap(::Type{T}, u::T, ::ForwardOrdering) where T <: Unsigned = u uint_map(x::Signed, ::ForwardOrdering) = unsigned(xor(x, typemin(x))) uint_unmap(::Type{T}, u::Unsigned, ::ForwardOrdering) where T <: Signed = xor(signed(u), typemin(T)) UIntMappable(T::BitIntegerType, ::ForwardOrdering) = unsigned(T) # Floats are not UIntMappable under regular orderings because they fail on NaN edge cases. # uint mappings for floats are defined in Float, where the Left and Right orderings # guarantee that there are no NaN values # Chars uint_map(x::Char, ::ForwardOrdering) = reinterpret(UInt32, x) uint_unmap(::Type{Char}, u::UInt32, ::ForwardOrdering) = reinterpret(Char, u) UIntMappable(::Type{Char}, ::ForwardOrdering) = UInt32 ### Reverse orderings uint_map(x, rev::ReverseOrdering) = ~uint_map(x, rev.fwd) uint_unmap(T::Type, u::Unsigned, rev::ReverseOrdering) = uint_unmap(T, ~u, rev.fwd) UIntMappable(T::Type, order::ReverseOrdering) = UIntMappable(T, order.fwd) ### Vectors # Convert v to unsigned integers in place, maintaining sort order. function uint_map!(v::AbstractVector, lo::Integer, hi::Integer, order::Ordering) u = reinterpret(UIntMappable(eltype(v), order), v) @inbounds for i in lo:hi u[i] = uint_map(v[i], order) end u end function uint_unmap!(v::AbstractVector, u::AbstractVector{U}, lo::Integer, hi::Integer, order::Ordering, offset::U=zero(U), index_offset::Integer=0) where U <: Unsigned @inbounds for i in lo:hi v[i] = uint_unmap(eltype(v), u[i+index_offset]+offset, order) end v end ### Unused constructs for backward compatibility ### ## Old algorithms ## struct QuickSortAlg <: Algorithm end struct MergeSortAlg <: Algorithm end """ PartialQuickSort{T <: Union{Integer,OrdinalRange}} Indicate that a sorting function should use the partial quick sort algorithm. `PartialQuickSort(k)` is like `QuickSort`, but is only required to find and sort the elements that would end up in `v[k]` were `v` fully sorted. Characteristics: * *not stable*: does not preserve the ordering of elements that compare equal (e.g. "a" and "A" in a sort of letters that ignores case). * *in-place* in memory. * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). Note that `PartialQuickSort(k)` does not necessarily sort the whole array. For example, ```jldoctest julia> x = rand(100); julia> k = 50:100; julia> s1 = sort(x; alg=QuickSort); julia> s2 = sort(x; alg=PartialQuickSort(k)); julia> map(issorted, (s1, s2)) (true, false) julia> map(x->issorted(x[k]), (s1, s2)) (true, true) julia> s1[k] == s2[k] true ``` """ struct PartialQuickSort{T <: Union{Integer,OrdinalRange}} <: Algorithm k::T end """ QuickSort Indicate that a sorting function should use the quick sort algorithm, which is *not* stable. Characteristics: * *not stable*: does not preserve the ordering of elements that compare equal (e.g. "a" and "A" in a sort of letters that ignores case). * *in-place* in memory. * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). * *good performance* for large collections. """ const QuickSort = QuickSortAlg() """ MergeSort Indicate that a sorting function should use the merge sort algorithm. Merge sort divides the collection into subcollections and repeatedly merges them, sorting each subcollection at each step, until the entire collection has been recombined in sorted form. Characteristics: * *stable*: preserves the ordering of elements that compare equal (e.g. "a" and "A" in a sort of letters that ignores case). * *not in-place* in memory. * *divide-and-conquer* sort strategy. * *good performance* for large collections but typically not quite as fast as [`QuickSort`](@ref). """ const MergeSort = MergeSortAlg() maybe_apply_initial_optimizations(alg::Algorithm) = alg maybe_apply_initial_optimizations(alg::QuickSortAlg) = InitialOptimizations(alg) maybe_apply_initial_optimizations(alg::MergeSortAlg) = InitialOptimizations(alg) maybe_apply_initial_optimizations(alg::InsertionSortAlg) = InitialOptimizations(alg) # selectpivot! # # Given 3 locations in an array (lo, mi, and hi), sort v[lo], v[mi], v[hi] and # choose the middle value as a pivot # # Upon return, the pivot is in v[lo], and v[hi] is guaranteed to be # greater than the pivot @inline function selectpivot!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) @inbounds begin mi = midpoint(lo, hi) # sort v[mi] <= v[lo] <= v[hi] such that the pivot is immediately in place if lt(o, v[lo], v[mi]) v[mi], v[lo] = v[lo], v[mi] end if lt(o, v[hi], v[lo]) if lt(o, v[hi], v[mi]) v[hi], v[lo], v[mi] = v[lo], v[mi], v[hi] else v[hi], v[lo] = v[lo], v[hi] end end # return the pivot return v[lo] end end # partition! # # select a pivot, and partition v according to the pivot function partition!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) pivot = selectpivot!(v, lo, hi, o) # pivot == v[lo], v[hi] > pivot i, j = lo, hi @inbounds while true i += 1; j -= 1 while lt(o, v[i], pivot); i += 1; end; while lt(o, pivot, v[j]); j -= 1; end; i >= j && break v[i], v[j] = v[j], v[i] end v[j], v[lo] = pivot, v[j] # v[j] == pivot # v[k] >= pivot for k > j # v[i] <= pivot for i < j return j end function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::QuickSortAlg, o::Ordering) @inbounds while lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) j = partition!(v, lo, hi, o) if j-lo < hi-j # recurse on the smaller chunk # this is necessary to preserve O(log(n)) # stack space in the worst case (rather than O(n)) lo < (j-1) && sort!(v, lo, j-1, a, o) lo = j+1 else j+1 < hi && sort!(v, j+1, hi, a, o) hi = j-1 end end return v end sort!(v::AbstractVector{T}, lo::Integer, hi::Integer, a::MergeSortAlg, o::Ordering, t0::Vector{T}) where T = invoke(sort!, Tuple{typeof.((v, lo, hi, a, o))..., AbstractVector{T}}, v, lo, hi, a, o, t0) # For disambiguation function sort!(v::AbstractVector{T}, lo::Integer, hi::Integer, a::MergeSortAlg, o::Ordering, t0::Union{AbstractVector{T}, Nothing}=nothing) where T @inbounds if lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) m = midpoint(lo, hi) t = t0 === nothing ? similar(v, m-lo+1) : t0 length(t) < m-lo+1 && resize!(t, m-lo+1) Base.require_one_based_indexing(t) sort!(v, lo, m, a, o, t) sort!(v, m+1, hi, a, o, t) i, j = 1, lo while j <= m t[i] = v[j] i += 1 j += 1 end i, k = 1, lo while k < j <= hi if lt(o, v[j], t[i]) v[k] = v[j] j += 1 else v[k] = t[i] i += 1 end k += 1 end while k < j v[k] = t[i] k += 1 i += 1 end end return v end function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::PartialQuickSort, o::Ordering) @inbounds while lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) j = partition!(v, lo, hi, o) if j <= first(a.k) lo = j+1 elseif j >= last(a.k) hi = j-1 else # recurse on the smaller chunk # this is necessary to preserve O(log(n)) # stack space in the worst case (rather than O(n)) if j-lo < hi-j lo < (j-1) && sort!(v, lo, j-1, a, o) lo = j+1 else hi > (j+1) && sort!(v, j+1, hi, a, o) hi = j-1 end end end return v end ## Old extensibility mechanisms ## # Support 3-, 5-, and 6-argument versions of sort! for calling into the internals in the old way sort!(v::AbstractVector, a::Algorithm, o::Ordering) = sort!(v, firstindex(v), lastindex(v), a, o) function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering) _sort!(v, a, o, (; lo, hi, legacy_dispatch_entry=a)) v end sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering, _) = sort!(v, lo, hi, a, o) function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::Algorithm, o::Ordering, scratch::Vector) _sort!(v, a, o, (; lo, hi, scratch, legacy_dispatch_entry=a)) v end # Support dispatch on custom algorithms in the old way # sort!(::AbstractVector, ::Integer, ::Integer, ::MyCustomAlgorithm, ::Ordering) = ... function _sort!(v::AbstractVector, a::Algorithm, o::Ordering, kw) @getkw lo hi scratch legacy_dispatch_entry if legacy_dispatch_entry === a # This error prevents infinite recursion for unknown algorithms throw(ArgumentError("Base.Sort._sort!(::$(typeof(v)), ::$(typeof(a)), ::$(typeof(o)), ::Any) is not defined")) else sort!(v, lo, hi, a, o) scratch end end end # module Sort V/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/binaryplatforms.jlnš# This file is a part of Julia. License is MIT: https://julialang.org/license module BinaryPlatforms export AbstractPlatform, Platform, HostPlatform, platform_dlext, tags, arch, os, os_version, libc, libgfortran_version, libstdcxx_version, cxxstring_abi, parse_dl_name_version, detect_libgfortran_version, detect_libstdcxx_version, detect_cxxstring_abi, call_abi, wordsize, triplet, select_platform, platforms_match, platform_name import .Libc.Libdl ### Submodule with information about CPU features include("cpuid.jl") using .CPUID # This exists to ease compatibility with old-style Platform objects abstract type AbstractPlatform; end """ Platform A `Platform` represents all relevant pieces of information that a julia process may need to know about its execution environment, such as the processor architecture, operating system, libc implementation, etc... It is, at its heart, a key-value mapping of tags (such as `arch`, `os`, `libc`, etc...) to values (such as `"arch" => "x86_64"`, or `"os" => "windows"`, etc...). `Platform` objects are extensible in that the tag mapping is open for users to add their own mappings to, as long as the mappings do not conflict with the set of reserved tags: `arch`, `os`, `os_version`, `libc`, `call_abi`, `libgfortran_version`, `libstdcxx_version`, `cxxstring_abi` and `julia_version`. Valid tags and values are composed of alphanumeric and period characters. All tags and values will be lowercased when stored to reduce variation. Example: Platform("x86_64", "windows"; cuda = "10.1") """ struct Platform <: AbstractPlatform tags::Dict{String,String} # The "compare strategy" allows selective overriding on how a tag is compared compare_strategies::Dict{String,Function} # Passing `tags` as a `Dict` avoids the need to infer different NamedTuple specializations function Platform(arch::String, os::String, _tags::Dict{String}; validate_strict::Bool = false, compare_strategies::Dict{String,<:Function} = Dict{String,Function}()) # A wee bit of normalization os = lowercase(os) arch = CPUID.normalize_arch(arch) tags = Dict{String,String}( "arch" => arch, "os" => os, ) for (tag, value) in _tags value = value::Union{String,VersionNumber,Nothing} tag = lowercase(tag) if tag โˆˆ ("arch", "os") throw(ArgumentError("Cannot double-pass key $(tag)")) end # Drop `nothing` values; this means feature is not present or use default value. if value === nothing continue end # Normalize things that are known to be version numbers so that comparisons are easy. # Note that in our effort to be extremely compatible, we actually allow something that # doesn't parse nicely into a VersionNumber to persist, but if `validate_strict` is # set to `true`, it will cause an error later on. if tag โˆˆ ("libgfortran_version", "libstdcxx_version", "os_version") if isa(value, VersionNumber) value = string(value) elseif isa(value, String) v = tryparse(VersionNumber, value) if isa(v, VersionNumber) value = string(v) end end end # Use `add_tag!()` to add the tag to our collection of tags add_tag!(tags, tag, string(value)::String) end # Auto-map call_abi and libc where necessary: if os == "linux" && !haskey(tags, "libc") # Default to `glibc` on Linux tags["libc"] = "glibc" end if os == "linux" && arch โˆˆ ("armv7l", "armv6l") && "call_abi" โˆ‰ keys(tags) # default `call_abi` to `eabihf` on 32-bit ARM tags["call_abi"] = "eabihf" end # If the user is asking for strict validation, do so. if validate_strict validate_tags(tags) end # By default, we compare julia_version only against major and minor versions: if haskey(tags, "julia_version") && !haskey(compare_strategies, "julia_version") compare_strategies["julia_version"] = (a::String, b::String, a_comparator, b_comparator) -> begin a = VersionNumber(a) b = VersionNumber(b) return a.major == b.major && a.minor == b.minor end end return new(tags, compare_strategies) end end # Keyword interface (to avoid inference of specialized NamedTuple methods, use the Dict interface for `tags`) function Platform(arch::String, os::String; validate_strict::Bool = false, compare_strategies::Dict{String,<:Function} = Dict{String,Function}(), kwargs...) tags = Dict{String,Any}(String(tag)::String=>tagvalue(value) for (tag, value) in kwargs) return Platform(arch, os, tags; validate_strict, compare_strategies) end tagvalue(v::Union{String,VersionNumber,Nothing}) = v tagvalue(v::Symbol) = String(v) tagvalue(v::AbstractString) = convert(String, v)::String # Simple tag insertion that performs a little bit of validation function add_tag!(tags::Dict{String,String}, tag::String, value::String) # I know we said only alphanumeric and dots, but let's be generous so that we can expand # our support in the future while remaining as backwards-compatible as possible. The # only characters that are absolutely disallowed right now are `-`, `+`, ` ` and things # that are illegal in filenames: nonos = raw"""+- /<>:"'\|?*""" if any(occursin(nono, tag) for nono in nonos) throw(ArgumentError("Invalid character in tag name \"$(tag)\"!")) end # Normalize and reject nonos value = lowercase(value) if any(occursin(nono, value) for nono in nonos) throw(ArgumentError("Invalid character in tag value \"$(value)\"!")) end tags[tag] = value return value end # Other `Platform` types can override this (I'm looking at you, `AnyPlatform`) tags(p::Platform) = p.tags # Make it act more like a dict Base.getindex(p::AbstractPlatform, k::String) = getindex(tags(p), k) Base.haskey(p::AbstractPlatform, k::String) = haskey(tags(p), k) function Base.setindex!(p::AbstractPlatform, v::String, k::String) add_tag!(tags(p), k, v) return p end # Hash definition to ensure that it's stable function Base.hash(p::Platform, h::UInt) h += 0x506c6174666f726d % UInt h = hash(p.tags, h) h = hash(p.compare_strategies, h) return h end # Simple equality definition; for compatibility testing, use `platforms_match()` function Base.:(==)(a::Platform, b::Platform) return a.tags == b.tags && a.compare_strategies == b.compare_strategies end # Allow us to easily serialize Platform objects function Base.repr(p::Platform; context=nothing) str = string( "Platform(", repr(arch(p)), ", ", repr(os(p)), "; ", join(("$(k) = $(repr(v))" for (k, v) in tags(p) if k โˆ‰ ("arch", "os")), ", "), ")", ) end # Make showing the platform a bit more palatable function Base.show(io::IO, p::Platform) str = string(platform_name(p), " ", arch(p)) # Add on all the other tags not covered by os/arch: other_tags = sort!(filter!(kv -> kv[1] โˆ‰ ("os", "arch"), collect(tags(p)))) if !isempty(other_tags) str = string(str, " {", join([string(k, "=", v) for (k, v) in other_tags], ", "), "}") end print(io, str) end function validate_tags(tags::Dict) throw_invalid_key(k) = throw(ArgumentError("Key \"$(k)\" cannot have value \"$(tags[k])\"")) # Validate `arch` if tags["arch"] โˆ‰ ("x86_64", "i686", "armv7l", "armv6l", "aarch64", "powerpc64le") throw_invalid_key("arch") end # Validate `os` if tags["os"] โˆ‰ ("linux", "macos", "freebsd", "windows") throw_invalid_key("os") end # Validate `os`/`arch` combination throw_os_mismatch() = throw(ArgumentError("Invalid os/arch combination: $(tags["os"])/$(tags["arch"])")) if tags["os"] == "windows" && tags["arch"] โˆ‰ ("x86_64", "i686", "armv7l", "aarch64") throw_os_mismatch() end if tags["os"] == "macos" && tags["arch"] โˆ‰ ("x86_64", "aarch64") throw_os_mismatch() end # Validate `os`/`libc` combination throw_libc_mismatch() = throw(ArgumentError("Invalid os/libc combination: $(tags["os"])/$(tags["libc"])")) if tags["os"] == "linux" # Linux always has a `libc` entry if tags["libc"] โˆ‰ ("glibc", "musl") throw_libc_mismatch() end else # Nothing else is allowed to have a `libc` entry if haskey(tags, "libc") throw_libc_mismatch() end end # Validate `os`/`arch`/`call_abi` combination throw_call_abi_mismatch() = throw(ArgumentError("Invalid os/arch/call_abi combination: $(tags["os"])/$(tags["arch"])/$(tags["call_abi"])")) if tags["os"] == "linux" && tags["arch"] โˆˆ ("armv7l", "armv6l") # If an ARM linux has does not have `call_abi` set to something valid, be sad. if !haskey(tags, "call_abi") || tags["call_abi"] โˆ‰ ("eabihf", "eabi") throw_call_abi_mismatch() end else # Nothing else should have a `call_abi`. if haskey(tags, "call_abi") throw_call_abi_mismatch() end end # Validate `libgfortran_version` is a parsable `VersionNumber` throw_version_number(k) = throw(ArgumentError("\"$(k)\" cannot have value \"$(tags[k])\", must be a valid VersionNumber")) if "libgfortran_version" in keys(tags) && tryparse(VersionNumber, tags["libgfortran_version"]) === nothing throw_version_number("libgfortran_version") end # Validate `cxxstring_abi` is one of the two valid options: if "cxxstring_abi" in keys(tags) && tags["cxxstring_abi"] โˆ‰ ("cxx03", "cxx11") throw_invalid_key("cxxstring_abi") end # Validate `libstdcxx_version` is a parsable `VersionNumber` if "libstdcxx_version" in keys(tags) && tryparse(VersionNumber, tags["libstdcxx_version"]) === nothing throw_version_number("libstdcxx_version") end end function set_compare_strategy!(p::Platform, key::String, f::Function) if !haskey(p.tags, key) throw(ArgumentError("Cannot set comparison strategy for nonexistent tag $(key)!")) end p.compare_strategies[key] = f end function get_compare_strategy(p::Platform, key::String, default = compare_default) if !haskey(p.tags, key) throw(ArgumentError("Cannot get comparison strategy for nonexistent tag $(key)!")) end return get(p.compare_strategies, key, default) end get_compare_strategy(p::AbstractPlatform, key::String, default = compare_default) = default """ compare_default(a::String, b::String, a_requested::Bool, b_requested::Bool) Default comparison strategy that falls back to `a == b`. This only ever happens if both `a` and `b` request this strategy, as any other strategy is preferable to this one. """ function compare_default(a::String, b::String, a_requested::Bool, b_requested::Bool) return a == b end """ compare_version_cap(a::String, b::String, a_comparator, b_comparator) Example comparison strategy for `set_comparison_strategy!()` that implements a version cap for host platforms that support _up to_ a particular version number. As an example, if an artifact is built for macOS 10.9, it can run on macOS 10.11, however if it were built for macOS 10.12, it could not. Therefore, the host platform of macOS 10.11 has a version cap at `v"10.11"`. Note that because both hosts and artifacts are represented with `Platform` objects it is possible to call `platforms_match()` with two artifacts, a host and an artifact, an artifact and a host, and even two hosts. We attempt to do something intelligent for all cases, but in the case of comparing version caps between two hosts, we return `true` only if the two host platforms are in fact identical. """ function compare_version_cap(a::String, b::String, a_requested::Bool, b_requested::Bool) a = VersionNumber(a) b = VersionNumber(b) # If both b and a requested, then we fall back to equality: if a_requested && b_requested return a == b end # Otherwise, do the comparison between the the single version cap and the single version: if a_requested return b <= a else return a <= b end end """ HostPlatform(p::AbstractPlatform) Convert a `Platform` to act like a "host"; e.g. if it has a version-bound tag such as `"libstdcxx_version" => "3.4.26"`, it will treat that value as an upper bound, rather than a characteristic. `Platform` objects that define artifacts generally denote the SDK or version that the artifact was built with, but for platforms, these versions are generally the maximal version the platform can support. The way this transformation is implemented is to change the appropriate comparison strategies to treat these pieces of data as bounds rather than points in any comparison. """ function HostPlatform(p::AbstractPlatform) if haskey(p, "os_version") set_compare_strategy!(p, "os_version", compare_version_cap) end if haskey(p, "libstdcxx_version") set_compare_strategy!(p, "libstdcxx_version", compare_version_cap) end return p end """ arch(p::AbstractPlatform) Get the architecture for the given `Platform` object as a `String`. # Examples ```jldoctest julia> arch(Platform("aarch64", "Linux")) "aarch64" julia> arch(Platform("amd64", "freebsd")) "x86_64" ``` """ arch(p::AbstractPlatform) = get(tags(p), "arch", nothing) """ os(p::AbstractPlatform) Get the operating system for the given `Platform` object as a `String`. # Examples ```jldoctest julia> os(Platform("armv7l", "Linux")) "linux" julia> os(Platform("aarch64", "macos")) "macos" ``` """ os(p::AbstractPlatform) = get(tags(p), "os", nothing) # As a special helper, it's sometimes useful to know the current OS at compile-time function os() if Sys.iswindows() return "windows" elseif Sys.isapple() return "macos" elseif Sys.isbsd() return "freebsd" else return "linux" end end """ libc(p::AbstractPlatform) Get the libc for the given `Platform` object as a `String`. Returns `nothing` on platforms with no explicit `libc` choices (which is most platforms). # Examples ```jldoctest julia> libc(Platform("armv7l", "Linux")) "glibc" julia> libc(Platform("aarch64", "linux"; libc="musl")) "musl" julia> libc(Platform("i686", "Windows")) ``` """ libc(p::AbstractPlatform) = get(tags(p), "libc", nothing) """ call_abi(p::AbstractPlatform) Get the call ABI for the given `Platform` object as a `String`. Returns `nothing` on platforms with no explicit `call_abi` choices (which is most platforms). # Examples ```jldoctest julia> call_abi(Platform("armv7l", "Linux")) "eabihf" julia> call_abi(Platform("x86_64", "macos")) ``` """ call_abi(p::AbstractPlatform) = get(tags(p), "call_abi", nothing) const platform_names = Dict( "linux" => "Linux", "macos" => "macOS", "windows" => "Windows", "freebsd" => "FreeBSD", nothing => "Unknown", ) """ platform_name(p::AbstractPlatform) Get the "platform name" of the given platform, returning e.g. "Linux" or "Windows". """ function platform_name(p::AbstractPlatform) return platform_names[os(p)] end function VNorNothing(d::Dict, key) v = get(d, key, nothing) if v === nothing return nothing end return VersionNumber(v)::VersionNumber end """ libgfortran_version(p::AbstractPlatform) Get the libgfortran version dictated by this `Platform` object as a `VersionNumber`, or `nothing` if no compatibility bound is imposed. """ libgfortran_version(p::AbstractPlatform) = VNorNothing(tags(p), "libgfortran_version") """ libstdcxx_version(p::AbstractPlatform) Get the libstdc++ version dictated by this `Platform` object, or `nothing` if no compatibility bound is imposed. """ libstdcxx_version(p::AbstractPlatform) = VNorNothing(tags(p), "libstdcxx_version") """ cxxstring_abi(p::AbstractPlatform) Get the c++ string ABI dictated by this `Platform` object, or `nothing` if no ABI is imposed. """ cxxstring_abi(p::AbstractPlatform) = get(tags(p), "cxxstring_abi", nothing) """ os_version(p::AbstractPlatform) Get the OS version dictated by this `Platform` object, or `nothing` if no OS version is imposed/no data is available. This is most commonly used by MacOS and FreeBSD objects where we have high platform SDK fragmentation, and features are available only on certain platform versions. """ os_version(p::AbstractPlatform) = VNorNothing(tags(p), "os_version") """ wordsize(p::AbstractPlatform) Get the word size for the given `Platform` object. # Examples ```jldoctest julia> wordsize(Platform("armv7l", "linux")) 32 julia> wordsize(Platform("x86_64", "macos")) 64 ``` """ wordsize(p::AbstractPlatform) = (arch(p) โˆˆ ("i686", "armv6l", "armv7l")) ? 32 : 64 """ triplet(p::AbstractPlatform; exclude_tags::Vector{String}) Get the target triplet for the given `Platform` object as a `String`. # Examples ```jldoctest julia> triplet(Platform("x86_64", "MacOS")) "x86_64-apple-darwin" julia> triplet(Platform("i686", "Windows")) "i686-w64-mingw32" julia> triplet(Platform("armv7l", "Linux"; libgfortran_version="3")) "armv7l-linux-gnueabihf-libgfortran3" ``` """ function triplet(p::AbstractPlatform) str = string( arch(p)::Union{Symbol,String}, os_str(p), libc_str(p), call_abi_str(p), ) # Tack on optional compiler ABI flags if libgfortran_version(p) !== nothing str = string(str, "-libgfortran", libgfortran_version(p).major) end if cxxstring_abi(p) !== nothing str = string(str, "-", cxxstring_abi(p)) end if libstdcxx_version(p) !== nothing str = string(str, "-libstdcxx", libstdcxx_version(p).patch) end # Tack on all extra tags for (tag, val) in tags(p) if tag โˆˆ ("os", "arch", "libc", "call_abi", "libgfortran_version", "libstdcxx_version", "cxxstring_abi", "os_version") continue end str = string(str, "-", tag, "+", val) end return str end function os_str(p::AbstractPlatform) if os(p) == "linux" return "-linux" elseif os(p) == "macos" osvn = os_version(p) if osvn !== nothing return "-apple-darwin$(osvn.major)" else return "-apple-darwin" end elseif os(p) == "windows" return "-w64-mingw32" elseif os(p) == "freebsd" osvn = os_version(p) if osvn !== nothing return "-unknown-freebsd$(osvn.major).$(osvn.minor)" else return "-unknown-freebsd" end else return "-unknown" end end # Helper functions for Linux and FreeBSD libc/abi mishmashes function libc_str(p::AbstractPlatform) lc = libc(p) if lc === nothing return "" elseif lc === "glibc" return "-gnu" else return string("-", lc) end end function call_abi_str(p::AbstractPlatform) cabi = call_abi(p) cabi === nothing ? "" : string(cabi::Union{Symbol,String}) end Sys.isapple(p::AbstractPlatform) = os(p) == "macos" Sys.islinux(p::AbstractPlatform) = os(p) == "linux" Sys.iswindows(p::AbstractPlatform) = os(p) == "windows" Sys.isfreebsd(p::AbstractPlatform) = os(p) == "freebsd" Sys.isbsd(p::AbstractPlatform) = os(p) โˆˆ ("freebsd", "macos") Sys.isunix(p::AbstractPlatform) = Sys.isbsd(p) || Sys.islinux(p) const arch_mapping = Dict( "x86_64" => "(x86_|amd)64", "i686" => "i\\d86", "aarch64" => "(aarch64|arm64)", "armv7l" => "arm(v7l)?", # if we just see `arm-linux-gnueabihf`, we assume it's `armv7l` "armv6l" => "armv6l", "powerpc64le" => "p(ower)?pc64le", ) # Keep this in sync with `CPUID.ISAs_by_family` # These are the CPUID side of the microarchitectures targeted by GCC flags in BinaryBuilder.jl const arch_march_isa_mapping = let function get_set(arch, name) all = CPUID.ISAs_by_family[arch] return all[findfirst(x -> x.first == name, all)].second end Dict( "i686" => [ "pentium4" => get_set("i686", "pentium4"), "prescott" => get_set("i686", "prescott"), ], "x86_64" => [ "x86_64" => get_set("x86_64", "x86_64"), "avx" => get_set("x86_64", "sandybridge"), "avx2" => get_set("x86_64", "haswell"), "avx512" => get_set("x86_64", "skylake_avx512"), ], "armv6l" => [ "arm1176jzfs" => get_set("armv6l", "arm1176jzfs"), ], "armv7l" => [ "armv7l" => get_set("armv7l", "armv7l"), "neonvfpv4" => get_set("armv7l", "armv7l+neon+vfpv4"), ], "aarch64" => [ "armv8_0" => get_set("aarch64", "armv8.0-a"), "armv8_1" => get_set("aarch64", "armv8.1-a"), "armv8_2_crypto" => get_set("aarch64", "armv8.2-a+crypto"), "a64fx" => get_set("aarch64", "a64fx"), "apple_m1" => get_set("aarch64", "apple_m1"), ], "powerpc64le" => [ "power8" => get_set("powerpc64le", "power8"), ] ) end const os_mapping = Dict( "macos" => "-apple-darwin[\\d\\.]*", "freebsd" => "-(.*-)?freebsd[\\d\\.]*", "windows" => "-w64-mingw32", "linux" => "-(.*-)?linux", ) const libc_mapping = Dict( "libc_nothing" => "", "glibc" => "-gnu", "musl" => "-musl", ) const call_abi_mapping = Dict( "call_abi_nothing" => "", "eabihf" => "eabihf", "eabi" => "eabi", ) const libgfortran_version_mapping = Dict( "libgfortran_nothing" => "", "libgfortran3" => "(-libgfortran3)|(-gcc4)", # support old-style `gccX` versioning "libgfortran4" => "(-libgfortran4)|(-gcc7)", "libgfortran5" => "(-libgfortran5)|(-gcc8)", ) const cxxstring_abi_mapping = Dict( "cxxstring_nothing" => "", "cxx03" => "-cxx03", "cxx11" => "-cxx11", ) const libstdcxx_version_mapping = Dict{String,String}( "libstdcxx_nothing" => "", "libstdcxx" => "-libstdcxx\\d+", ) """ parse(::Type{Platform}, triplet::AbstractString) Parses a string platform triplet back into a `Platform` object. """ function Base.parse(::Type{Platform}, triplet::String; validate_strict::Bool = false) # Helper function to collapse dictionary of mappings down into a regex of # named capture groups joined by "|" operators c(mapping) = string("(",join(["(?<$k>$v)" for (k, v) in mapping], "|"), ")") # We're going to build a mondo regex here to parse everything: triplet_regex = Regex(string( "^", # First, the core triplet; arch/os/libc/call_abi c(arch_mapping), c(os_mapping), c(libc_mapping), c(call_abi_mapping), # Next, optional things, like libgfortran/libstdcxx/cxxstring abi c(libgfortran_version_mapping), c(cxxstring_abi_mapping), c(libstdcxx_version_mapping), # Finally, the catch-all for extended tags "(?(?:-[^-]+\\+[^-]+)*)?", "\$", )) m = match(triplet_regex, triplet) if m !== nothing # Helper function to find the single named field within the giant regex # that is not `nothing` for each mapping we give it. get_field(m, mapping) = begin for k in keys(mapping) if m[k] !== nothing # Convert our sentinel `nothing` values to actual `nothing` if endswith(k, "_nothing") return nothing end # Convert libgfortran/libstdcxx version numbers if startswith(k, "libgfortran") return VersionNumber(parse(Int,k[12:end])) elseif startswith(k, "libstdcxx") return VersionNumber(3, 4, parse(Int,m[k][11:end])) else return k end end end end # Extract the information we're interested in: tags = Dict{String,Any}() arch = get_field(m, arch_mapping) os = get_field(m, os_mapping) tags["libc"] = get_field(m, libc_mapping) tags["call_abi"] = get_field(m, call_abi_mapping) tags["libgfortran_version"] = get_field(m, libgfortran_version_mapping) tags["libstdcxx_version"] = get_field(m, libstdcxx_version_mapping) tags["cxxstring_abi"] = get_field(m, cxxstring_abi_mapping) function split_tags(tagstr) tag_fields = split(tagstr, "-"; keepempty=false) if isempty(tag_fields) return Pair{String,String}[] end return map(v -> String(v[1]) => String(v[2]), split.(tag_fields, "+")) end merge!(tags, Dict(split_tags(m["tags"]))) # Special parsing of os version number, if any exists function extract_os_version(os_name, pattern) m_osvn = match(pattern, m[os_name]) if m_osvn !== nothing return VersionNumber(m_osvn.captures[1]) end return nothing end os_version = nothing if os == "macos" os_version = extract_os_version("macos", r".*darwin([\d\.]+)"sa) end if os == "freebsd" os_version = extract_os_version("freebsd", r".*freebsd([\d.]+)"sa) end tags["os_version"] = os_version return Platform(arch, os, tags; validate_strict) end throw(ArgumentError("Platform `$(triplet)` is not an officially supported platform")) end Base.parse(::Type{Platform}, triplet::AbstractString; kwargs...) = parse(Platform, convert(String, triplet)::String; kwargs...) function Base.tryparse(::Type{Platform}, triplet::AbstractString) try parse(Platform, triplet) catch e if isa(e, InterruptException) rethrow(e) end return nothing end end """ platform_dlext(p::AbstractPlatform = HostPlatform()) Return the dynamic library extension for the given platform, defaulting to the currently running platform. E.g. returns "so" for a Linux-based platform, "dll" for a Windows-based platform, etc... """ function platform_dlext(p::AbstractPlatform = HostPlatform()) if os(p) == "windows" return "dll" elseif os(p) == "macos" return "dylib" else return "so" end end """ parse_dl_name_version(path::String, platform::AbstractPlatform) Given a path to a dynamic library, parse out what information we can from the filename. E.g. given something like "lib/libfoo.so.3.2", this function returns `"libfoo", v"3.2"`. If the path name is not a valid dynamic library, this method throws an error. If no soversion can be extracted from the filename, as in "libbar.so" this method returns `"libbar", nothing`. """ function parse_dl_name_version(path::String, os::String) # Use an extraction regex that matches the given OS local dlregex if os == "windows" # On Windows, libraries look like `libnettle-6.dll` dlregex = r"^(.*?)(?:-((?:[\.\d]+)*))?\.dll$"sa elseif os == "macos" # On OSX, libraries look like `libnettle.6.3.dylib` dlregex = r"^(.*?)((?:\.[\d]+)*)\.dylib$"sa else # On Linux and FreeBSD, libraries look like `libnettle.so.6.3.0` dlregex = r"^(.*?)\.so((?:\.[\d]+)*)$"sa end m = match(dlregex, basename(path)) if m === nothing throw(ArgumentError("Invalid dynamic library path '$path'")) end # Extract name and version name = m.captures[1] version = m.captures[2] if version === nothing || isempty(version) version = nothing else version = VersionNumber(strip(version, '.')) end return name, version end # Adapter for `AbstractString` function parse_dl_name_version(path::AbstractString, os::AbstractString) return parse_dl_name_version(string(path)::String, string(os)::String) end """ detect_libgfortran_version() Inspects the current Julia process to determine the libgfortran version this Julia is linked against (if any). """ function detect_libgfortran_version() libgfortran_paths = filter!(x -> occursin("libgfortran", x), Libdl.dllist()) if isempty(libgfortran_paths) # One day, I hope to not be linking against libgfortran in base Julia return nothing end libgfortran_path = first(libgfortran_paths) name, version = parse_dl_name_version(libgfortran_path, os()) if version === nothing # Even though we complain about this, we allow it to continue in the hopes that # we shall march on to a BRIGHTER TOMORROW. One in which we are not shackled # by the constraints of libgfortran compiler ABIs upon our precious programming # languages; one where the mistakes of yesterday are mere memories and not # continual maintenance burdens upon the children of the dawn; one where numeric # code may be cleanly implemented in a modern language and not bestowed onto the # next generation by grizzled ancients, documented only with a faded yellow # sticky note that bears a hastily-scribbled "good luck". @warn("Unable to determine libgfortran version from '$(libgfortran_path)'") end return version end """ detect_libstdcxx_version(max_minor_version::Int=30) Inspects the currently running Julia process to find out what version of libstdc++ it is linked against (if any). `max_minor_version` is the latest version in the 3.4 series of GLIBCXX where the search is performed. """ function detect_libstdcxx_version(max_minor_version::Int=30) libstdcxx_paths = filter!(x -> occursin("libstdc++", x), Libdl.dllist()) if isempty(libstdcxx_paths) # This can happen if we were built by clang, so we don't link against # libstdc++ at all. return nothing end # Brute-force our way through GLIBCXX_* symbols to discover which version we're linked against hdl = Libdl.dlopen(first(libstdcxx_paths))::Ptr{Cvoid} # Try all GLIBCXX versions down to GCC v4.8: # https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for minor_version in max_minor_version:-1:18 if Libdl.dlsym(hdl, "GLIBCXX_3.4.$(minor_version)"; throw_error=false) !== nothing Libdl.dlclose(hdl) return VersionNumber("3.4.$(minor_version)") end end Libdl.dlclose(hdl) return nothing end """ detect_cxxstring_abi() Inspects the currently running Julia process to see what version of the C++11 string ABI it was compiled with (this is only relevant if compiled with `g++`; `clang` has no incompatibilities yet, bless its heart). In reality, this actually checks for symbols within LLVM, but that is close enough for our purposes, as you can't mix configurations between Julia and LLVM; they must match. """ function detect_cxxstring_abi() # First, if we're not linked against libstdc++, then early-exit because this doesn't matter. libstdcxx_paths = filter!(x -> occursin("libstdc++", x), Libdl.dllist()) if isempty(libstdcxx_paths) # We were probably built by `clang`; we don't link against `libstdc++`` at all. return nothing end function open_libllvm(f::Function) for lib_name in (Base.libllvm_name, "libLLVM", "LLVM", "libLLVMSupport") hdl = Libdl.dlopen_e(lib_name) if hdl != C_NULL try return f(hdl) finally Libdl.dlclose(hdl) end end end error("Unable to open libLLVM!") end return open_libllvm() do hdl # Check for llvm::sys::getProcessTriple(), first without cxx11 tag: if Libdl.dlsym_e(hdl, "_ZN4llvm3sys16getProcessTripleEv") != C_NULL return "cxx03" elseif Libdl.dlsym_e(hdl, "_ZN4llvm3sys16getProcessTripleB5cxx11Ev") != C_NULL return "cxx11" else @warn("Unable to find llvm::sys::getProcessTriple() in libLLVM!") return nothing end end end """ host_triplet() Build host triplet out of `Sys.MACHINE` and various introspective utilities that detect compiler ABI values such as `libgfortran_version`, `libstdcxx_version` and `cxxstring_abi`. We do this without using any `Platform` tech as it must run before we have much of that built. """ function host_triplet() str = Base.BUILD_TRIPLET if !occursin("-libgfortran", str) libgfortran_version = detect_libgfortran_version() if libgfortran_version !== nothing str = string(str, "-libgfortran", libgfortran_version.major) end end if !occursin("-cxx", str) cxxstring_abi = detect_cxxstring_abi() if cxxstring_abi !== nothing str = string(str, "-", cxxstring_abi) end end if !occursin("-libstdcxx", str) libstdcxx_version = detect_libstdcxx_version() if libstdcxx_version !== nothing str = string(str, "-libstdcxx", libstdcxx_version.patch) end end # Add on julia_version extended tag if !occursin("-julia_version+", str) str = string(str, "-julia_version+", VersionNumber(VERSION.major, VERSION.minor, VERSION.patch)) end return str end """ HostPlatform() Return the `Platform` object that corresponds to the current host system, with all relevant comparison strategies set to host platform mode. This is equivalent to: HostPlatform(parse(Platform, Base.BinaryPlatforms.host_triplet())) """ function HostPlatform() return HostPlatform(parse(Platform, host_triplet()))::Platform end """ platforms_match(a::AbstractPlatform, b::AbstractPlatform) Return `true` if `a` and `b` are matching platforms, where matching is determined by comparing all keys contained within the platform objects, and if both objects contain entries for that key, they must match. Comparison, by default, is performed using the `==` operator, however this can be overridden on a key-by-key basis by adding "comparison strategies" through `set_compare_strategy!(platform, key, func)`. Note that as the comparison strategy is set on the `Platform` object, and not globally, a custom comparison strategy is first looked for within the `a` object, then if none is found, it is looked for in the `b` object. Finally, if none is found in either, the default of `==(ak, bk)` is used. We throw an error if custom comparison strategies are used on both `a` and `b` and they are not the same custom comparison. The reserved tags `os_version` and `libstdcxx_version` use this mechanism to provide bounded version constraints, where an artifact can specify that it was built using APIs only available in macOS `v"10.11"` and later, or an artifact can state that it requires a libstdc++ that is at least `v"3.4.22"`, etc... """ function platforms_match(a::AbstractPlatform, b::AbstractPlatform) for k in union(keys(tags(a)::Dict{String,String}), keys(tags(b)::Dict{String,String})) ak = get(tags(a), k, nothing) bk = get(tags(b), k, nothing) # Only continue if both `ak` and `bk` are not `nothing` if ak === nothing || bk === nothing continue end a_comp = get_compare_strategy(a, k) b_comp = get_compare_strategy(b, k) # Throw an error if `a` and `b` have both set non-default comparison strategies for `k` # and they're not the same strategy. if a_comp !== compare_default && b_comp !== compare_default && a_comp !== b_comp throw(ArgumentError("Cannot compare Platform objects with two different non-default comparison strategies for the same key \"$(k)\"")) end # Select the custom comparator, if we have one. comparator = a_comp if b_comp !== compare_default comparator = b_comp end # Call the comparator, passing in which objects requested this comparison (one, the other, or both) # For some comparators this doesn't matter, but for non-symmetrical comparisons, it does. if !(comparator(ak, bk, a_comp === comparator, b_comp === comparator)::Bool) return false end end return true end function platforms_match(a::String, b::AbstractPlatform) return platforms_match(parse(Platform, a), b) end function platforms_match(a::AbstractPlatform, b::String) return platforms_match(a, parse(Platform, b)) end platforms_match(a::String, b::String) = platforms_match(parse(Platform, a), parse(Platform, b)) # Adapters for AbstractString backedge avoidance platforms_match(a::AbstractString, b::AbstractPlatform) = platforms_match(string(a)::String, b) platforms_match(a::AbstractPlatform, b::AbstractString) = platforms_match(a, string(b)::String) platforms_match(a::AbstractString, b::AbstractString) = platforms_match(string(a)::String, string(b)::String) """ select_platform(download_info::Dict, platform::AbstractPlatform = HostPlatform()) Given a `download_info` dictionary mapping platforms to some value, choose the value whose key best matches `platform`, returning `nothing` if no matches can be found. Platform attributes such as architecture, libc, calling ABI, etc... must all match exactly, however attributes such as compiler ABI can have wildcards within them such as `nothing` which matches any version of GCC. """ function select_platform(download_info::Dict, platform::AbstractPlatform = HostPlatform()) ps = collect(filter(p -> platforms_match(p, platform), keys(download_info))) if isempty(ps) return nothing end # At this point, we may have multiple possibilities. We now engage a multi- # stage selection algorithm, where we first sort the matches by how complete # the match is, e.g. preferring matches where the intersection of tags is # equal to the union of the tags: function match_loss(a, b) a_tags = Set(keys(tags(a))) b_tags = Set(keys(tags(b))) return length(union(a_tags, b_tags)) - length(intersect(a_tags, b_tags)) end # We prefer these better matches, and secondarily reverse-sort by triplet so # as to generally choose the latest release (e.g. a `libgfortran5` tarball # over a `libgfortran3` tarball). sort!(ps, lt = (a, b) -> begin loss_a = match_loss(a, platform) loss_b = match_loss(b, platform) if loss_a != loss_b return loss_a < loss_b end return triplet(a) > triplet(b) end) # @invokelatest here to not get invalidated by new defs of `==(::Function, ::Function)` return @invokelatest getindex(download_info, first(ps)) end # precompiles to reduce latency (see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1025692379) Dict{Platform,String}()[HostPlatform()] = "" Platform("x86_64", "linux", Dict{String,Any}(); validate_strict=true) Platform("x86_64", "linux", Dict{String,String}(); validate_strict=false) # called this way from Artifacts.unpack_platform end # module L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/cpuid.jlก# This file is a part of Julia. License is MIT: https://julialang.org/license module CPUID export cpu_isa """ ISA(features::Set{UInt32}) A structure which represents the Instruction Set Architecture (ISA) of a computer. It holds the `Set` of features of the CPU. The numerical values of the features are automatically generated from the C source code of Julia and stored in the `features_h.jl` Julia file. """ struct ISA features::Set{UInt32} end Base.:<=(a::ISA, b::ISA) = a.features <= b.features Base.:<(a::ISA, b::ISA) = a.features < b.features Base.isless(a::ISA, b::ISA) = a < b include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "features_h.jl")) # include($BUILDROOT/base/features_h.jl) # Keep in sync with `arch_march_isa_mapping`. const ISAs_by_family = Dict( "i686" => [ # Source: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html. # Implicit in all sets, because always required by Julia: mmx, sse, sse2 "pentium4" => ISA(Set{UInt32}()), "prescott" => ISA(Set((JL_X86_sse3,))), ], "x86_64" => [ # Source: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html. # Implicit in all sets, because always required by x86-64 architecture: mmx, sse, sse2 "x86_64" => ISA(Set{UInt32}()), "core2" => ISA(Set((JL_X86_sse3, JL_X86_ssse3))), "nehalem" => ISA(Set((JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt))), "sandybridge" => ISA(Set((JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_aes, JL_X86_pclmul))), "haswell" => ISA(Set((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c))), "skylake" => ISA(Set((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c, JL_X86_rdseed, JL_X86_adx, JL_X86_prfchw, JL_X86_clflushopt, JL_X86_xsavec, JL_X86_xsaves))), "skylake_avx512" => ISA(Set((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_pku, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c, JL_X86_rdseed, JL_X86_adx, JL_X86_prfchw, JL_X86_clflushopt, JL_X86_xsavec, JL_X86_xsaves, JL_X86_avx512f, JL_X86_clwb, JL_X86_avx512vl, JL_X86_avx512bw, JL_X86_avx512dq, JL_X86_avx512cd))), ], "armv6l" => [ # The only armv6l processor we know of that runs Julia on armv6l # We don't have a good way to tell the different armv6l variants apart through features, # and honestly we don't care much since it's basically this one chip that people want to use with Julia. "arm1176jzfs" => ISA(Set{UInt32}()), ], "armv7l" => [ "armv7l" => ISA(Set{UInt32}()), "armv7l+neon" => ISA(Set((JL_AArch32_neon,))), "armv7l+neon+vfpv4" => ISA(Set((JL_AArch32_neon, JL_AArch32_vfp4))), ], "aarch64" => [ # Implicit in all sets, because always required: fp, asimd "armv8.0-a" => ISA(Set{UInt32}()), "armv8.1-a" => ISA(Set((JL_AArch64_v8_1a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm))), "armv8.2-a+crypto" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2))), "a64fx" => ISA(Set((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_sha2, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fullfp16, JL_AArch64_sve))), "apple_m1" => ISA(Set((JL_AArch64_v8_5a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_sha3, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fp16fml, JL_AArch64_fullfp16, JL_AArch64_dotprod, JL_AArch64_rcpc, JL_AArch64_altnzcv))), ], "powerpc64le" => [ # We have no way to test powerpc64le features yet, so we're only going to declare the lowest ISA: "power8" => ISA(Set{UInt32}()), ] ) # Test a CPU feature exists on the currently-running host test_cpu_feature(feature::UInt32) = ccall(:jl_test_cpu_feature, Bool, (UInt32,), feature) # Normalize some variation in ARCH values (which typically come from `uname -m`) function normalize_arch(arch::String) arch = lowercase(arch) if arch โˆˆ ("amd64",) arch = "x86_64" elseif arch โˆˆ ("i386", "i486", "i586") arch = "i686" elseif arch โˆˆ ("armv6",) arch = "armv6l" elseif arch โˆˆ ("arm", "armv7", "armv8", "armv8l") arch = "armv7l" elseif arch โˆˆ ("arm64",) arch = "aarch64" elseif arch โˆˆ ("ppc64le",) arch = "powerpc64le" end return arch end let # Collect all relevant features for the current architecture, if any. FEATURES = UInt32[] arch = normalize_arch(String(Sys.ARCH)) if arch in keys(ISAs_by_family) for isa in ISAs_by_family[arch] unique!(append!(FEATURES, last(isa).features)) end end # Use `@eval` to inline the list of features. @eval function cpu_isa() return ISA(Set{UInt32}(feat for feat in $(FEATURES) if test_cpu_feature(feat))) end end """ cpu_isa() Return the [`ISA`](@ref) (instruction set architecture) of the current CPU. """ cpu_isa end # module CPUID Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/features_h.jlƒ# X86 features const JL_X86_sse3 = UInt32(0) const JL_X86_pclmul = UInt32(1) const JL_X86_ssse3 = UInt32(9) const JL_X86_fma = UInt32(12) const JL_X86_cx16 = UInt32(13) const JL_X86_sse41 = UInt32(19) const JL_X86_sse42 = UInt32(20) const JL_X86_movbe = UInt32(22) const JL_X86_popcnt = UInt32(23) const JL_X86_aes = UInt32(25) const JL_X86_xsave = UInt32(26) const JL_X86_avx = UInt32(28) const JL_X86_f16c = UInt32(29) const JL_X86_rdrnd = UInt32(30) const JL_X86_fsgsbase = UInt32(32 * 2 + 0) const JL_X86_bmi = UInt32(32 * 2 + 3) const JL_X86_avx2 = UInt32(32 * 2 + 5) const JL_X86_bmi2 = UInt32(32 * 2 + 8) const JL_X86_rtm = UInt32(32 * 2 + 11) const JL_X86_avx512f = UInt32(32 * 2 + 16) const JL_X86_avx512dq = UInt32(32 * 2 + 17) const JL_X86_rdseed = UInt32(32 * 2 + 18) const JL_X86_adx = UInt32(32 * 2 + 19) const JL_X86_avx512ifma = UInt32(32 * 2 + 21) const JL_X86_clflushopt = UInt32(32 * 2 + 23) const JL_X86_clwb = UInt32(32 * 2 + 24) const JL_X86_avx512pf = UInt32(32 * 2 + 26) const JL_X86_avx512er = UInt32(32 * 2 + 27) const JL_X86_avx512cd = UInt32(32 * 2 + 28) const JL_X86_sha = UInt32(32 * 2 + 29) const JL_X86_avx512bw = UInt32(32 * 2 + 30) const JL_X86_avx512vl = UInt32(32 * 2 + 31) const JL_X86_prefetchwt1 = UInt32(32 * 3 + 0) const JL_X86_avx512vbmi = UInt32(32 * 3 + 1) const JL_X86_pku = UInt32(32 * 3 + 4) const JL_X86_waitpkg = UInt32(32 * 3 + 5) const JL_X86_avx512vbmi2 = UInt32(32 * 3 + 6) const JL_X86_shstk = UInt32(32 * 3 + 7) const JL_X86_gfni = UInt32(32 * 3 + 8) const JL_X86_vaes = UInt32(32 * 3 + 9) const JL_X86_vpclmulqdq = UInt32(32 * 3 + 10) const JL_X86_avx512vnni = UInt32(32 * 3 + 11) const JL_X86_avx512bitalg = UInt32(32 * 3 + 12) const JL_X86_avx512vpopcntdq = UInt32(32 * 3 + 14) const JL_X86_rdpid = UInt32(32 * 3 + 22) const JL_X86_cldemote = UInt32(32 * 3 + 25) const JL_X86_movdiri = UInt32(32 * 3 + 27) const JL_X86_movdir64b = UInt32(32 * 3 + 28) const JL_X86_enqcmd = UInt32(32 * 3 + 29) const JL_X86_uintr = UInt32(32 * 4 + 5) const JL_X86_avx512vp2intersect = UInt32(32 * 4 + 8) const JL_X86_serialize = UInt32(32 * 4 + 14) const JL_X86_tsxldtrk = UInt32(32 * 4 + 16) const JL_X86_pconfig = UInt32(32 * 4 + 18) const JL_X86_amx_bf16 = UInt32(32 * 4 + 22) const JL_X86_avx512fp16 = UInt32(32 * 4 + 23) const JL_X86_amx_tile = UInt32(32 * 4 + 24) const JL_X86_amx_int8 = UInt32(32 * 4 + 25) const JL_X86_sahf = UInt32(32 * 5 + 0) const JL_X86_lzcnt = UInt32(32 * 5 + 5) const JL_X86_sse4a = UInt32(32 * 5 + 6) const JL_X86_prfchw = UInt32(32 * 5 + 8) const JL_X86_xop = UInt32(32 * 5 + 11) const JL_X86_fma4 = UInt32(32 * 5 + 16) const JL_X86_tbm = UInt32(32 * 5 + 21) const JL_X86_mwaitx = UInt32(32 * 5 + 29) const JL_X86_xsaveopt = UInt32(32 * 7 + 0) const JL_X86_xsavec = UInt32(32 * 7 + 1) const JL_X86_xsaves = UInt32(32 * 7 + 3) const JL_X86_clzero = UInt32(32 * 8 + 0) const JL_X86_wbnoinvd = UInt32(32 * 8 + 9) const JL_X86_avxvnni = UInt32(32 * 9 + 4) const JL_X86_avx512bf16 = UInt32(32 * 9 + 5) const JL_X86_ptwrite = UInt32(32 * 10 + 4) # AArch32 features const JL_AArch32_neon = UInt32(12) const JL_AArch32_vfp3 = UInt32(13) const JL_AArch32_vfp4 = UInt32(16) const JL_AArch32_hwdiv_arm = UInt32(17) const JL_AArch32_hwdiv = UInt32(18) const JL_AArch32_d32 = UInt32(19) const JL_AArch32_crypto = UInt32(32 + 0) const JL_AArch32_crc = UInt32(32 + 4) const JL_AArch32_aclass = UInt32(32 * 2 + 0) const JL_AArch32_rclass = UInt32(32 * 2 + 1) const JL_AArch32_mclass = UInt32(32 * 2 + 2) const JL_AArch32_v7 = UInt32(32 * 2 + 3) const JL_AArch32_v8 = UInt32(32 * 2 + 4) const JL_AArch32_v8_1a = UInt32(32 * 2 + 5) const JL_AArch32_v8_2a = UInt32(32 * 2 + 6) const JL_AArch32_v8_3a = UInt32(32 * 2 + 7) const JL_AArch32_v8_m_main = UInt32(32 * 2 + 8) const JL_AArch32_v8_4a = UInt32(32 * 2 + 9) const JL_AArch32_v8_5a = UInt32(32 * 2 + 10) const JL_AArch32_v8_6a = UInt32(32 * 2 + 11) # AArch64 features const JL_AArch64_aes = UInt32(4) const JL_AArch64_sha2 = UInt32(6) const JL_AArch64_crc = UInt32(7) const JL_AArch64_lse = UInt32(8) const JL_AArch64_fullfp16 = UInt32(9) const JL_AArch64_rdm = UInt32(12) const JL_AArch64_jsconv = UInt32(13) const JL_AArch64_complxnum = UInt32(14) const JL_AArch64_rcpc = UInt32(15) const JL_AArch64_ccpp = UInt32(16) const JL_AArch64_sha3 = UInt32(17) const JL_AArch64_sm4 = UInt32(19) const JL_AArch64_dotprod = UInt32(20) const JL_AArch64_sve = UInt32(22) const JL_AArch64_fp16fml = UInt32(23) const JL_AArch64_dit = UInt32(24) const JL_AArch64_rcpc_immo = UInt32(26) const JL_AArch64_flagm = UInt32(27) const JL_AArch64_ssbs = UInt32(28) const JL_AArch64_sb = UInt32(29) const JL_AArch64_pauth = UInt32(30) const JL_AArch64_ccdp = UInt32(32 + 0) const JL_AArch64_sve2 = UInt32(32 + 1) const JL_AArch64_sve2_aes = UInt32(32 + 3) const JL_AArch64_sve2_bitperm = UInt32(32 + 4) const JL_AArch64_sve2_sha3 = UInt32(32 + 5) const JL_AArch64_sve2_sm4 = UInt32(32 + 6) const JL_AArch64_altnzcv = UInt32(32 + 7) const JL_AArch64_fptoint = UInt32(32 + 8) const JL_AArch64_f32mm = UInt32(32 + 10) const JL_AArch64_f64mm = UInt32(32 + 11) const JL_AArch64_i8mm = UInt32(32 + 13) const JL_AArch64_bf16 = UInt32(32 + 14) const JL_AArch64_rand = UInt32(32 + 16) const JL_AArch64_bti = UInt32(32 + 17) const JL_AArch64_mte = UInt32(32 + 18) const JL_AArch64_v8_1a = UInt32(32 * 2 + 0) const JL_AArch64_v8_2a = UInt32(32 * 2 + 1) const JL_AArch64_v8_3a = UInt32(32 * 2 + 2) const JL_AArch64_v8_4a = UInt32(32 * 2 + 3) const JL_AArch64_v8_5a = UInt32(32 * 2 + 4) const JL_AArch64_v8_6a = UInt32(32 * 2 + 5) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/fastmath.jl8# This file is a part of Julia. License is MIT: https://julialang.org/license # Support for @fastmath # This module provides versions of math functions that may violate # strict IEEE semantics. # This allows the following transformations. For more information see # http://llvm.org/docs/LangRef.html#fast-math-flags: # nnan: No NaNs - Allow optimizations to assume the arguments and # result are not NaN. Such optimizations are required to retain # defined behavior over NaNs, but the value of the result is # undefined. # ninf: No Infs - Allow optimizations to assume the arguments and # result are not +/-Inf. Such optimizations are required to # retain defined behavior over +/-Inf, but the value of the # result is undefined. # nsz: No Signed Zeros - Allow optimizations to treat the sign of a # zero argument or result as insignificant. # arcp: Allow Reciprocal - Allow optimizations to use the reciprocal # of an argument rather than perform division. # fast: Fast - Allow algebraically equivalent transformations that may # dramatically change results in floating point (e.g. # reassociate). This flag implies all the others. module FastMath export @fastmath import Core.Intrinsics: sqrt_llvm_fast, neg_float_fast, add_float_fast, sub_float_fast, mul_float_fast, div_float_fast, eq_float_fast, ne_float_fast, lt_float_fast, le_float_fast const fast_op = Dict(# basic arithmetic :+ => :add_fast, :- => :sub_fast, :* => :mul_fast, :/ => :div_fast, :(==) => :eq_fast, :!= => :ne_fast, :< => :lt_fast, :<= => :le_fast, :> => :gt_fast, :>= => :ge_fast, :abs => :abs_fast, :abs2 => :abs2_fast, :cmp => :cmp_fast, :conj => :conj_fast, :inv => :inv_fast, :rem => :rem_fast, :sign => :sign_fast, :isfinite => :isfinite_fast, :isinf => :isinf_fast, :isnan => :isnan_fast, :issubnormal => :issubnormal_fast, # math functions :^ => :pow_fast, :acos => :acos_fast, :acosh => :acosh_fast, :angle => :angle_fast, :asin => :asin_fast, :asinh => :asinh_fast, :atan => :atan_fast, :atanh => :atanh_fast, :cbrt => :cbrt_fast, :cis => :cis_fast, :cos => :cos_fast, :cosh => :cosh_fast, :exp10 => :exp10_fast, :exp2 => :exp2_fast, :exp => :exp_fast, :expm1 => :expm1_fast, :hypot => :hypot_fast, :log10 => :log10_fast, :log1p => :log1p_fast, :log2 => :log2_fast, :log => :log_fast, :max => :max_fast, :min => :min_fast, :minmax => :minmax_fast, :sin => :sin_fast, :sincos => :sincos_fast, :sinh => :sinh_fast, :sqrt => :sqrt_fast, :tan => :tan_fast, :tanh => :tanh_fast, # reductions :maximum => :maximum_fast, :minimum => :minimum_fast, :maximum! => :maximum!_fast, :minimum! => :minimum!_fast) const rewrite_op = Dict(:+= => :+, :-= => :-, :*= => :*, :/= => :/, :^= => :^) function make_fastmath(expr::Expr) if expr.head === :quote return expr elseif expr.head === :call && expr.args[1] === :^ && expr.args[3] isa Integer # mimic Julia's literal_pow lowering of literal integer powers return Expr(:call, :(Base.FastMath.pow_fast), make_fastmath(expr.args[2]), Val{expr.args[3]}()) end op = get(rewrite_op, expr.head, :nothing) if op !== :nothing var = expr.args[1] rhs = expr.args[2] if isa(var, Symbol) # simple assignment expr = :($var = $op($var, $rhs)) end # It is hard to optimize array[i += 1] += 1 # and array[end] += 1 without bugs. (#47241) # We settle for not optimizing the op= call. end Base.exprarray(make_fastmath(expr.head), Base.mapany(make_fastmath, expr.args)) end function make_fastmath(symb::Symbol) fast_symb = get(fast_op, symb, :nothing) if fast_symb === :nothing return symb end :(Base.FastMath.$fast_symb) end make_fastmath(expr) = expr """ @fastmath expr Execute a transformed version of the expression, which calls functions that may violate strict IEEE semantics. This allows the fastest possible operation, but results are undefined -- be careful when doing this, as it may change numerical results. This sets the [LLVM Fast-Math flags](http://llvm.org/docs/LangRef.html#fast-math-flags), and corresponds to the `-ffast-math` option in clang. See [the notes on performance annotations](@ref man-performance-annotations) for more details. # Examples ```jldoctest julia> @fastmath 1+2 3 julia> @fastmath(sin(3)) 0.1411200080598672 ``` """ macro fastmath(expr) make_fastmath(esc(expr)) end # Basic arithmetic const FloatTypes = Union{Float16,Float32,Float64} sub_fast(x::FloatTypes) = neg_float_fast(x) add_fast(x::T, y::T) where {T<:FloatTypes} = add_float_fast(x, y) sub_fast(x::T, y::T) where {T<:FloatTypes} = sub_float_fast(x, y) mul_fast(x::T, y::T) where {T<:FloatTypes} = mul_float_fast(x, y) div_fast(x::T, y::T) where {T<:FloatTypes} = div_float_fast(x, y) add_fast(x::T, y::T, zs::T...) where {T<:FloatTypes} = add_fast(add_fast(x, y), zs...) mul_fast(x::T, y::T, zs::T...) where {T<:FloatTypes} = mul_fast(mul_fast(x, y), zs...) @fastmath begin cmp_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(x==y, 0, ifelse(x x, y, x) min_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(y > x, x, y) minmax_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(y > x, (x,y), (y,x)) max_fast(x::T, y::T, z::T...) where {T<:FloatTypes} = max_fast(max_fast(x, y), z...) min_fast(x::T, y::T, z::T...) where {T<:FloatTypes} = min_fast(min_fast(x, y), z...) end # fall-back implementations and type promotion for op in (:abs, :abs2, :conj, :inv, :sign) op_fast = fast_op[op] @eval begin # fall-back implementation for non-numeric types $op_fast(xs...) = $op(xs...) end end for op in (:+, :-, :*, :/, :(==), :!=, :<, :<=, :cmp, :rem, :min, :max, :minmax) op_fast = fast_op[op] @eval begin # fall-back implementation for non-numeric types $op_fast(xs...) = $op(xs...) # type promotion $op_fast(x::Number, y::Number, zs::Number...) = $op_fast(promote(x,y,zs...)...) # fall-back implementation that applies after promotion $op_fast(x::T,ys::T...) where {T<:Number} = $op(x,ys...) end end # Math functions exp2_fast(x::Union{Float32,Float64}) = Base.Math.exp2_fast(x) exp_fast(x::Union{Float32,Float64}) = Base.Math.exp_fast(x) exp10_fast(x::Union{Float32,Float64}) = Base.Math.exp10_fast(x) # builtins pow_fast(x::Float32, y::Integer) = ccall("llvm.powi.f32.i32", llvmcall, Float32, (Float32, Int32), x, y) pow_fast(x::Float64, y::Integer) = ccall("llvm.powi.f64.i32", llvmcall, Float64, (Float64, Int32), x, y) pow_fast(x::FloatTypes, ::Val{p}) where {p} = pow_fast(x, p) # inlines already via llvm.powi @inline pow_fast(x, v::Val) = Base.literal_pow(^, x, v) sqrt_fast(x::FloatTypes) = sqrt_llvm_fast(x) sincos_fast(v::FloatTypes) = sincos(v) @inline function sincos_fast(v::Float16) s, c = sincos_fast(Float32(v)) return Float16(s), Float16(c) end sincos_fast(v::AbstractFloat) = (sin_fast(v), cos_fast(v)) sincos_fast(v::Real) = sincos_fast(float(v)::AbstractFloat) sincos_fast(v) = (sin_fast(v), cos_fast(v)) function rem_fast(x::T, y::T) where {T<:FloatTypes} return @fastmath copysign(Base.rem_internal(abs(x), abs(y)), x) end @fastmath begin hypot_fast(x::T, y::T) where {T<:FloatTypes} = sqrt(x*x + y*y) # complex numbers function cis_fast(x::T) where {T<:FloatTypes} s, c = sincos_fast(x) Complex{T}(c, s) end # See pow_fast(x::T, y::T) where {T<:ComplexTypes} = exp(y*log(x)) pow_fast(x::T, y::Complex{T}) where {T<:FloatTypes} = exp(y*log(x)) pow_fast(x::Complex{T}, y::T) where {T<:FloatTypes} = exp(y*log(x)) acos_fast(x::T) where {T<:ComplexTypes} = convert(T,ฯ€)/2 + im*log(im*x + sqrt(1-x*x)) acosh_fast(x::ComplexTypes) = log(x + sqrt(x+1) * sqrt(x-1)) angle_fast(x::ComplexTypes) = atan(imag(x), real(x)) asin_fast(x::ComplexTypes) = -im*asinh(im*x) asinh_fast(x::ComplexTypes) = log(x + sqrt(1+x*x)) atan_fast(x::ComplexTypes) = -im*atanh(im*x) atanh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(log(1+x) - log(1-x)) cis_fast(x::ComplexTypes) = exp(-imag(x)) * cis(real(x)) cos_fast(x::ComplexTypes) = cosh(im*x) cosh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(exp(x) + exp(-x)) exp10_fast(x::T) where {T<:ComplexTypes} = exp10(real(x)) * cis(imag(x)*log(convert(T,10))) exp2_fast(x::T) where {T<:ComplexTypes} = exp2(real(x)) * cis(imag(x)*log(convert(T,2))) exp_fast(x::ComplexTypes) = exp(real(x)) * cis(imag(x)) expm1_fast(x::ComplexTypes) = exp(x)-1 log10_fast(x::T) where {T<:ComplexTypes} = log(x) / log(convert(T,10)) log1p_fast(x::ComplexTypes) = log(1+x) log2_fast(x::T) where {T<:ComplexTypes} = log(x) / log(convert(T,2)) log_fast(x::T) where {T<:ComplexTypes} = T(log(abs2(x))/2, angle(x)) log_fast(b::T, x::T) where {T<:ComplexTypes} = T(log(x)/log(b)) sin_fast(x::ComplexTypes) = -im*sinh(im*x) sinh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(exp(x) - exp(-x)) sqrt_fast(x::ComplexTypes) = sqrt(abs(x)) * cis(angle(x)/2) tan_fast(x::ComplexTypes) = -im*tanh(im*x) tanh_fast(x::ComplexTypes) = (a=exp(x); b=exp(-x); (a-b)/(a+b)) end # fall-back implementations and type promotion for f in (:acos, :acosh, :angle, :asin, :asinh, :atan, :atanh, :cbrt, :cis, :cos, :cosh, :exp10, :exp2, :exp, :expm1, :log10, :log1p, :log2, :log, :sin, :sinh, :sqrt, :tan, :tanh) f_fast = fast_op[f] @eval begin $f_fast(x) = $f(x) end end for f in (:^, :atan, :hypot, :log) f_fast = fast_op[f] @eval begin # fall-back implementation for non-numeric types $f_fast(x, y) = $f(x, y) # type promotion $f_fast(x::Number, y::Number) = $f_fast(promote(x, y)...) # fall-back implementation that applies after promotion $f_fast(x::T, y::T) where {T<:Number} = $f(x, y) end end # Reductions maximum_fast(a; kw...) = Base.reduce(max_fast, a; kw...) minimum_fast(a; kw...) = Base.reduce(min_fast, a; kw...) maximum_fast(f, a; kw...) = Base.mapreduce(f, max_fast, a; kw...) minimum_fast(f, a; kw...) = Base.mapreduce(f, min_fast, a; kw...) Base.reducedim_init(f, ::typeof(max_fast), A::AbstractArray, region) = Base.reducedim_init(f, max, A::AbstractArray, region) Base.reducedim_init(f, ::typeof(min_fast), A::AbstractArray, region) = Base.reducedim_init(f, min, A::AbstractArray, region) maximum!_fast(r::AbstractArray, A::AbstractArray; kw...) = maximum!_fast(identity, r, A; kw...) minimum!_fast(r::AbstractArray, A::AbstractArray; kw...) = minimum!_fast(identity, r, A; kw...) maximum!_fast(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = Base.mapreducedim!(f, max_fast, Base.initarray!(r, f, max, init, A), A) minimum!_fast(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = Base.mapreducedim!(f, min_fast, Base.initarray!(r, f, min, init, A), A) end L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/Enums.jlอ# This file is a part of Julia. License is MIT: https://julialang.org/license module Enums import Core.Intrinsics.bitcast export Enum, @enum function namemap end """ Enum{T<:Integer} The abstract supertype of all enumerated types defined with [`@enum`](@ref). """ abstract type Enum{T<:Integer} end basetype(::Type{<:Enum{T}}) where {T<:Integer} = T (::Type{T})(x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(bitcast(T2, x))::T Base.cconvert(::Type{T}, x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(x)::T Base.write(io::IO, x::Enum{T}) where {T<:Integer} = write(io, T(x)) Base.read(io::IO, ::Type{T}) where {T<:Enum} = T(read(io, basetype(T))) """ _enum_hash(x::Enum, h::UInt) Compute hash for an enum value `x`. This internal method will be specialized for every enum type created through [`@enum`](@ref). """ _enum_hash(x::Enum, h::UInt) = invoke(hash, Tuple{Any, UInt}, x, h) Base.hash(x::Enum, h::UInt) = _enum_hash(x, h) Base.isless(x::T, y::T) where {T<:Enum} = isless(basetype(T)(x), basetype(T)(y)) Base.Symbol(x::Enum) = namemap(typeof(x))[Integer(x)]::Symbol function _symbol(x::Enum) names = namemap(typeof(x)) x = Integer(x) get(() -> Symbol(""), names, x)::Symbol end Base.print(io::IO, x::Enum) = print(io, _symbol(x)) function Base.show(io::IO, x::Enum) sym = _symbol(x) if !(get(io, :compact, false)::Bool) from = get(io, :module, Base.active_module()) def = parentmodule(typeof(x)) if from === nothing || !Base.isvisible(sym, def, from) show(io, def) print(io, ".") end end print(io, sym) end function Base.show(io::IO, ::MIME"text/plain", x::Enum) print(io, x, "::") show(IOContext(io, :compact => true), typeof(x)) print(io, " = ") show(io, Integer(x)) end function Base.show(io::IO, m::MIME"text/plain", t::Type{<:Enum}) if isconcretetype(t) print(io, "Enum ") Base.show_datatype(io, t) print(io, ":") for x in instances(t) print(io, "\n", Symbol(x), " = ") show(io, Integer(x)) end else invoke(show, Tuple{IO, MIME"text/plain", Type}, io, m, t) end end # generate code to test whether expr is in the given set of values function membershiptest(expr, values) lo, hi = extrema(values) if length(values) == hi - lo + 1 :($lo <= $expr <= $hi) elseif length(values) < 20 foldl((x1,x2)->:($x1 || ($expr == $x2)), values[2:end]; init=:($expr == $(values[1]))) else :($expr in $(Set(values))) end end # give Enum types scalar behavior in broadcasting Base.broadcastable(x::Enum) = Ref(x) @noinline enum_argument_error(typename, x) = throw(ArgumentError(string("invalid value for Enum $(typename): $x"))) """ @enum EnumName[::BaseType] value1[=x] value2[=y] Create an `Enum{BaseType}` subtype with name `EnumName` and enum member values of `value1` and `value2` with optional assigned values of `x` and `y`, respectively. `EnumName` can be used just like other types and enum member values as regular values, such as # Examples ```jldoctest fruitenum julia> @enum Fruit apple=1 orange=2 kiwi=3 julia> f(x::Fruit) = "I'm a Fruit with value: \$(Int(x))" f (generic function with 1 method) julia> f(apple) "I'm a Fruit with value: 1" julia> Fruit(1) apple::Fruit = 1 ``` Values can also be specified inside a `begin` block, e.g. ```julia @enum EnumName begin value1 value2 end ``` `BaseType`, which defaults to [`Int32`](@ref), must be a primitive subtype of `Integer`. Member values can be converted between the enum type and `BaseType`. `read` and `write` perform these conversions automatically. In case the enum is created with a non-default `BaseType`, `Integer(value1)` will return the integer `value1` with the type `BaseType`. To list all the instances of an enum use `instances`, e.g. ```jldoctest fruitenum julia> instances(Fruit) (apple, orange, kiwi) ``` It is possible to construct a symbol from an enum instance: ```jldoctest fruitenum julia> Symbol(apple) :apple ``` """ macro enum(T::Union{Symbol,Expr}, syms...) if isempty(syms) throw(ArgumentError("no arguments given for Enum $T")) end basetype = Int32 typename = T if isa(T, Expr) && T.head === :(::) && length(T.args) == 2 && isa(T.args[1], Symbol) typename = T.args[1] basetype = Core.eval(__module__, T.args[2]) if !isa(basetype, DataType) || !(basetype <: Integer) || !isbitstype(basetype) throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an integer primitive type")) end elseif !isa(T, Symbol) throw(ArgumentError("invalid type expression for enum $T")) end values = Vector{basetype}() seen = Set{Symbol}() namemap = Dict{basetype,Symbol}() lo = hi = i = zero(basetype) hasexpr = false if length(syms) == 1 && syms[1] isa Expr && syms[1].head === :block syms = syms[1].args end for s in syms s isa LineNumberNode && continue if isa(s, Symbol) if i == typemin(basetype) && !isempty(values) throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) end elseif isa(s, Expr) && (s.head === :(=) || s.head === :kw) && length(s.args) == 2 && isa(s.args[1], Symbol) i = Core.eval(__module__, s.args[2]) # allow exprs, e.g. uint128"1" if !isa(i, Integer) throw(ArgumentError("invalid value for Enum $typename, $s; values must be integers")) end i = convert(basetype, i) s = s.args[1] hasexpr = true else throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) end s = s::Symbol if !Base.isidentifier(s) throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier")) end if hasexpr && haskey(namemap, i) throw(ArgumentError("both $s and $(namemap[i]) have value $i in Enum $typename; values must be unique")) end namemap[i] = s push!(values, i) if s in seen throw(ArgumentError("name \"$s\" in Enum $typename is not unique")) end push!(seen, s) if length(values) == 1 lo = hi = i else hi = max(hi, i) end i += oneunit(i) end blk = quote # enum definition Base.@__doc__(primitive type $(esc(typename)) <: Enum{$(basetype)} $(sizeof(basetype) * 8) end) function $(esc(typename))(x::Integer) $(membershiptest(:x, values)) || enum_argument_error($(Expr(:quote, typename)), x) return bitcast($(esc(typename)), convert($(basetype), x)) end Enums.namemap(::Type{$(esc(typename))}) = $(esc(namemap)) Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))($lo) Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($hi) let type_hash = hash($(esc(typename))) # Use internal `_enum_hash` to allow users to specialize # `Base.hash` for their own enum types without overwriting the # method we would define here. This avoids a warning for # precompilation. Enums._enum_hash(x::$(esc(typename)), h::UInt) = hash(type_hash, hash(Integer(x), h)) end let insts = (Any[ $(esc(typename))(v) for v in $values ]...,) Base.instances(::Type{$(esc(typename))}) = insts end end if isa(typename, Symbol) for (i, sym) in namemap push!(blk.args, :(const $(esc(sym)) = $(esc(typename))($i))) end end push!(blk.args, :nothing) blk.head = :toplevel return blk end end # module J/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/gmp.jlฤ•# This file is a part of Julia. License is MIT: https://julialang.org/license module GMP export BigInt import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, nand, nor, binomial, cmp, convert, div, divrem, factorial, cld, fld, gcd, gcdx, lcm, mod, ndigits, promote_rule, rem, show, isqrt, string, powermod, sum, prod, trailing_zeros, trailing_ones, count_ones, count_zeros, tryparse_internal, bin, oct, dec, hex, isequal, invmod, _prevpow2, _nextpow2, ndigits0zpb, widen, signed, unsafe_trunc, trunc, iszero, isone, big, flipsign, signbit, sign, hastypemax, isodd, iseven, digits!, hash, hash_integer, top_set_bit if Clong == Int32 const ClongMax = Union{Int8, Int16, Int32} const CulongMax = Union{UInt8, UInt16, UInt32} else const ClongMax = Union{Int8, Int16, Int32, Int64} const CulongMax = Union{UInt8, UInt16, UInt32, UInt64} end const CdoubleMax = Union{Float16, Float32, Float64} if Sys.iswindows() const libgmp = "libgmp-10.dll" elseif Sys.isapple() const libgmp = "@rpath/libgmp.10.dylib" else const libgmp = "libgmp.so.10" end version() = VersionNumber(unsafe_string(unsafe_load(cglobal((:__gmp_version, libgmp), Ptr{Cchar})))) bits_per_limb() = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, libgmp), Cint))) const VERSION = version() const BITS_PER_LIMB = bits_per_limb() # GMP's mp_limb_t is by default a typedef of `unsigned long`, but can also be configured to be either # `unsigned int` or `unsigned long long int`. The correct unsigned type is here named Limb, and must # be used whenever mp_limb_t is in the signature of ccall'ed GMP functions. if BITS_PER_LIMB == 32 const Limb = UInt32 const SLimbMax = Union{Int8, Int16, Int32} const ULimbMax = Union{UInt8, UInt16, UInt32} elseif BITS_PER_LIMB == 64 const Limb = UInt64 const SLimbMax = Union{Int8, Int16, Int32, Int64} const ULimbMax = Union{UInt8, UInt16, UInt32, UInt64} else error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $BITS_PER_LIMB)") end """ BigInt <: Signed Arbitrary precision integer type. """ mutable struct BigInt <: Signed alloc::Cint size::Cint d::Ptr{Limb} function BigInt(; nbits::Integer=0) b = MPZ.init2!(new(), nbits) finalizer(cglobal((:__gmpz_clear, libgmp)), b) return b end end """ BigInt(x) Create an arbitrary precision integer. `x` may be an `Int` (or anything that can be converted to an `Int`). The usual mathematical operators are defined for this type, and results are promoted to a [`BigInt`](@ref). Instances can be constructed from strings via [`parse`](@ref), or using the `big` string literal. # Examples ```jldoctest julia> parse(BigInt, "42") 42 julia> big"313" 313 julia> BigInt(10)^19 10000000000000000000 ``` """ BigInt(x) """ ALLOC_OVERFLOW_FUNCTION A reference that holds a boolean, if true, indicating julia is linked with a patched GMP that does not abort on huge allocation and throws OutOfMemoryError instead. """ const ALLOC_OVERFLOW_FUNCTION = Ref(false) function __init__() try if version().major != VERSION.major || bits_per_limb() != BITS_PER_LIMB msg = """The dynamically loaded GMP library (v\"$(version())\" with __gmp_bits_per_limb == $(bits_per_limb())) does not correspond to the compile time version (v\"$VERSION\" with __gmp_bits_per_limb == $BITS_PER_LIMB). Please rebuild Julia.""" bits_per_limb() != BITS_PER_LIMB ? @error(msg) : @warn(msg) end ccall((:__gmp_set_memory_functions, libgmp), Cvoid, (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}), cglobal(:jl_gc_counted_malloc), cglobal(:jl_gc_counted_realloc_with_old_size), cglobal(:jl_gc_counted_free_with_size)) ZERO.alloc, ZERO.size, ZERO.d = 0, 0, C_NULL ONE.alloc, ONE.size, ONE.d = 1, 1, pointer(_ONE) catch ex Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") end # This only works with a patched version of GMP, ignore otherwise try ccall((:__gmp_set_alloc_overflow_function, libgmp), Cvoid, (Ptr{Cvoid},), cglobal(:jl_throw_out_of_memory_error)) ALLOC_OVERFLOW_FUNCTION[] = true catch ex # ErrorException("ccall: could not find function...") if typeof(ex) != ErrorException rethrow() end end end module MPZ # wrapping of libgmp functions # - "output parameters" are labeled x, y, z, and are returned when appropriate # - constant input parameters are labeled a, b, c # - a method modifying its input has a "!" appended to its name, according to Julia's conventions # - some convenient methods are added (in addition to the pure MPZ ones), e.g. `add(a, b) = add!(BigInt(), a, b)` # and `add!(x, a) = add!(x, x, a)`. using ..GMP: BigInt, Limb, BITS_PER_LIMB, libgmp const mpz_t = Ref{BigInt} const bitcnt_t = Culong gmpz(op::Symbol) = (Symbol(:__gmpz_, op), libgmp) init!(x::BigInt) = (ccall((:__gmpz_init, libgmp), Cvoid, (mpz_t,), x); x) init2!(x::BigInt, a) = (ccall((:__gmpz_init2, libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) realloc2!(x, a) = (ccall((:__gmpz_realloc2, libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) realloc2(a) = realloc2!(BigInt(), a) sizeinbase(a::BigInt, b) = Int(ccall((:__gmpz_sizeinbase, libgmp), Csize_t, (mpz_t, Cint), a, b)) for (op, nbits) in (:add => :(BITS_PER_LIMB*(1 + max(abs(a.size), abs(b.size)))), :sub => :(BITS_PER_LIMB*(1 + max(abs(a.size), abs(b.size)))), :mul => 0, :fdiv_q => 0, :tdiv_q => 0, :cdiv_q => 0, :fdiv_r => 0, :tdiv_r => 0, :cdiv_r => 0, :gcd => 0, :lcm => 0, :and => 0, :ior => 0, :xor => 0) op! = Symbol(op, :!) @eval begin $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t, mpz_t), x, a, b); x) $op(a::BigInt, b::BigInt) = $op!(BigInt(nbits=$nbits), a, b) $op!(x::BigInt, b::BigInt) = $op!(x, x, b) end end invert!(x::BigInt, a::BigInt, b::BigInt) = ccall((:__gmpz_invert, libgmp), Cint, (mpz_t, mpz_t, mpz_t), x, a, b) invert(a::BigInt, b::BigInt) = invert!(BigInt(), a, b) invert!(x::BigInt, b::BigInt) = invert!(x, x, b) for op in (:add_ui, :sub_ui, :mul_ui, :mul_2exp, :fdiv_q_2exp, :pow_ui, :bin_ui) op! = Symbol(op, :!) @eval begin $op!(x::BigInt, a::BigInt, b) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t, Culong), x, a, b); x) $op(a::BigInt, b) = $op!(BigInt(), a, b) $op!(x::BigInt, b) = $op!(x, x, b) end end ui_sub!(x::BigInt, a, b::BigInt) = (ccall((:__gmpz_ui_sub, libgmp), Cvoid, (mpz_t, Culong, mpz_t), x, a, b); x) ui_sub(a, b::BigInt) = ui_sub!(BigInt(), a, b) for op in (:scan1, :scan0) # when there is no meaningful answer, ccall returns typemax(Culong), where Culong can # be UInt32 (Windows) or UInt64; we return -1 in this case for all architectures @eval $op(a::BigInt, b) = Int(signed(ccall($(gmpz(op)), Culong, (mpz_t, Culong), a, b))) end mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, libgmp), Cvoid, (mpz_t, mpz_t, Clong), x, a, b); x) mul_si(a::BigInt, b) = mul_si!(BigInt(), a, b) mul_si!(x::BigInt, b) = mul_si!(x, x, b) for op in (:neg, :com, :sqrt, :set) op! = Symbol(op, :!) @eval begin $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t), x, a); x) $op(a::BigInt) = $op!(BigInt(), a) end op === :set && continue # MPZ.set!(x) would make no sense @eval $op!(x::BigInt) = $op!(x, x) end for (op, T) in ((:fac_ui, Culong), (:set_ui, Culong), (:set_si, Clong), (:set_d, Cdouble)) op! = Symbol(op, :!) @eval begin $op!(x::BigInt, a) = (ccall($(gmpz(op)), Cvoid, (mpz_t, $T), x, a); x) $op(a) = $op!(BigInt(), a) end end popcount(a::BigInt) = Int(signed(ccall((:__gmpz_popcount, libgmp), Culong, (mpz_t,), a))) mpn_popcount(d::Ptr{Limb}, s::Integer) = Int(ccall((:__gmpn_popcount, libgmp), Culong, (Ptr{Limb}, Csize_t), d, s)) mpn_popcount(a::BigInt) = mpn_popcount(a.d, abs(a.size)) function tdiv_qr!(x::BigInt, y::BigInt, a::BigInt, b::BigInt) ccall((:__gmpz_tdiv_qr, libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t), x, y, a, b) x, y end tdiv_qr(a::BigInt, b::BigInt) = tdiv_qr!(BigInt(), BigInt(), a, b) powm!(x::BigInt, a::BigInt, b::BigInt, c::BigInt) = (ccall((:__gmpz_powm, libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t), x, a, b, c); x) powm(a::BigInt, b::BigInt, c::BigInt) = powm!(BigInt(), a, b, c) powm!(x::BigInt, b::BigInt, c::BigInt) = powm!(x, x, b, c) function gcdext!(x::BigInt, y::BigInt, z::BigInt, a::BigInt, b::BigInt) ccall((:__gmpz_gcdext, libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t, mpz_t), x, y, z, a, b) x, y, z end gcdext(a::BigInt, b::BigInt) = gcdext!(BigInt(), BigInt(), BigInt(), a, b) cmp(a::BigInt, b::BigInt) = Int(ccall((:__gmpz_cmp, libgmp), Cint, (mpz_t, mpz_t), a, b)) cmp_si(a::BigInt, b) = Int(ccall((:__gmpz_cmp_si, libgmp), Cint, (mpz_t, Clong), a, b)) cmp_ui(a::BigInt, b) = Int(ccall((:__gmpz_cmp_ui, libgmp), Cint, (mpz_t, Culong), a, b)) cmp_d(a::BigInt, b) = Int(ccall((:__gmpz_cmp_d, libgmp), Cint, (mpz_t, Cdouble), a, b)) mpn_cmp(a::Ptr{Limb}, b::Ptr{Limb}, c) = ccall((:__gmpn_cmp, libgmp), Cint, (Ptr{Limb}, Ptr{Limb}, Clong), a, b, c) mpn_cmp(a::BigInt, b::BigInt, c) = mpn_cmp(a.d, b.d, c) get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,libgmp), Ptr{Cchar}, (Ptr{Cchar}, Cint, mpz_t), x, a, b); x) set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), x, a, b)) get_d(a::BigInt) = ccall((:__gmpz_get_d, libgmp), Cdouble, (mpz_t,), a) function export!(a::AbstractVector{T}, n::BigInt; order::Integer=-1, nails::Integer=0, endian::Integer=0) where {T<:Base.BitInteger} stride(a, 1) == 1 || throw(ArgumentError("a must have stride 1")) ndigits = cld(sizeinbase(n, 2), 8*sizeof(T) - nails) length(a) < ndigits && resize!(a, ndigits) count = Ref{Csize_t}() ccall((:__gmpz_export, libgmp), Ptr{T}, (Ptr{T}, Ref{Csize_t}, Cint, Csize_t, Cint, Csize_t, mpz_t), a, count, order, sizeof(T), endian, nails, n) @assert count[] โ‰ค length(a) return a, Int(count[]) end limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, libgmp), Ptr{Limb}, (mpz_t, Clong), x, a) limbs_finish!(x::BigInt, a) = ccall((:__gmpz_limbs_finish, libgmp), Cvoid, (mpz_t, Clong), x, a) import!(x::BigInt, a, b, c, d, e, f) = ccall((:__gmpz_import, libgmp), Cvoid, (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Cvoid}), x, a, b, c, d, e, f) setbit!(x, a) = (ccall((:__gmpz_setbit, libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) tstbit(a::BigInt, b) = ccall((:__gmpz_tstbit, libgmp), Cint, (mpz_t, bitcnt_t), a, b) % Bool end # module MPZ const ZERO = BigInt() const ONE = BigInt() const _ONE = Limb[1] widen(::Type{Int128}) = BigInt widen(::Type{UInt128}) = BigInt widen(::Type{BigInt}) = BigInt signed(x::BigInt) = x BigInt(x::BigInt) = x Signed(x::BigInt) = x hastypemax(::Type{BigInt}) = false function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) # don't make a copy in the common case where we are parsing a whole String bstr = startpos == firstindex(s) && endpos == lastindex(s) ? String(s) : String(SubString(s,startpos,endpos)) sgn, base, i = Base.parseint_preamble(true,Int(base_),bstr,firstindex(bstr),lastindex(bstr)) if !(2 <= base <= 62) raise && throw(ArgumentError("invalid base: base must be 2 โ‰ค base โ‰ค 62, got $base")) return nothing end if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(bstr))")) return nothing end z = BigInt() if Base.containsnul(bstr) err = -1 # embedded NUL char (not handled correctly by GMP) else err = GC.@preserve bstr MPZ.set_str!(z, pointer(bstr)+(i-firstindex(bstr)), base) end if err != 0 raise && throw(ArgumentError("invalid BigInt: $(repr(bstr))")) return nothing end flipsign!(z, sgn) end BigInt(x::Union{Clong,Int32}) = MPZ.set_si(x) BigInt(x::Union{Culong,UInt32}) = MPZ.set_ui(x) BigInt(x::Bool) = BigInt(UInt(x)) unsafe_trunc(::Type{BigInt}, x::Union{Float16,Float32,Float64}) = MPZ.set_d(x) function BigInt(x::Float64) isinteger(x) || throw(InexactError(:BigInt, BigInt, x)) unsafe_trunc(BigInt,x) end function trunc(::Type{BigInt}, x::Union{Float16,Float32,Float64}) isfinite(x) || throw(InexactError(:trunc, BigInt, x)) unsafe_trunc(BigInt,x) end BigInt(x::Float16) = BigInt(Float64(x)) BigInt(x::Float32) = BigInt(Float64(x)) function BigInt(x::Integer) # On 64-bit Windows, `Clong` is `Int32`, not `Int64`, so construction of # `Int64` constants, e.g. `BigInt(3)`, uses this method. isbits(x) && typemin(Clong) <= x <= typemax(Clong) && return BigInt((x % Clong)::Clong) nd = ndigits(x, base=2) z = MPZ.realloc2(nd) ux = unsigned(x < 0 ? -x : x) size = 0 limbnbits = sizeof(Limb) << 3 while nd > 0 size += 1 unsafe_store!(z.d, ux % Limb, size) ux >>= limbnbits nd -= limbnbits end z.size = x < 0 ? -size : size z end rem(x::BigInt, ::Type{Bool}) = !iszero(x) & unsafe_load(x.d) % Bool # never unsafe here rem(x::BigInt, ::Type{T}) where T<:Union{SLimbMax,ULimbMax} = iszero(x) ? zero(T) : flipsign(unsafe_load(x.d) % T, x.size) function rem(x::BigInt, ::Type{T}) where T<:Union{Base.BitUnsigned,Base.BitSigned} u = zero(T) for l = 1:min(abs(x.size), cld(sizeof(T), sizeof(Limb))) u += (unsafe_load(x.d, l) % T) << ((sizeof(Limb)<<3)*(l-1)) end flipsign(u, x.size) end rem(x::Integer, ::Type{BigInt}) = BigInt(x) isodd(x::BigInt) = MPZ.tstbit(x, 0) iseven(x::BigInt) = !isodd(x) function (::Type{T})(x::BigInt) where T<:Base.BitUnsigned if sizeof(T) < sizeof(Limb) convert(T, convert(Limb,x)) else 0 <= x.size <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError(nameof(T), T, x)) x % T end end function (::Type{T})(x::BigInt) where T<:Base.BitSigned n = abs(x.size) if sizeof(T) < sizeof(Limb) SLimb = typeof(Signed(one(Limb))) convert(T, convert(SLimb, x)) else 0 <= n <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError(nameof(T), T, x)) y = x % T ispos(x) โŠป (y > 0) && throw(InexactError(nameof(T), T, x)) # catch overflow y end end Float64(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) function (::Type{T})(n::BigInt, ::RoundingMode{:ToZero}) where T<:Union{Float16,Float32} T(Float64(n,RoundToZero),RoundToZero) end function (::Type{T})(n::BigInt, ::RoundingMode{:Down}) where T<:CdoubleMax x = T(n,RoundToZero) x > n ? prevfloat(x) : x end function (::Type{T})(n::BigInt, ::RoundingMode{:Up}) where T<:CdoubleMax x = T(n,RoundToZero) x < n ? nextfloat(x) : x end function Float64(x::BigInt, ::RoundingMode{:Nearest}) x == 0 && return 0.0 xsize = abs(x.size) if xsize*BITS_PER_LIMB > 1024 z = Inf64 elseif xsize == 1 z = Float64(unsafe_load(x.d)) elseif Limb == UInt32 && xsize == 2 z = Float64((unsafe_load(x.d, 2) % UInt64) << BITS_PER_LIMB + unsafe_load(x.d)) else y1 = unsafe_load(x.d, xsize) % UInt64 n = top_set_bit(y1) # load first 54(1 + 52 bits of fraction + 1 for rounding) y = y1 >> (n - (precision(Float64)+1)) if Limb == UInt64 y += n > precision(Float64) ? 0 : (unsafe_load(x.d, xsize-1) >> (10+n)) else y += (unsafe_load(x.d, xsize-1) % UInt64) >> (n-22) y += n > (precision(Float64) - 32) ? 0 : (unsafe_load(x.d, xsize-2) >> (10+n)) end y = (y + 1) >> 1 # round, ties up y &= ~UInt64(trailing_zeros(x) == (n-54 + (xsize-1)*BITS_PER_LIMB)) # fix last bit to round to even d = ((n+1021) % UInt64) << 52 z = reinterpret(Float64, d+y) z = ldexp(z, (xsize-1)*BITS_PER_LIMB) end return flipsign(z, x.size) end function Float32(x::BigInt, ::RoundingMode{:Nearest}) x == 0 && return 0f0 xsize = abs(x.size) if xsize*BITS_PER_LIMB > 128 z = Inf32 elseif xsize == 1 z = Float32(unsafe_load(x.d)) else y1 = unsafe_load(x.d, xsize) n = BITS_PER_LIMB - leading_zeros(y1) # load first 25(1 + 23 bits of fraction + 1 for rounding) y = (y1 >> (n - (precision(Float32)+1))) % UInt32 y += (n > precision(Float32) ? 0 : unsafe_load(x.d, xsize-1) >> (BITS_PER_LIMB - (25-n))) % UInt32 y = (y + one(UInt32)) >> 1 # round, ties up y &= ~UInt32(trailing_zeros(x) == (n-25 + (xsize-1)*BITS_PER_LIMB)) # fix last bit to round to even d = ((n+125) % UInt32) << 23 z = reinterpret(Float32, d+y) z = ldexp(z, (xsize-1)*BITS_PER_LIMB) end return flipsign(z, x.size) end function Float16(x::BigInt, ::RoundingMode{:Nearest}) x == 0 && return Float16(0.0) y1 = unsafe_load(x.d) n = BITS_PER_LIMB - leading_zeros(y1) if n > 16 || abs(x.size) > 1 z = Inf16 else # load first 12(1 + 10 bits for fraction + 1 for rounding) y = (y1 >> (n - (precision(Float16)+1))) % UInt16 y = (y + one(UInt16)) >> 1 # round, ties up y &= ~UInt16(trailing_zeros(x) == (n-12)) # fix last bit to round to even d = ((n+13) % UInt16) << 10 z = reinterpret(Float16, d+y) end return flipsign(z, x.size) end Float64(n::BigInt) = Float64(n, RoundNearest) Float32(n::BigInt) = Float32(n, RoundNearest) Float16(n::BigInt) = Float16(n, RoundNearest) promote_rule(::Type{BigInt}, ::Type{<:Integer}) = BigInt """ big(x) Convert a number to a maximum precision representation (typically [`BigInt`](@ref) or `BigFloat`). See [`BigFloat`](@ref BigFloat(::Any, rounding::RoundingMode)) for information about some pitfalls with floating-point numbers. """ function big end big(::Type{<:Integer}) = BigInt big(::Type{<:Rational}) = Rational{BigInt} big(n::Integer) = convert(BigInt, n) # Binary ops for (fJ, fC) in ((:+, :add), (:-,:sub), (:*, :mul), (:mod, :fdiv_r), (:rem, :tdiv_r), (:gcd, :gcd), (:lcm, :lcm), (:&, :and), (:|, :ior), (:xor, :xor)) @eval begin ($fJ)(x::BigInt, y::BigInt) = MPZ.$fC(x, y) end end for (r, f) in ((RoundToZero, :tdiv_q), (RoundDown, :fdiv_q), (RoundUp, :cdiv_q)) @eval div(x::BigInt, y::BigInt, ::typeof($r)) = MPZ.$f(x, y) end # For compat only. Remove in 2.0. div(x::BigInt, y::BigInt) = div(x, y, RoundToZero) fld(x::BigInt, y::BigInt) = div(x, y, RoundDown) cld(x::BigInt, y::BigInt) = div(x, y, RoundUp) /(x::BigInt, y::BigInt) = float(x)/float(y) function invmod(x::BigInt, y::BigInt) z = zero(BigInt) ya = abs(y) if ya == 1 return z end if (y==0 || MPZ.invert!(z, x, ya) == 0) throw(DomainError(y)) end # GMP always returns a positive inverse; we instead want to # normalize such that div(z, y) == 0, i.e. we want a negative z # when y is negative. if y < 0 MPZ.add!(z, y) end # The postcondition is: mod(z * x, y) == mod(big(1), m) && div(z, y) == 0 return z end # More efficient commutative operations for (fJ, fC) in ((:+, :add), (:*, :mul), (:&, :and), (:|, :ior), (:xor, :xor)) fC! = Symbol(fC, :!) @eval begin ($fJ)(a::BigInt, b::BigInt, c::BigInt) = MPZ.$fC!(MPZ.$fC(a, b), c) ($fJ)(a::BigInt, b::BigInt, c::BigInt, d::BigInt) = MPZ.$fC!(MPZ.$fC!(MPZ.$fC(a, b), c), d) ($fJ)(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) = MPZ.$fC!(MPZ.$fC!(MPZ.$fC!(MPZ.$fC(a, b), c), d), e) end end # Basic arithmetic without promotion +(x::BigInt, c::CulongMax) = MPZ.add_ui(x, c) +(c::CulongMax, x::BigInt) = x + c -(x::BigInt, c::CulongMax) = MPZ.sub_ui(x, c) -(c::CulongMax, x::BigInt) = MPZ.ui_sub(c, x) +(x::BigInt, c::ClongMax) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) +(c::ClongMax, x::BigInt) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) -(x::BigInt, c::ClongMax) = c < 0 ? +(x, -(c % Culong)) : -(x, convert(Culong, c)) -(c::ClongMax, x::BigInt) = c < 0 ? -(x + -(c % Culong)) : -(convert(Culong, c), x) *(x::BigInt, c::CulongMax) = MPZ.mul_ui(x, c) *(c::CulongMax, x::BigInt) = x * c *(x::BigInt, c::ClongMax) = MPZ.mul_si(x, c) *(c::ClongMax, x::BigInt) = x * c /(x::BigInt, y::Union{ClongMax,CulongMax}) = float(x)/y /(x::Union{ClongMax,CulongMax}, y::BigInt) = x/float(y) # unary ops (-)(x::BigInt) = MPZ.neg(x) (~)(x::BigInt) = MPZ.com(x) <<(x::BigInt, c::UInt) = c == 0 ? x : MPZ.mul_2exp(x, c) >>(x::BigInt, c::UInt) = c == 0 ? x : MPZ.fdiv_q_2exp(x, c) >>>(x::BigInt, c::UInt) = x >> c function trailing_zeros(x::BigInt) c = MPZ.scan1(x, 0) c == -1 && throw(DomainError(x, "`x` must be non-zero")) c end function trailing_ones(x::BigInt) c = MPZ.scan0(x, 0) c == -1 && throw(DomainError(x, "`x` must not be equal to -1")) c end function count_ones(x::BigInt) c = MPZ.popcount(x) c == -1 && throw(DomainError(x, "`x` cannot be negative")) c end # generic definition is not used to provide a better error message function count_zeros(x::BigInt) c = MPZ.popcount(~x) c == -1 && throw(DomainError(x, "`x` must be negative")) c end """ count_ones_abs(x::BigInt) Number of ones in the binary representation of abs(x). """ count_ones_abs(x::BigInt) = iszero(x) ? 0 : MPZ.mpn_popcount(x) function top_set_bit(x::BigInt) isneg(x) && throw(DomainError(x, "top_set_bit only supports negative arguments when they have type BitSigned.")) iszero(x) && return 0 x.size * sizeof(Limb) << 3 - leading_zeros(GC.@preserve x unsafe_load(x.d, x.size)) end divrem(x::BigInt, y::BigInt) = MPZ.tdiv_qr(x, y) divrem(x::BigInt, y::Integer) = MPZ.tdiv_qr(x, big(y)) cmp(x::BigInt, y::BigInt) = sign(MPZ.cmp(x, y)) cmp(x::BigInt, y::ClongMax) = sign(MPZ.cmp_si(x, y)) cmp(x::BigInt, y::CulongMax) = sign(MPZ.cmp_ui(x, y)) cmp(x::BigInt, y::Integer) = cmp(x, big(y)) cmp(x::Integer, y::BigInt) = -cmp(y, x) cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? -1 : sign(MPZ.cmp_d(x, y)) cmp(x::CdoubleMax, y::BigInt) = -cmp(y, x) isqrt(x::BigInt) = MPZ.sqrt(x) ^(x::BigInt, y::Culong) = MPZ.pow_ui(x, y) function bigint_pow(x::BigInt, y::Integer) if y<0; throw(DomainError(y, "`y` cannot be negative.")); end @noinline throw1(y) = throw(OverflowError("exponent $y is too large and computation will overflow")) if x== 1; return x; end if x==-1; return isodd(y) ? x : -x; end if y>typemax(Culong) x==0 && return x #At this point, x is not 1, 0 or -1 and it is not possible to use #gmpz_pow_ui to compute the answer. Note that the magnitude of the #answer is: #- at least 2^(2^32-1) โ‰ˆ 10^(1.3e9) (if Culong === UInt32). #- at least 2^(2^64-1) โ‰ˆ 10^(5.5e18) (if Culong === UInt64). # #Assume that the answer will definitely overflow. throw1(y) end return x^convert(Culong, y) end ^(x::BigInt , y::BigInt ) = bigint_pow(x, y) ^(x::BigInt , y::Bool ) = y ? x : one(x) ^(x::BigInt , y::Integer) = bigint_pow(x, y) ^(x::Integer, y::BigInt ) = bigint_pow(BigInt(x), y) ^(x::Bool , y::BigInt ) = Base.power_by_squaring(x, y) function powermod(x::BigInt, p::BigInt, m::BigInt) r = MPZ.powm(x, p, m) return m < 0 && r > 0 ? MPZ.add!(r, m) : r # choose sign consistent with mod(x^p, m) end powermod(x::Integer, p::Integer, m::BigInt) = powermod(big(x), big(p), m) function gcdx(a::BigInt, b::BigInt) if iszero(b) # shortcut this to ensure consistent results with gcdx(a,b) return a < 0 ? (-a,-ONE,b) : (a,one(BigInt),b) # we don't return the globals ONE and ZERO in case the user wants to # mutate the result end g, s, t = MPZ.gcdext(a, b) if t == 0 # work around a difference in some versions of GMP if a == b return g, t, s elseif abs(a)==abs(b) return g, t, -s end end g, s, t end +(x::BigInt, y::BigInt, rest::BigInt...) = sum(tuple(x, y, rest...)) sum(arr::Union{AbstractArray{BigInt}, Tuple{BigInt, Vararg{BigInt}}}) = foldl(MPZ.add!, arr; init=BigInt(0)) function prod(arr::AbstractArray{BigInt}) # compute first the needed number of bits for the result, # to avoid re-allocations; # GMP will always request n+m limbs for the result in MPZ.mul!, # if the arguments have n and m limbs; so we add all the bits # taken by the array elements, and add BITS_PER_LIMB to that, # to account for the rounding to limbs in MPZ.mul! # (BITS_PER_LIMB-1 would typically be enough, to which we add # 1 for the initial multiplication by init=1 in foldl) nbits = BITS_PER_LIMB for x in arr iszero(x) && return zero(BigInt) xsize = abs(x.size) lz = GC.@preserve x leading_zeros(unsafe_load(x.d, xsize)) nbits += xsize * BITS_PER_LIMB - lz end init = BigInt(; nbits) MPZ.set_si!(init, 1) foldl(MPZ.mul!, arr; init) end factorial(x::BigInt) = isneg(x) ? BigInt(0) : MPZ.fac_ui(x) function binomial(n::BigInt, k::Integer) k < 0 && return BigInt(0) k <= typemax(Culong) && return binomial(n, Culong(k)) n < 0 && return isodd(k) ? -binomial(k - n - 1, k) : binomial(k - n - 1, k) ฮบ = n - k ฮบ < 0 && return BigInt(0) ฮบ <= typemax(Culong) && return binomial(n, Culong(ฮบ)) throw(OverflowError("Computation would exceed memory")) end binomial(n::BigInt, k::Culong) = MPZ.bin_ui(n, k) ==(x::BigInt, y::BigInt) = cmp(x,y) == 0 ==(x::BigInt, i::Integer) = cmp(x,i) == 0 ==(i::Integer, x::BigInt) = cmp(x,i) == 0 ==(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) == 0 ==(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) == 0 iszero(x::BigInt) = x.size == 0 isone(x::BigInt) = x == Culong(1) <=(x::BigInt, y::BigInt) = cmp(x,y) <= 0 <=(x::BigInt, i::Integer) = cmp(x,i) <= 0 <=(i::Integer, x::BigInt) = cmp(x,i) >= 0 <=(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) <= 0 <=(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) >= 0 <(x::BigInt, y::BigInt) = cmp(x,y) < 0 <(x::BigInt, i::Integer) = cmp(x,i) < 0 <(i::Integer, x::BigInt) = cmp(x,i) > 0 <(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) < 0 <(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) > 0 isneg(x::BigInt) = x.size < 0 ispos(x::BigInt) = x.size > 0 signbit(x::BigInt) = isneg(x) flipsign!(x::BigInt, y::Integer) = (signbit(y) && (x.size = -x.size); x) flipsign( x::BigInt, y::Integer) = signbit(y) ? -x : x flipsign( x::BigInt, y::BigInt) = signbit(y) ? -x : x # above method to resolving ambiguities with flipsign(::T, ::T) where T<:Signed function sign(x::BigInt) isneg(x) && return -one(x) ispos(x) && return one(x) return x end show(io::IO, x::BigInt) = print(io, string(x)) function string(n::BigInt; base::Integer = 10, pad::Integer = 1) base < 0 && return Base._base(Int(base), n, pad, (base>0) & (n.size<0)) 2 <= base <= 62 || throw(ArgumentError("base must be 2 โ‰ค base โ‰ค 62, got $base")) iszero(n) && pad < 1 && return "" nd1 = ndigits(n, base=base) nd = max(nd1, pad) sv = Base.StringVector(nd + isneg(n)) GC.@preserve sv MPZ.get_str!(pointer(sv) + nd - nd1, base, n) @inbounds for i = (1:nd-nd1) .+ isneg(n) sv[i] = '0' % UInt8 end isneg(n) && (sv[1] = '-' % UInt8) String(sv) end function digits!(a::AbstractVector{T}, n::BigInt; base::Integer = 10) where {T<:Integer} if base โ‰ฅ 2 if base โ‰ค 62 # fast path using mpz_get_str via string(n; base) s = codeunits(string(n; base)) i, j = firstindex(a)-1, length(s)+1 lasti = min(lastindex(a), firstindex(a) + length(s)-1 - isneg(n)) while i < lasti # base โ‰ค 36: 0-9, plus a-z for 10-35 # base > 36: 0-9, plus A-Z for 10-35 and a-z for 36..61 x = s[j -= 1] a[i += 1] = base โ‰ค 36 ? (x>0x39 ? x-0x57 : x-0x30) : (x>0x39 ? (x>0x60 ? x-0x3d : x-0x37) : x-0x30) end lasti = lastindex(a) while i < lasti; a[i+=1] = zero(T); end return isneg(n) ? map!(-,a,a) : a elseif a isa StridedVector{<:Base.BitInteger} && stride(a,1) == 1 && ispow2(base) && base-1 โ‰ค typemax(T) # fast path using mpz_export origlen = length(a) _, writelen = MPZ.export!(a, n; nails = 8sizeof(T) - trailing_zeros(base)) length(a) != origlen && resize!(a, origlen) # truncate to least-significant digits a[begin+writelen:end] .= zero(T) return isneg(n) ? map!(-,a,a) : a end end return invoke(digits!, Tuple{typeof(a), Integer}, a, n; base) # slow generic fallback end function ndigits0zpb(x::BigInt, b::Integer) b < 2 && throw(DomainError(b, "`b` cannot be less than 2.")) x.size == 0 && return 0 # for consistency with other ndigits0z methods if ispow2(b) && 2 <= b <= 62 # GMP assumes b is in this range MPZ.sizeinbase(x, b) else # non-base 2 mpz_sizeinbase might return an answer 1 too big # use property that log(b, x) < ndigits(x, base=b) <= log(b, x) + 1 n = MPZ.sizeinbase(x, 2) lb = log2(b) # assumed accurate to <1ulp (true for openlibm) q,r = divrem(n,lb) iq = Int(q) maxerr = q*eps(lb) # maximum error in remainder if r-1.0 < maxerr abs(x) >= big(b)^iq ? iq+1 : iq elseif lb-r < maxerr abs(x) >= big(b)^(iq+1) ? iq+2 : iq+1 else iq+1 end end end # Fast paths for nextpow(2, x::BigInt) # below, ONE is always left-shifted by at least one digit, so a new BigInt is # allocated, which can be safely mutated _prevpow2(x::BigInt) = -2 <= x <= 2 ? x : flipsign!(ONE << (ndigits(x, base=2) - 1), x) _nextpow2(x::BigInt) = count_ones_abs(x) <= 1 ? x : flipsign!(ONE << ndigits(x, base=2), x) Base.checked_abs(x::BigInt) = abs(x) Base.checked_neg(x::BigInt) = -x Base.checked_add(a::BigInt, b::BigInt) = a + b Base.checked_sub(a::BigInt, b::BigInt) = a - b Base.checked_mul(a::BigInt, b::BigInt) = a * b Base.checked_div(a::BigInt, b::BigInt) = div(a, b) Base.checked_rem(a::BigInt, b::BigInt) = rem(a, b) Base.checked_fld(a::BigInt, b::BigInt) = fld(a, b) Base.checked_mod(a::BigInt, b::BigInt) = mod(a, b) Base.checked_cld(a::BigInt, b::BigInt) = cld(a, b) Base.add_with_overflow(a::BigInt, b::BigInt) = a + b, false Base.sub_with_overflow(a::BigInt, b::BigInt) = a - b, false Base.mul_with_overflow(a::BigInt, b::BigInt) = a * b, false Base.deepcopy_internal(x::BigInt, stackdict::IdDict) = get!(() -> MPZ.set(x), stackdict, x) ## streamlined hashing for BigInt, by avoiding allocation from shifts ## if Limb === UInt64 === UInt # On 64 bit systems we can define # an optimized version for BigInt of hash_integer (used e.g. for Rational{BigInt}), # and of hash using .Base: hash_uint function hash_integer(n::BigInt, h::UInt) GC.@preserve n begin s = n.size s == 0 && return hash_integer(0, h) p = convert(Ptr{UInt64}, n.d) b = unsafe_load(p) h โŠป= hash_uint(ifelse(s < 0, -b, b) โŠป h) for k = 2:abs(s) h โŠป= hash_uint(unsafe_load(p, k) โŠป h) end return h end end function hash(x::BigInt, h::UInt) GC.@preserve x begin sz = x.size sz == 0 && return hash(0, h) ptr = Ptr{UInt64}(x.d) if sz == 1 return hash(unsafe_load(ptr), h) elseif sz == -1 limb = unsafe_load(ptr) limb <= typemin(Int) % UInt && return hash(-(limb % Int), h) end pow = trailing_zeros(x) nd = Base.ndigits0z(x, 2) idx = (pow >>> 6) + 1 shift = (pow & 63) % UInt upshift = BITS_PER_LIMB - shift asz = abs(sz) if shift == 0 limb = unsafe_load(ptr, idx) else limb1 = unsafe_load(ptr, idx) limb2 = idx < asz ? unsafe_load(ptr, idx+1) : UInt(0) limb = limb2 << upshift | limb1 >> shift end if nd <= 1024 && nd - pow <= 53 return hash(ldexp(flipsign(Float64(limb), sz), pow), h) end h = hash_integer(pow, h) h โŠป= hash_uint(flipsign(limb, sz) โŠป h) for idx = idx+1:asz if shift == 0 limb = unsafe_load(ptr, idx) else limb1 = limb2 if idx == asz limb = limb1 >> shift limb == 0 && break # don't hash leading zeros else limb2 = unsafe_load(ptr, idx+1) limb = limb2 << upshift | limb1 >> shift end end h โŠป= hash_uint(limb โŠป h) end return h end end end module MPQ # Rational{BigInt} import .Base: unsafe_rational, __throw_rational_argerror_zero import ..GMP: BigInt, MPZ, Limb, isneg, libgmp gmpq(op::Symbol) = (Symbol(:__gmpq_, op), libgmp) mutable struct _MPQ num_alloc::Cint num_size::Cint num_d::Ptr{Limb} den_alloc::Cint den_size::Cint den_d::Ptr{Limb} # to prevent GC rat::Rational{BigInt} end const mpq_t = Ref{_MPQ} _MPQ(x::BigInt,y::BigInt) = _MPQ(x.alloc, x.size, x.d, y.alloc, y.size, y.d, unsafe_rational(BigInt, x, y)) _MPQ() = _MPQ(BigInt(), BigInt()) _MPQ(x::Rational{BigInt}) = _MPQ(x.num, x.den) function sync_rational!(xq::_MPQ) xq.rat.num.alloc = xq.num_alloc xq.rat.num.size = xq.num_size xq.rat.num.d = xq.num_d xq.rat.den.alloc = xq.den_alloc xq.rat.den.size = xq.den_size xq.rat.den.d = xq.den_d return xq.rat end function Rational{BigInt}(num::BigInt, den::BigInt) if iszero(den) iszero(num) && __throw_rational_argerror_zero(BigInt) return set_si(flipsign(1, num), 0) end xq = _MPQ(MPZ.set(num), MPZ.set(den)) ccall((:__gmpq_canonicalize, libgmp), Cvoid, (mpq_t,), xq) return sync_rational!(xq) end # define set, set_ui, set_si, set_z, and their inplace versions function set!(z::Rational{BigInt}, x::Rational{BigInt}) zq = _MPQ(z) ccall((:__gmpq_set, libgmp), Cvoid, (mpq_t, mpq_t), zq, _MPQ(x)) return sync_rational!(zq) end function set_z!(z::Rational{BigInt}, x::BigInt) zq = _MPQ(z) ccall((:__gmpq_set_z, libgmp), Cvoid, (mpq_t, MPZ.mpz_t), zq, x) return sync_rational!(zq) end for (op, T) in ((:set, Rational{BigInt}), (:set_z, BigInt)) op! = Symbol(op, :!) @eval $op(a::$T) = $op!(unsafe_rational(BigInt(), BigInt()), a) end # note that rationals returned from set_ui and set_si are not checked, # set_ui(0, 0) will return 0//0 without errors, just like unsafe_rational for (op, T1, T2) in ((:set_ui, Culong, Culong), (:set_si, Clong, Culong)) op! = Symbol(op, :!) @eval begin function $op!(z::Rational{BigInt}, a, b) zq = _MPQ(z) ccall($(gmpq(op)), Cvoid, (mpq_t, $T1, $T2), zq, a, b) return sync_rational!(zq) end $op(a, b) = $op!(unsafe_rational(BigInt(), BigInt()), a, b) end end # define add, sub, mul, div, and their inplace versions function add!(z::Rational{BigInt}, x::Rational{BigInt}, y::Rational{BigInt}) if iszero(x.den) || iszero(y.den) if iszero(x.den) && iszero(y.den) && isneg(x.num) != isneg(y.num) throw(DivideError()) end return set!(z, iszero(x.den) ? x : y) end zq = _MPQ(z) ccall((:__gmpq_add, libgmp), Cvoid, (mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y)) return sync_rational!(zq) end function sub!(z::Rational{BigInt}, x::Rational{BigInt}, y::Rational{BigInt}) if iszero(x.den) || iszero(y.den) if iszero(x.den) && iszero(y.den) && isneg(x.num) == isneg(y.num) throw(DivideError()) end iszero(x.den) && return set!(z, x) return set_si!(z, flipsign(-1, y.num), 0) end zq = _MPQ(z) ccall((:__gmpq_sub, libgmp), Cvoid, (mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y)) return sync_rational!(zq) end function mul!(z::Rational{BigInt}, x::Rational{BigInt}, y::Rational{BigInt}) if iszero(x.den) || iszero(y.den) if iszero(x.num) || iszero(y.num) throw(DivideError()) end return set_si!(z, ifelse(xor(isneg(x.num), isneg(y.num)), -1, 1), 0) end zq = _MPQ(z) ccall((:__gmpq_mul, libgmp), Cvoid, (mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y)) return sync_rational!(zq) end function div!(z::Rational{BigInt}, x::Rational{BigInt}, y::Rational{BigInt}) if iszero(x.den) if iszero(y.den) throw(DivideError()) end isneg(y.num) || return set!(z, x) return set_si!(z, flipsign(-1, x.num), 0) elseif iszero(y.den) return set_si!(z, 0, 1) elseif iszero(y.num) if iszero(x.num) throw(DivideError()) end return set_si!(z, flipsign(1, x.num), 0) end zq = _MPQ(z) ccall((:__gmpq_div, libgmp), Cvoid, (mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y)) return sync_rational!(zq) end for (fJ, fC) in ((:+, :add), (:-, :sub), (:*, :mul), (://, :div)) fC! = Symbol(fC, :!) @eval begin ($fC!)(x::Rational{BigInt}, y::Rational{BigInt}) = $fC!(x, x, y) (Base.$fJ)(x::Rational{BigInt}, y::Rational{BigInt}) = $fC!(unsafe_rational(BigInt(), BigInt()), x, y) end end function Base.cmp(x::Rational{BigInt}, y::Rational{BigInt}) Int(ccall((:__gmpq_cmp, libgmp), Cint, (mpq_t, mpq_t), _MPQ(x), _MPQ(y))) end end # MPQ module end # module N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ryu/Ryu.jl็module Ryu using .Base.Libc import .Base: significand_bits, significand_mask, exponent_bits, exponent_mask, exponent_bias, exponent_max, uinttype include("utils.jl") include("shortest.jl") include("fixed.jl") include("exp.jl") """ Ryu.neededdigits(T) Number of digits necessary to represent type `T` in fixed-precision decimal. """ neededdigits(::Type{Float64}) = 309 + 17 neededdigits(::Type{Float32}) = 39 + 9 + 2 neededdigits(::Type{Float16}) = 9 + 5 + 9 """ Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) Ryu.writeshortest(buf::Vector{UInt8}, pos::Int, x, args...) Convert a float value `x` into its "shortest" decimal string, which can be parsed back to the same value. This function allows achieving the `%g` printf format. Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. Various options for the output format include: * `plus`: for positive `x`, prefix decimal string with a `'+'` character * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision * `precision`: minimum number of digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `expchar`: character to use exponent component in scientific notation * `padexp`: whether two digits should always be written, even for single-digit exponents (e.g. `e+1` becomes `e+01`) * `decchar`: decimal point character to be used * `typed`: whether additional type information should be printed for `Float16` / `Float32` * `compact`: output will be limited to 6 significant digits """ function writeshortest(x::T, plus::Bool=false, space::Bool=false, hash::Bool=true, precision::Integer=-1, expchar::UInt8=UInt8('e'), padexp::Bool=false, decchar::UInt8=UInt8('.'), typed::Bool=false, compact::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(neededdigits(T)) pos = writeshortest(buf, 1, x, plus, space, hash, precision, expchar, padexp, decchar, typed, compact) return String(resize!(buf, pos - 1)) end """ Ryu.writefixed(x, precision, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) Ryu.writefixed(buf::Vector{UInt8}, pos::Int, x, args...) Convert a float value `x` into a "fixed" size decimal string of the provided precision. This function allows achieving the `%f` printf format. Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. Various options for the output format include: * `plus`: for positive `x`, prefix decimal string with a `'+'` character * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `decchar`: decimal point character to be used * `trimtrailingzeros`: whether trailing zeros of fractional part should be removed """ function writefixed(x::T, precision::Integer, plus::Bool=false, space::Bool=false, hash::Bool=false, decchar::UInt8=UInt8('.'), trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(precision + neededdigits(T)) pos = writefixed(buf, 1, x, precision, plus, space, hash, decchar, trimtrailingzeros) return String(resize!(buf, pos - 1)) end """ Ryu.writeexp(x, precision, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) Ryu.writeexp(buf::Vector{UInt8}, pos::Int, x, args...) Convert a float value `x` into a scientific notation decimal string. This function allows achieving the `%e` printf format. Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. Various options for the output format include: * `plus`: for positive `x`, prefix decimal string with a `'+'` character * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary * `expchar`: character to use exponent component in scientific notation * `decchar`: decimal point character to be used * `trimtrailingzeros`: whether trailing zeros should be removed """ function writeexp(x::T, precision::Integer, plus::Bool=false, space::Bool=false, hash::Bool=false, expchar::UInt8=UInt8('e'), decchar::UInt8=UInt8('.'), trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} buf = Base.StringVector(precision + neededdigits(T)) pos = writeexp(buf, 1, x, precision, plus, space, hash, expchar, decchar, trimtrailingzeros) return String(resize!(buf, pos - 1)) end function Base.show(io::IO, x::T, forceuntyped::Bool=false, fromprint::Bool=false) where {T <: Base.IEEEFloat} compact = get(io, :compact, false)::Bool buf = Base.StringVector(neededdigits(T)) typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x) pos = writeshortest(buf, 1, x, false, false, true, -1, (x isa Float32 && !fromprint) ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) write(io, resize!(buf, pos - 1)) return end function Base.string(x::T) where {T <: Base.IEEEFloat} buf = Base.StringVector(neededdigits(T)) pos = writeshortest(buf, 1, x, false, false, true, -1, UInt8('e'), false, UInt8('.'), false, false) return String(resize!(buf, pos - 1)) end Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true, true) end # module P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ryu/utils.jl $const MANTISSA_MASK = Base.significand_mask(Float64) const EXP_MASK = Base.exponent_mask(Float64) >> Base.significand_bits(Float64) # Note: these are smaller than the values given in Figure 4 from the paper # see https://github.com/ulfjack/ryu/issues/119 pow5_bitcount(::Type{Float16}) = 30 pow5_bitcount(::Type{Float32}) = 61 pow5_bitcount(::Type{Float64}) = 121 pow5_inv_bitcount(::Type{Float16}) = 30 pow5_inv_bitcount(::Type{Float32}) = 59 pow5_inv_bitcount(::Type{Float64}) = 122 qinvbound(::Type{Float16}) = 4 qinvbound(::Type{Float32}) = 9 qinvbound(::Type{Float64}) = 21 qbound(::Type{Float16}) = 15 qbound(::Type{Float32}) = 31 qbound(::Type{Float64}) = 63 """ Ryu.log10pow2(e::Integer) Computes `floor(log10(2^e))`. This is valid for all `e < 1651`. """ log10pow2(e) = (e * 78913) >> 18 """ Ryu.log10pow5(e::Integer) Computes `floor(log10(5^e))`. This is valid for all `e < 2621`. """ log10pow5(e) = (e * 732923) >> 20 """ Ryu.pow5bits(e) Computes `e == 0 ? 1 : ceil(log2(5^e))`. This is valid for `e < 3529` (if performend in `Int32` arithmetic). """ pow5bits(e) = ((e * 1217359) >> 19) + 1 """" Ryu.mulshift(m::U, mula, j) where {U<:Unsigned} Compute `(m * mul) >> j`, where `j >= 8*sizeof(U)`. The type of the results is the larger of `U` or `UInt32`. """ function mulshift(m::U, mul, j) where {U<:Unsigned} W = widen(U) nbits = 8*sizeof(U) return ((((W(m) * (mul % U)) >> nbits) + W(m) * (mul >> nbits)) >> (j - nbits)) % promote_type(U,UInt32) end indexforexp(e) = div(e + 15, 16) pow10bitsforindex(idx) = 16 * idx + 120 lengthforindex(idx) = div(((Int64(16 * idx) * 1292913986) >> 32) + 1 + 16 + 8, 9) """ Ryu.pow5(x, p) Return `true` if `5^p` is a divisor of `x`. """ pow5(x, p) = x % (UInt64(5)^p) == 0 """ Ryu.pow2(x, p) Return `true` if `2^p` is a divisor of `x`. In other words, if the trailing `p` bits of `x` are zero. """ pow2(x, p) = (x & ((Int64(1) << p) - 1)) == 0 """ Ryu.decimallength(v) The number of decimal digits of the integer `v`. """ function decimallength(v) v >= 10000000000000000 && return 17 v >= 1000000000000000 && return 16 v >= 100000000000000 && return 15 v >= 10000000000000 && return 14 v >= 1000000000000 && return 13 v >= 100000000000 && return 12 v >= 10000000000 && return 11 v >= 1000000000 && return 10 v >= 100000000 && return 9 v >= 10000000 && return 8 v >= 1000000 && return 7 v >= 100000 && return 6 v >= 10000 && return 5 v >= 1000 && return 4 v >= 100 && return 3 v >= 10 && return 2 return 1 end function decimallength(v::UInt32) v >= 100000000 && return 9 v >= 10000000 && return 8 v >= 1000000 && return 7 v >= 100000 && return 6 v >= 10000 && return 5 v >= 1000 && return 4 v >= 100 && return 3 v >= 10 && return 2 return 1 end function decimallength(v::UInt16) v >= 10000 && return 5 v >= 1000 && return 4 v >= 100 && return 3 v >= 10 && return 2 return 1 end function mulshiftinvsplit(::Type{T}, mv, mp, mm, i, j) where {T} mul = pow5invsplit_lookup(T, i) vr = mulshift(mv, mul, j) vp = mulshift(mp, mul, j) vm = mulshift(mm, mul, j) return vr, vp, vm end function mulshiftsplit(::Type{T}, mv, mp, mm, i, j) where {T} mul = pow5split_lookup(T, i) vr = mulshift(mv, mul, j) vp = mulshift(mp, mul, j) vm = mulshift(mm, mul, j) return vr, vp, vm end """ Ryu.umul256(a::UInt128, bHi::UInt64, bLo::UInt64)::Tuple{UInt128, UInt128} Compute `p = a*b` where `b = bLo + bHi<<64`, returning the result as `pLo, pHi` where `p = pLo + pHi<<128`. """ function umul256(a::UInt128, bHi::UInt64, bLo::UInt64) aLo = a % UInt64 aHi = (a >> 64) % UInt64 b00 = UInt128(aLo) * bLo b01 = UInt128(aLo) * bHi b10 = UInt128(aHi) * bLo b11 = UInt128(aHi) * bHi b00Lo = b00 % UInt64 b00Hi = (b00 >> 64) % UInt64 mid1 = b10 + b00Hi mid1Lo = mid1 % UInt64 mid1Hi = (mid1 >> 64) % UInt64 mid2 = b01 + mid1Lo mid2Lo = mid2 % UInt64 mid2Hi = (mid2 >> 64) % UInt64 pHi = b11 + mid1Hi + mid2Hi pLo = (UInt128(mid2Lo) << 64) | b00Lo return pLo, pHi end """ Ryu.umul256_hi(a::UInt128, bHi::UInt64, bLo::UInt64)::UInt128 Compute `pHi = (a*b)>>128` where `b = bLo + bHi<<64`. """ umul256_hi(a::UInt128, bHi::UInt64, bLo::UInt64) = umul256(a, bHi, bLo)[2] """ Ryu.mulshiftmod1e9(m, mula, mulb, mulc, j)::UInt32 Compute `(m * mul) >> j % 10^9` where `mul = mula + mulb<<64 + mulc<<128`, and `j >= 128`. """ function mulshiftmod1e9(m, mula, mulb, mulc, j) b0 = UInt128(m) * mula b1 = UInt128(m) * mulb b2 = UInt128(m) * mulc mid = b1 + ((b0 >> 64) % UInt64) s1 = b2 + ((mid >> 64) % UInt64) v = s1 >> (j - 128) multiplied = umul256_hi(v, 0x89705F4136B4A597, 0x31680A88F8953031) shifted = (multiplied >> 29) % UInt32 return (v % UInt32) - UInt32(1000000000) * shifted end function append_sign(x, plus::Bool, space::Bool, buf, pos::Int) if signbit(x) && !isnan(x) # suppress minus sign for signaling NaNs buf[pos] = UInt8('-') pos += 1 elseif plus buf[pos] = UInt8('+') pos += 1 elseif space buf[pos] = UInt8(' ') pos += 1 end return pos end import Base: append_c_digits_fast as append_c_digits, append_nine_digits function append_d_digits(olength::Int, digits::Unsigned, buf, pos::Int, decchar) newpos = append_c_digits(olength, digits, buf, pos + 1) @inbounds buf[pos] = buf[pos + 1] @inbounds buf[pos + 1] = decchar return newpos # == pos + olength + 1 end const BIG_MASK = (big(1) << 64) - 1 const POW10_SPLIT = collect(Iterators.flatten(map(0:63) do idx pow10bits = pow10bitsforindex(idx) map(0:lengthforindex(idx)-1) do i v = (div(big(1) << pow10bits, big(10)^(9 * i)) + 1) % ((big(10)^9) << 136) return (UInt64(v & BIG_MASK), UInt64((v >> 64) & BIG_MASK), UInt64((v >> 128) & BIG_MASK)) end end)) function generateinversetables() POW10_OFFSET_2 = Vector{UInt16}(undef, 68 + 1) MIN_BLOCK_2 = fill(0xff, 68 + 1) POW10_SPLIT_2 = Tuple{UInt64, UInt64, UInt64}[] lowerCutoff = big(1) << (54 + 8) for idx = 0:67 POW10_OFFSET_2[idx + 1] = length(POW10_SPLIT_2) i = 0 while true v = ((big(10)^(9 * (i + 1)) >> (-(120 - 16 * idx))) % (big(10)^9) << (120 + 16)) if MIN_BLOCK_2[idx + 1] == 0xff && ((v * lowerCutoff) >> 128) == 0 i += 1 continue end if MIN_BLOCK_2[idx + 1] == 0xff MIN_BLOCK_2[idx + 1] = i end v == 0 && break push!(POW10_SPLIT_2, ((v & BIG_MASK) % UInt64, ((v >> 64) & BIG_MASK) % UInt64, ((v >> 128) & BIG_MASK) % UInt64)) i += 1 end end POW10_OFFSET_2[end] = length(POW10_SPLIT_2) MIN_BLOCK_2[end] = 0x00 return POW10_OFFSET_2, MIN_BLOCK_2, POW10_SPLIT_2 end const POW10_OFFSET_2, MIN_BLOCK_2, POW10_SPLIT_2 = generateinversetables() """ Ryu.pow5invsplit(T, i) Compute `floor(2^k/5^i)+1`, where `k = pow5bits(i) - 1 + pow5_inv_bitcount(T)`. The result is an unsigned integer twice as wide as `T` (i.e. a `UInt128` if `T == Float64`), with `pow5_inv_bitcount(T)` significant bits. """ function pow5invsplit(::Type{T}, i) where {T<:AbstractFloat} W = widen(uinttype(T)) pow = big(5)^i inv = div(big(1) << (ndigits(pow, base=2) - 1 + pow5_inv_bitcount(T)), pow) + 1 return W(inv) end """ Ryu.pow5invsplit_lookup(T, i) [`pow5invsplit`](@ref) computed via lookup table. """ function pow5invsplit_lookup end for T in (Float64, Float32, Float16) e2_max = exponent_max(T) - precision(T) - 1 i_max = log10pow2(e2_max) table_sym = Symbol("pow5invsplit_table_", string(T)) @eval const $table_sym = Tuple(Any[pow5invsplit($T, i) for i = 0:$i_max]) @eval pow5invsplit_lookup(::Type{$T}, i) = @inbounds($table_sym[i+1]) end """ Ryu.pow5split(T, i) Compute `floor(5^i/2^k)`, where `k = pow5bits(i) - pow5_bitcount(T)`. The result is an unsigned integer twice as wide as `T` (i.e. a `UInt128` if `T == Float64`), with `pow5_bitcount(T)` significant bits. """ function pow5split(::Type{T}, i) where {T<:AbstractFloat} W = widen(uinttype(T)) pow = big(5)^i return W(pow >> (ndigits(pow, base=2) - pow5_bitcount(T))) end """ Ryu.pow5split_lookup(T, i) [`pow5split`](@ref) computed via lookup table. """ function pow5split_lookup end for T in (Float64, Float32, Float16) e2_min = 1 - exponent_bias(T) - significand_bits(T) - 2 i_max = 1 - e2_min - log10pow5(-e2_min) table_sym = Symbol("pow5split_table_", string(T)) @eval const $table_sym = Tuple(Any[pow5split($T, i) for i = 0:$i_max]) @eval pow5split_lookup(::Type{$T}, i) = @inbounds($table_sym[i+1]) end const DIGIT_TABLE16 = Base._dec_d100 const POW10_OFFSET = UInt16[ 0, 2, 5, 8, 12, 16, 21, 26, 32, 39, 46, 54, 62, 71, 80, 90, 100, 111, 122, 134, 146, 159, 173, 187, 202, 217, 233, 249, 266, 283, 301, 319, 338, 357, 377, 397, 418, 440, 462, 485, 508, 532, 556, 581, 606, 632, 658, 685, 712, 740, 769, 798, 828, 858, 889, 920, 952, 984, 1017, 1050, 1084, 1118, 1153, 1188 ] S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ryu/shortest.jlว:""" b, e10 = reduce_shortest(f[, maxsignif]) Reduce to shortest decimal representation where `abs(f) == b * 10^e10` and `b` is an integer. If a `maxsignif` argument is provided, then `b < maxsignif`. """ @inline function reduce_shortest(f::T, maxsignif=nothing) where {T} U = uinttype(T) uf = reinterpret(U, f) m = uf & significand_mask(T) e = ((uf & exponent_mask(T)) >> significand_bits(T)) % Int ## Step 1 # mf * 2^ef == f mf = (one(U) << significand_bits(T)) | m ef = e - exponent_bias(T) - significand_bits(T) f_isinteger = mf & ((one(U) << -ef) - one(U)) == 0 if ef > 0 || ef < -Base.significand_bits(T) || !f_isinteger # fixup subnormals if e == 0 ef = 1 - exponent_bias(T) - significand_bits(T) mf = m end ## Step 2 # u * 2^e2 == (f + prevfloat(f))/2 # v * 2^e2 == f # w * 2^e2 == (f + nextfloat(f))/2 e2 = ef - 2 mf_iseven = iseven(mf) # trailing bit of significand is zero v = U(4) * mf w = v + U(2) u_shift_half = m == 0 && e > 1 # if first element of binade, other than first normal one u = v - U(2) + u_shift_half ## Step 3 # a == floor(u * 2^e2 / 10^e10), exact if a_allzero # b == floor(v * 2^e2 / 10^e10), exact if b_allzero # c == floor(w * 2^e2 / 10^e10) a_allzero = false b_allzero = false b_lastdigit = 0x00 if e2 >= 0 q = log10pow2(e2) - (T == Float64 ? (e2 > 3) : 0) e10 = q k = pow5_inv_bitcount(T) + pow5bits(q) - 1 i = -e2 + q + k a, b, c = mulshiftinvsplit(T, u, v, w, q, i) if T == Float32 || T == Float16 if q != 0 && div(c - 1, 10) <= div(a, 10) l = pow5_inv_bitcount(T) + pow5bits(q - 1) - 1 mul = pow5invsplit_lookup(T, q-1) b_lastdigit = (mulshift(v, mul, -e2 + q - 1 + l) % 10) % UInt8 end end if q <= qinvbound(T) if ((v % UInt32) - 5 * div(v, 5)) == 0 b_allzero = pow5(v, q) elseif mf_iseven a_allzero = pow5(u, q) else c -= pow5(w, q) end end else q = log10pow5(-e2) - (T == Float64 ? (-e2 > 1) : 0) e10 = q + e2 i = -e2 - q k = pow5bits(i) - pow5_bitcount(T) j = q - k a, b, c = mulshiftsplit(T, u, v, w, i, j) if T == Float32 || T == Float16 if q != 0 && div(c - 1, 10) <= div(a, 10) j = q - 1 - (pow5bits(i + 1) - pow5_bitcount(T)) mul = pow5split_lookup(T, i+1) b_lastdigit = (mulshift(v, mul, j) % 10) % UInt8 end end if q <= 1 b_allzero = true if mf_iseven a_allzero = !u_shift_half else c -= 1 end elseif q < qbound(T) b_allzero = pow2(v, q - (T != Float64)) end end ## Step 4: reduction if a_allzero || b_allzero # a) slow loop while true c_div10 = div(c, 10) a_div10 = div(a, 10) if c_div10 <= a_div10 break end a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) a_allzero &= a_mod10 == 0 b_allzero &= b_lastdigit == 0 b_lastdigit = b_mod10 % UInt8 b = b_div10 c = c_div10 a = a_div10 e10 += 1 end if a_allzero while true a_div10 = div(a, 10) a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) if a_mod10 != 0 && (maxsignif === nothing || b < maxsignif) break end c_div10 = div(c, 10) b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) b_allzero &= b_lastdigit == 0 b_lastdigit = b_mod10 % UInt8 b = b_div10 c = c_div10 a = a_div10 e10 += 1 end end if b_allzero && b_lastdigit == 5 && iseven(b) b_lastdigit = UInt8(4) end roundup = (b == a && (!mf_iseven || !a_allzero)) || b_lastdigit >= 5 else # b) specialized for common case (99% Float64, 96% Float32) roundup = b_lastdigit >= 5 c_div100 = div(c, 100) a_div100 = div(a, 100) if c_div100 > a_div100 b_div100 = div(b, 100) b_mod100 = (b % UInt32) - UInt32(100) * (b_div100 % UInt32) roundup = b_mod100 >= 50 b = b_div100 c = c_div100 a = a_div100 e10 += 2 end while true c_div10 = div(c, 10) a_div10 = div(a, 10) if c_div10 <= a_div10 break end b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) roundup = b_mod10 >= 5 b = b_div10 c = c_div10 a = a_div10 e10 += 1 end roundup = (b == a || roundup) end if maxsignif !== nothing && b > maxsignif # reduce to max significant digits while true b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) if b <= maxsignif break end b = b_div10 roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 b_allzero &= b_mod10 == 0 e10 += 1 end b = b + roundup # remove trailing zeros while true b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) if b_mod10 != 0 break end b = b_div10 e10 += 1 end else b = b + roundup end else # c) specialized f an integer < 2^53 b = mf >> -ef e10 = 0 if maxsignif !== nothing && b > maxsignif b_allzero = true # reduce to max significant digits while true b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) if b <= maxsignif break end b = b_div10 roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 b_allzero &= b_mod10 == 0 e10 += 1 end b = b + roundup end while true b_div10 = div(b, 10) b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) if b_mod10 != 0 break end b = b_div10 e10 += 1 end end return b, e10 end function writeshortest(buf::Vector{UInt8}, pos, x::T, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) where {T} @assert 0 < pos <= length(buf) # special cases if x == 0 if typed && x isa Float16 @inbounds buf[pos] = UInt8('F') @inbounds buf[pos + 1] = UInt8('l') @inbounds buf[pos + 2] = UInt8('o') @inbounds buf[pos + 3] = UInt8('a') @inbounds buf[pos + 4] = UInt8('t') @inbounds buf[pos + 5] = UInt8('1') @inbounds buf[pos + 6] = UInt8('6') @inbounds buf[pos + 7] = UInt8('(') pos += 8 end pos = append_sign(x, plus, space, buf, pos) @inbounds buf[pos] = UInt8('0') pos += 1 if hash @inbounds buf[pos] = decchar pos += 1 end if precision == -1 @inbounds buf[pos] = UInt8('0') pos += 1 if typed && x isa Float32 @inbounds buf[pos] = UInt8('f') @inbounds buf[pos + 1] = UInt8('0') pos += 2 end if typed && x isa Float16 @inbounds buf[pos] = UInt8(')') pos += 1 end return pos end while hash && precision > 1 @inbounds buf[pos] = UInt8('0') pos += 1 precision -= 1 end if typed && x isa Float32 @inbounds buf[pos] = UInt8('f') @inbounds buf[pos + 1] = UInt8('0') pos += 2 end if typed && x isa Float16 @inbounds buf[pos] = UInt8(')') pos += 1 end return pos elseif isnan(x) pos = append_sign(x, plus, space, buf, pos) @inbounds buf[pos] = UInt8('N') @inbounds buf[pos + 1] = UInt8('a') @inbounds buf[pos + 2] = UInt8('N') if typed if x isa Float32 @inbounds buf[pos + 3] = UInt8('3') @inbounds buf[pos + 4] = UInt8('2') elseif x isa Float16 @inbounds buf[pos + 3] = UInt8('1') @inbounds buf[pos + 4] = UInt8('6') end end return pos + 3 + (typed && x isa Union{Float32, Float16} ? 2 : 0) elseif !isfinite(x) pos = append_sign(x, plus, space, buf, pos) @inbounds buf[pos] = UInt8('I') @inbounds buf[pos + 1] = UInt8('n') @inbounds buf[pos + 2] = UInt8('f') if typed if x isa Float32 @inbounds buf[pos + 3] = UInt8('3') @inbounds buf[pos + 4] = UInt8('2') elseif x isa Float16 @inbounds buf[pos + 3] = UInt8('1') @inbounds buf[pos + 4] = UInt8('6') end end return pos + 3 + (typed && x isa Union{Float32, Float16} ? 2 : 0) end output, nexp = reduce_shortest(x, compact ? 999_999 : nothing) if typed && x isa Float16 @inbounds buf[pos] = UInt8('F') @inbounds buf[pos + 1] = UInt8('l') @inbounds buf[pos + 2] = UInt8('o') @inbounds buf[pos + 3] = UInt8('a') @inbounds buf[pos + 4] = UInt8('t') @inbounds buf[pos + 5] = UInt8('1') @inbounds buf[pos + 6] = UInt8('6') @inbounds buf[pos + 7] = UInt8('(') pos += 8 end pos = append_sign(x, plus, space, buf, pos) olength = decimallength(output) exp_form = true pt = nexp + olength if -4 < pt <= (precision == -1 ? (T == Float16 ? 3 : 6) : precision) && !(pt >= olength && abs(mod(x + 0.05, 10^(pt - olength)) - 0.05) > 0.05) exp_form = false if pt <= 0 @inbounds buf[pos] = UInt8('0') pos += 1 @inbounds buf[pos] = decchar pos += 1 for _ = 1:abs(pt) @inbounds buf[pos] = UInt8('0') pos += 1 end # elseif pt >= olength # nothing to do at this point # else # nothing to do at this point end else # make space for decchar pos += 1 end append_c_digits(olength, output, buf, pos) if !exp_form if pt <= 0 pos += olength precision -= olength elseif pt >= olength pos += olength precision -= olength for _ = 1:nexp @inbounds buf[pos] = UInt8('0') pos += 1 precision -= 1 end if hash @inbounds buf[pos] = decchar pos += 1 if precision < 0 @inbounds buf[pos] = UInt8('0') pos += 1 end end else pointoff = olength - abs(nexp) # shift bytes after pointoff to make room for decchar ptr = pointer(buf) memmove(ptr + pos + pointoff, ptr + pos + pointoff - 1, olength - pointoff + 1) @inbounds buf[pos + pointoff] = decchar pos += olength + 1 precision -= olength end if hash while precision > 0 @inbounds buf[pos] = UInt8('0') pos += 1 precision -= 1 end end if typed && x isa Float32 @inbounds buf[pos] = UInt8('f') @inbounds buf[pos + 1] = UInt8('0') pos += 2 end else # move leading digit into place @inbounds buf[pos - 1] = buf[pos] if olength > 1 || hash @inbounds buf[pos] = decchar pos += olength precision -= olength end if hash if olength == 1 @inbounds buf[pos] = UInt8('0') pos += 1 end while precision > 0 @inbounds buf[pos] = UInt8('0') pos += 1 precision -= 1 end end @inbounds buf[pos] = expchar pos += 1 exp2 = nexp + olength - 1 if exp2 < 0 @inbounds buf[pos] = UInt8('-') pos += 1 exp2 = -exp2 elseif padexp @inbounds buf[pos] = UInt8('+') pos += 1 end if exp2 >= 100 c = exp2 % 10 @inbounds d100 = DIGIT_TABLE16[(div(exp2, 10) % Int) + 1] @inbounds buf[pos] = d100 % UInt8 @inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8 @inbounds buf[pos + 2] = UInt8('0') + (c % UInt8) pos += 3 elseif exp2 >= 10 @inbounds d100 = DIGIT_TABLE16[(exp2 % Int) + 1] @inbounds buf[pos] = d100 % UInt8 @inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8 pos += 2 else if padexp @inbounds buf[pos] = UInt8('0') pos += 1 end @inbounds buf[pos] = UInt8('0') + (exp2 % UInt8) pos += 1 end end if typed && x isa Float16 @inbounds buf[pos] = UInt8(')') pos += 1 end return pos end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ryu/fixed.jl๓function writefixed(buf, pos, v::T, precision=-1, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} @assert 0 < pos <= length(buf) startpos = pos x = Float64(v) pos = append_sign(x, plus, space, buf, pos) # special cases if x == 0 buf[pos] = UInt8('0') pos += 1 if precision > 0 && !trimtrailingzeros buf[pos] = decchar pos += 1 for _ = 1:precision buf[pos] = UInt8('0') pos += 1 end elseif hash buf[pos] = decchar pos += 1 end return pos elseif isnan(x) buf[pos] = UInt8('N') buf[pos + 1] = UInt8('a') buf[pos + 2] = UInt8('N') return pos + 3 elseif !isfinite(x) buf[pos] = UInt8('I') buf[pos + 1] = UInt8('n') buf[pos + 2] = UInt8('f') return pos + 3 end bits = Core.bitcast(UInt64, x) mant = bits & MANTISSA_MASK exp = Int((bits >> 52) & EXP_MASK) if exp == 0 # subnormal e2 = 1 - 1023 - 52 m2 = mant else e2 = exp - 1023 - 52 m2 = (Int64(1) << 52) | mant end nonzero = false if e2 >= -52 idx = e2 < 0 ? 0 : indexforexp(e2) p10bits = pow10bitsforindex(idx) len = lengthforindex(idx) i = len - 1 while i >= 0 j = p10bits - e2 mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1] digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) if nonzero pos = append_nine_digits(digits, buf, pos) elseif digits != 0 olength = decimallength(digits) pos = append_c_digits(olength, digits, buf, pos) nonzero = true end i -= 1 end end if !nonzero buf[pos] = UInt8('0') pos += 1 end hasfractional = false if precision > 0 || hash buf[pos] = decchar pos += 1 hasfractional = true end if e2 < 0 idx = div(-e2, 16) blocks = div(precision, 9) + 1 roundUp = 0 i = 0 if blocks <= MIN_BLOCK_2[idx + 1] i = blocks for _ = 1:precision buf[pos] = UInt8('0') pos += 1 end elseif i < MIN_BLOCK_2[idx + 1] i = MIN_BLOCK_2[idx + 1] for _ = 1:(9 * i) buf[pos] = UInt8('0') pos += 1 end end while i < blocks j = 120 + (-e2 - 16 * idx) p = POW10_OFFSET_2[idx + 1] + UInt32(i) - MIN_BLOCK_2[idx + 1] if p >= POW10_OFFSET_2[idx + 2] for _ = 1:(precision - 9 * i) buf[pos] = UInt8('0') pos += 1 end break end mula, mulb, mulc = POW10_SPLIT_2[p + 1] digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) if i < blocks - 1 pos = append_nine_digits(digits, buf, pos) else maximum = precision - 9 * i lastDigit = 0 k = 0 while k < 9 - maximum # global digits, lastDigit, k lastDigit = digits % 10 digits = div(digits, 10) k += 1 end if lastDigit != 5 roundUp = lastDigit > 5 ? 1 : 0 else requiredTwos = -e2 - precision - 1 trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos)) roundUp = trailingZeros ? 2 : 1 # 2 means round only if odd end if maximum > 0 pos = append_c_digits(maximum, digits, buf, pos) end break end i += 1 end if roundUp != 0 roundPos = pos dotPos = 1 while true roundPos -= 1 if roundPos == (startpos - 1) || (buf[roundPos] == UInt8('-')) || (plus && buf[roundPos] == UInt8('+')) || (space && buf[roundPos] == UInt8(' ')) buf[pos] = UInt8('0') buf[roundPos + 1] = UInt8('1') if dotPos > 1 buf[dotPos] = UInt8('0') buf[dotPos + 1] = decchar hasfractional = true end pos += 1 break end c = roundPos > 0 ? buf[roundPos] : 0x00 if c == decchar dotPos = roundPos continue elseif c == UInt8('9') buf[roundPos] = UInt8('0') roundUp = 1 continue else if roundUp == 2 && UInt8(c) % 2 == 0 break end buf[roundPos] = c + 1 break end end end else for _ = 1:precision buf[pos] = UInt8('0') pos += 1 end end if trimtrailingzeros && hasfractional while buf[pos - 1] == UInt8('0') pos -= 1 end if buf[pos - 1] == decchar && !hash pos -= 1 end end return pos end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/ryu/exp.jlีfunction writeexp(buf, pos, v::T, precision=-1, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} @assert 0 < pos <= length(buf) startpos = pos x = Float64(v) pos = append_sign(x, plus, space, buf, pos) # special cases if iszero(x) @inbounds buf[pos] = UInt8('0') pos += 1 if precision > 0 && !trimtrailingzeros @inbounds buf[pos] = decchar pos += 1 for _ = 1:precision @inbounds buf[pos] = UInt8('0') pos += 1 end elseif hash @inbounds buf[pos] = decchar pos += 1 end @inbounds buf[pos] = expchar @inbounds buf[pos + 1] = UInt8('+') @inbounds buf[pos + 2] = UInt8('0') @inbounds buf[pos + 3] = UInt8('0') return pos + 4 elseif isnan(x) @inbounds buf[pos] = UInt8('N') @inbounds buf[pos + 1] = UInt8('a') @inbounds buf[pos + 2] = UInt8('N') return pos + 3 elseif !isfinite(x) @inbounds buf[pos] = UInt8('I') @inbounds buf[pos + 1] = UInt8('n') @inbounds buf[pos + 2] = UInt8('f') return pos + 3 end bits = Core.bitcast(UInt64, x) mant = bits & MANTISSA_MASK exp = Int((bits >> 52) & EXP_MASK) if iszero(exp) e2 = 1 - 1023 - 52 m2 = mant else e2 = exp - 1023 - 52 m2 = (Int64(1) << 52) | mant end nonzero = false precision += 1 digits = zero(UInt32) printedDigits = 0 availableDigits = 0 e = 0 if e2 >= -52 idx = e2 < 0 ? 0 : indexforexp(e2) p10bits = pow10bitsforindex(idx) len = lengthforindex(idx) i = len - 1 while i >= 0 j = p10bits - e2 #=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1] digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) if !iszero(printedDigits) if printedDigits + 9 > precision availableDigits = 9 break end pos = append_nine_digits(digits, buf, pos) printedDigits += 9 elseif !iszero(digits) availableDigits = decimallength(digits) e = i * 9 + availableDigits - 1 if availableDigits > precision break end if precision > 1 pos = append_d_digits(availableDigits, digits, buf, pos, decchar) else @inbounds buf[pos] = UInt8('0') + digits pos += 1 if hash @inbounds buf[pos] = decchar pos += 1 end end printedDigits = availableDigits availableDigits = 0 end i -= 1 end end if e2 < 0 && iszero(availableDigits) idx = div(-e2, 16) i = Int(MIN_BLOCK_2[idx + 1]) while i < 200 j = 120 + (-e2 - 16 * idx) p = POW10_OFFSET_2[idx + 1] + i - MIN_BLOCK_2[idx + 1] if p >= POW10_OFFSET_2[idx + 2] digits = zero(UInt32) else #=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1] digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) end if !iszero(printedDigits) if printedDigits + 9 > precision availableDigits = 9 break end pos = append_nine_digits(digits, buf, pos) printedDigits += 9 elseif !iszero(digits) availableDigits = decimallength(digits) e = -(i + 1) * 9 + availableDigits - 1 if availableDigits > precision break end if precision > 1 pos = append_d_digits(availableDigits, digits, buf, pos, decchar) else @inbounds buf[pos] = UInt8('0') + digits pos += 1 if hash @inbounds buf[pos] = decchar pos += 1 end end printedDigits = availableDigits availableDigits = 0 end i += 1 end end maximum = precision - printedDigits if iszero(availableDigits) digits = zero(UInt32) end lastDigit = zero(UInt32) if availableDigits > maximum for k = 0:(availableDigits - maximum - 1) lastDigit = digits % UInt32(10) digits = div(digits, UInt32(10)) end end roundUp = 0 if lastDigit != 5 roundUp = lastDigit > 5 ? 1 : 0 else rexp = precision - e requiredTwos = -e2 - rexp trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos)) if rexp < 0 requiredFives = -rexp trailingZeros = trailingZeros & pow5(m2, requiredFives) end roundUp = trailingZeros ? 2 : 1 end if !iszero(printedDigits) if iszero(digits) for _ = 1:maximum @inbounds buf[pos] = UInt8('0') pos += 1 end else pos = append_c_digits(maximum, digits, buf, pos) end else if precision > 1 pos = append_d_digits(maximum, digits, buf, pos, decchar) else @inbounds buf[pos] = UInt8('0') + digits pos += 1 if hash @inbounds buf[pos] = decchar pos += 1 end end end if !iszero(roundUp) roundPos = pos while true roundPos -= 1 if roundPos == (startpos - 1) || (@inbounds buf[roundPos]) == UInt8('-') || (plus && (@inbounds buf[roundPos]) == UInt8('+')) || (space && (@inbounds buf[roundPos]) == UInt8(' ')) @inbounds buf[roundPos + 1] = UInt8('1') e += 1 break end c = roundPos > 0 ? (@inbounds buf[roundPos]) : 0x00 if c == decchar continue elseif c == UInt8('9') @inbounds buf[roundPos] = UInt8('0') roundUp = 1 continue else if roundUp == 2 && iseven(c) break end @inbounds buf[roundPos] = c + 1 break end end end if trimtrailingzeros while @inbounds buf[pos - 1] == UInt8('0') pos -= 1 end if @inbounds buf[pos - 1] == decchar && !hash pos -= 1 end end buf[pos] = expchar pos += 1 if e < 0 @inbounds buf[pos] = UInt8('-') pos += 1 e = -e else @inbounds buf[pos] = UInt8('+') pos += 1 end if e >= 100 c = (e % 10) % UInt8 @inbounds d100 = DIGIT_TABLE16[div(e, 10) + 1] @inbounds buf[pos] = d100 % UInt8 @inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8 @inbounds buf[pos + 2] = UInt8('0') + c pos += 3 else @inbounds d100 = DIGIT_TABLE16[e + 1] @inbounds buf[pos] = d100 % UInt8 @inbounds buf[pos + 1] = (d100 >> 0x8) % UInt8 pos += 2 end return pos end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/mpfr.jlฎญ# This file is a part of Julia. License is MIT: https://julialang.org/license module MPFR export BigFloat, setprecision import .Base: *, +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, inv, exp, exp2, exponent, factorial, floor, fma, muladd, hypot, isinteger, isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float, sum, sqrt, string, print, trunc, precision, _precision, exp10, expm1, log1p, eps, signbit, sign, sin, cos, sincos, tan, sec, csc, cot, acos, asin, atan, cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, lerpi, cbrt, typemax, typemin, unsafe_trunc, floatmin, floatmax, rounding, setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, isone, big, _string_n, decompose, minmax, sinpi, cospi, sincospi, tanpi, sind, cosd, tand, asind, acosd, atand using .Base.Libc import ..Rounding: rounding_raw, setrounding_raw import ..GMP: ClongMax, CulongMax, CdoubleMax, Limb, libgmp import ..FastMath.sincos_fast if Sys.iswindows() const libmpfr = "libmpfr-6.dll" elseif Sys.isapple() const libmpfr = "@rpath/libmpfr.6.dylib" else const libmpfr = "libmpfr.so.6" end version() = VersionNumber(unsafe_string(ccall((:mpfr_get_version,libmpfr), Ptr{Cchar}, ()))) patches() = split(unsafe_string(ccall((:mpfr_get_patches,libmpfr), Ptr{Cchar}, ())),' ') function __init__() try # set exponent to full range by default set_emin!(get_emin_min()) set_emax!(get_emax_max()) catch ex Base.showerror_nostdio(ex, "WARNING: Error during initialization of module MPFR") end nothing end """ MPFR.MPFRRoundingMode Matches the `mpfr_rnd_t` enum provided by MPFR, see https://www.mpfr.org/mpfr-current/mpfr.html#Rounding-Modes This is for internal use, and ensures that `ROUNDING_MODE[]` is type-stable. """ @enum MPFRRoundingMode begin MPFRRoundNearest MPFRRoundToZero MPFRRoundUp MPFRRoundDown MPFRRoundFromZero MPFRRoundFaithful end convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Nearest}) = MPFRRoundNearest convert(::Type{MPFRRoundingMode}, ::RoundingMode{:ToZero}) = MPFRRoundToZero convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Up}) = MPFRRoundUp convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Down}) = MPFRRoundDown convert(::Type{MPFRRoundingMode}, ::RoundingMode{:FromZero}) = MPFRRoundFromZero function convert(::Type{RoundingMode}, r::MPFRRoundingMode) if r == MPFRRoundNearest return RoundNearest elseif r == MPFRRoundToZero return RoundToZero elseif r == MPFRRoundUp return RoundUp elseif r == MPFRRoundDown return RoundDown elseif r == MPFRRoundFromZero return RoundFromZero else throw(ArgumentError("invalid MPFR rounding mode code: $r")) end end const ROUNDING_MODE = Ref{MPFRRoundingMode}(MPFRRoundNearest) const DEFAULT_PRECISION = Ref{Clong}(256) # Basic type and initialization definitions """ BigFloat <: AbstractFloat Arbitrary precision floating point number type. """ mutable struct BigFloat <: AbstractFloat prec::Clong sign::Cint exp::Clong d::Ptr{Limb} # _d::Buffer{Limb} # Julia gc handle for memory @ d _d::String # Julia gc handle for memory @ d (optimized) # Not recommended for general use: # used internally by, e.g. deepcopy global function _BigFloat(prec::Clong, sign::Cint, exp::Clong, d::String) # ccall-based version, inlined below #z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL, d) #ccall((:mpfr_custom_init,libmpfr), Cvoid, (Ptr{Limb}, Clong), d, prec) # currently seems to be a no-op in mpfr #NAN_KIND = Cint(0) #ccall((:mpfr_custom_init_set,libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, d) #return z return new(prec, sign, exp, pointer(d), d) end function BigFloat(; precision::Integer=DEFAULT_PRECISION[]) precision < 1 && throw(DomainError(precision, "`precision` cannot be less than 1.")) nb = ccall((:mpfr_custom_get_size,libmpfr), Csize_t, (Clong,), precision) nb = (nb + Core.sizeof(Limb) - 1) รท Core.sizeof(Limb) # align to number of Limb allocations required for this #d = Vector{Limb}(undef, nb) d = _string_n(nb * Core.sizeof(Limb)) EXP_NAN = Clong(1) - Clong(typemax(Culong) >> 1) return _BigFloat(Clong(precision), one(Cint), EXP_NAN, d) # +NAN end end rounding_raw(::Type{BigFloat}) = ROUNDING_MODE[] setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r rounding(::Type{BigFloat}) = convert(RoundingMode, rounding_raw(BigFloat)) setrounding(::Type{BigFloat}, r::RoundingMode) = setrounding_raw(BigFloat, convert(MPFRRoundingMode, r)) # overload the definition of unsafe_convert to ensure that `x.d` is assigned # it may have been dropped in the event that the BigFloat was serialized Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = x @inline function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat}) x = x[] if x.d == C_NULL x.d = pointer(x._d) end return convert(Ptr{BigFloat}, Base.pointer_from_objref(x)) end """ BigFloat(x::Union{Real, AbstractString} [, rounding::RoundingMode=rounding(BigFloat)]; [precision::Integer=precision(BigFloat)]) Create an arbitrary precision floating point number from `x`, with precision `precision`. The `rounding` argument specifies the direction in which the result should be rounded if the conversion cannot be done exactly. If not provided, these are set by the current global values. `BigFloat(x::Real)` is the same as `convert(BigFloat,x)`, except if `x` itself is already `BigFloat`, in which case it will return a value with the precision set to the current global precision; `convert` will always return `x`. `BigFloat(x::AbstractString)` is identical to [`parse`](@ref). This is provided for convenience since decimal literals are converted to `Float64` when parsed, so `BigFloat(2.1)` may not yield what you expect. See also: - [`@big_str`](@ref) - [`rounding`](@ref) and [`setrounding`](@ref) - [`precision`](@ref) and [`setprecision`](@ref) !!! compat "Julia 1.1" `precision` as a keyword argument requires at least Julia 1.1. In Julia 1.0 `precision` is the second positional argument (`BigFloat(x, precision)`). # Examples ```jldoctest julia> BigFloat(2.1) # 2.1 here is a Float64 2.100000000000000088817841970012523233890533447265625 julia> BigFloat("2.1") # the closest BigFloat to 2.1 2.099999999999999999999999999999999999999999999999999999999999999999999999999986 julia> BigFloat("2.1", RoundUp) 2.100000000000000000000000000000000000000000000000000000000000000000000000000021 julia> BigFloat("2.1", RoundUp, precision=128) 2.100000000000000000000000000000000000007 ``` """ BigFloat(x, r::RoundingMode) widen(::Type{Float64}) = BigFloat widen(::Type{BigFloat}) = BigFloat function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) if precision == _precision(x) return x else z = BigFloat(;precision=precision) ccall((:mpfr_set, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, r) return z end end function _duplicate(x::BigFloat) z = BigFloat(;precision=_precision(x)) ccall((:mpfr_set, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, 0) return z end # convert to BigFloat for (fJ, fC) in ((:si,:Clong), (:ui,:Culong)) @eval begin function BigFloat(x::($fC), r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) z = BigFloat(;precision=precision) ccall(($(string(:mpfr_set_,fJ)), libmpfr), Int32, (Ref{BigFloat}, $fC, MPFRRoundingMode), z, x, r) return z end end end function BigFloat(x::Float64, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) z = BigFloat(;precision) # punt on the hard case where we might have to deal with rounding # we could use this path in all cases, but mpfr_set_d has a lot of overhead. if precision <= Base.significand_bits(Float64) ccall((:mpfr_set_d, libmpfr), Int32, (Ref{BigFloat}, Float64, MPFRRoundingMode), z, x, r) if isnan(x) && signbit(x) != signbit(z) z.sign = -z.sign end return z end z.sign = 1-2*signbit(x) if iszero(x) || !isfinite(x) if isinf(x) z.exp = Clong(2) - typemax(Clong) elseif isnan(x) z.exp = Clong(1) - typemax(Clong) else z.exp = - typemax(Clong) end return z end z.exp = 1 + exponent(x) # BigFloat doesn't have an implicit bit val = reinterpret(UInt64, significand(x))<<11 | typemin(Int64) nlimbs = (precision + 8*Core.sizeof(Limb) - 1) รท (8*Core.sizeof(Limb)) # Limb is a CLong which is a UInt32 on windows (thank M$) which makes this more complicated and slower. if Limb === UInt64 for i in 1:nlimbs-1 unsafe_store!(z.d, 0x0, i) end unsafe_store!(z.d, val, nlimbs) else for i in 1:nlimbs-2 unsafe_store!(z.d, 0x0, i) end unsafe_store!(z.d, val % UInt32, nlimbs-1) unsafe_store!(z.d, (val >> 32) % UInt32, nlimbs) end z end function BigFloat(x::BigInt, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) z = BigFloat(;precision=precision) ccall((:mpfr_set_z, libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, r) return z end BigFloat(x::Integer; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(BigInt(x)::BigInt, ROUNDING_MODE[]; precision=precision) BigFloat(x::Integer, r::MPFRRoundingMode; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(BigInt(x)::BigInt, r; precision=precision) BigFloat(x::Union{Bool,Int8,Int16,Int32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(convert(Clong, x), r; precision=precision) BigFloat(x::Union{UInt8,UInt16,UInt32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(convert(Culong, x), r; precision=precision) BigFloat(x::Union{Float16,Float32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(Float64(x), r; precision=precision) function BigFloat(x::Rational, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) setprecision(BigFloat, precision) do setrounding_raw(BigFloat, r) do BigFloat(numerator(x))::BigFloat / BigFloat(denominator(x))::BigFloat end end end function tryparse(::Type{BigFloat}, s::AbstractString; base::Integer=0, precision::Integer=DEFAULT_PRECISION[], rounding::MPFRRoundingMode=ROUNDING_MODE[]) !isempty(s) && isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base = base) z = BigFloat(precision=precision) err = ccall((:mpfr_set_str, libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, MPFRRoundingMode), z, s, base, rounding) err == 0 ? z : nothing end BigFloat(x::AbstractString, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = parse(BigFloat, x; precision=precision, rounding=r) Rational(x::BigFloat) = convert(Rational{BigInt}, x) AbstractFloat(x::BigInt) = BigFloat(x) float(::Type{BigInt}) = BigFloat BigFloat(x::Real, r::RoundingMode; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(x, convert(MPFRRoundingMode, r); precision=precision)::BigFloat BigFloat(x::AbstractString, r::RoundingMode; precision::Integer=DEFAULT_PRECISION[]) = BigFloat(x, convert(MPFRRoundingMode, r); precision=precision) ## BigFloat -> Integer _unchecked_cast(T, x::BigFloat, r::RoundingMode) = _unchecked_cast(T, x, convert(MPFRRoundingMode, r)) function _unchecked_cast(::Type{Int64}, x::BigFloat, r::MPFRRoundingMode) ccall((:__gmpfr_mpfr_get_sj,libmpfr), Cintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) end function _unchecked_cast(::Type{UInt64}, x::BigFloat, r::MPFRRoundingMode) ccall((:__gmpfr_mpfr_get_uj,libmpfr), Cuintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) end function _unchecked_cast(::Type{BigInt}, x::BigFloat, r::MPFRRoundingMode) z = BigInt() ccall((:mpfr_get_z, libmpfr), Int32, (Ref{BigInt}, Ref{BigFloat}, MPFRRoundingMode), z, x, r) return z end function _unchecked_cast(::Type{T}, x::BigFloat, r::MPFRRoundingMode) where T<:Union{Signed, Unsigned} CT = T <: Signed ? Int64 : UInt64 typemax(T) < typemax(CT) ? _unchecked_cast(CT, x, r) : _unchecked_cast(BigInt, x, r) end function round(::Type{T}, x::BigFloat, r::Union{RoundingMode, MPFRRoundingMode}) where T<:Union{Signed, Unsigned} clear_flags() res = _unchecked_cast(T, x, r) if had_range_exception() || !(typemin(T) <= res <= typemax(T)) throw(InexactError(:round, T, x)) end return unsafe_trunc(T, res) end function round(::Type{BigInt}, x::BigFloat, r::Union{RoundingMode, MPFRRoundingMode}) clear_flags() res = _unchecked_cast(BigInt, x, r) had_range_exception() && throw(InexactError(:round, BigInt, x)) return res end round(::Type{T}, x::BigFloat, r::RoundingMode) where T<:Union{Signed, Unsigned} = invoke(round, Tuple{Type{<:Union{Signed, Unsigned}}, BigFloat, Union{RoundingMode, MPFRRoundingMode}}, T, x, r) round(::Type{BigInt}, x::BigFloat, r::RoundingMode) = invoke(round, Tuple{Type{BigInt}, BigFloat, Union{RoundingMode, MPFRRoundingMode}}, BigInt, x, r) round(::Type{<:Integer}, x::BigFloat, r::RoundingMode) = throw(MethodError(round, (Integer, x, r))) unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_trunc(T, _unchecked_cast(T, x, RoundToZero)) unsafe_trunc(::Type{BigInt}, x::BigFloat) = _unchecked_cast(BigInt, x, RoundToZero) # TODO: Ideally the base fallbacks for these would already exist for (f, rnd) in zip((:trunc, :floor, :ceil, :round), (RoundToZero, RoundDown, RoundUp, :(ROUNDING_MODE[]))) @eval $f(::Type{T}, x::BigFloat) where T<:Union{Unsigned, Signed, BigInt} = round(T, x, $rnd) @eval $f(::Type{Integer}, x::BigFloat) = $f(BigInt, x) end function Bool(x::BigFloat) iszero(x) && return false isone(x) && return true throw(InexactError(:Bool, Bool, x)) end function BigInt(x::BigFloat) isinteger(x) || throw(InexactError(:BigInt, BigInt, x)) trunc(BigInt, x) end function (::Type{T})(x::BigFloat) where T<:Integer isinteger(x) || throw(InexactError(nameof(T), T, x)) trunc(T,x) end ## BigFloat -> AbstractFloat _cpynansgn(x::AbstractFloat, y::BigFloat) = isnan(x) && signbit(x) != signbit(y) ? -x : x Float64(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = _cpynansgn(ccall((:mpfr_get_d,libmpfr), Float64, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) Float64(x::BigFloat, r::RoundingMode) = Float64(x, convert(MPFRRoundingMode, r)) Float32(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = _cpynansgn(ccall((:mpfr_get_flt,libmpfr), Float32, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) Float32(x::BigFloat, r::RoundingMode) = Float32(x, convert(MPFRRoundingMode, r)) function Float16(x::BigFloat) :: Float16 res = Float32(x) resi = reinterpret(UInt32, res) if (resi&0x7fffffff) < 0x38800000 # if Float16(res) is subnormal #shift so that the mantissa lines up where it would for normal Float16 shift = 113-((resi & 0x7f800000)>>23) if shift<23 resi |= 0x0080_0000 # set implicit bit resi >>= shift end end if (resi & 0x1fff == 0x1000) # if we are halfway between 2 Float16 values # adjust the value by 1 ULP in the direction that will make Float16(res) give the right answer res = nextfloat(res, cmp(x, res)) end return res end promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat big(::Type{<:AbstractFloat}) = BigFloat big(x::AbstractFloat) = convert(BigFloat, x) function Rational{BigInt}(x::AbstractFloat) isnan(x) && return zero(BigInt) // zero(BigInt) isinf(x) && return copysign(one(BigInt),x) // zero(BigInt) iszero(x) && return zero(BigInt) // one(BigInt) s = max(precision(x) - exponent(x), 0) BigInt(ldexp(x,s)) // (BigInt(1) << s) end # Basic arithmetic without promotion for (fJ, fC) in ((:+,:add), (:*,:mul)) @eval begin # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,fC)),libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end # Unsigned Integer function ($fJ)(x::BigFloat, c::CulongMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_ui)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CulongMax, x::BigFloat) = ($fJ)(x,c) # Signed Integer function ($fJ)(x::BigFloat, c::ClongMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_si)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::ClongMax, x::BigFloat) = ($fJ)(x,c) # Float32/Float64 function ($fJ)(x::BigFloat, c::CdoubleMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_d)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CdoubleMax, x::BigFloat) = ($fJ)(x,c) # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() ccall(($(string(:mpfr_,fC,:_z)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::BigInt, x::BigFloat) = ($fJ)(x,c) end end for (fJ, fC) in ((:-,:sub), (:/,:div)) @eval begin # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,fC)),libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end # Unsigned Int function ($fJ)(x::BigFloat, c::CulongMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_ui)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CulongMax, x::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,:ui_,fC)), libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) return z end # Signed Integer function ($fJ)(x::BigFloat, c::ClongMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_si)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::ClongMax, x::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,:si_,fC)), libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) return z end # Float32/Float64 function ($fJ)(x::BigFloat, c::CdoubleMax) z = BigFloat() ccall(($(string(:mpfr_,fC,:_d)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CdoubleMax, x::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,:d_,fC)), libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) return z end # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() ccall(($(string(:mpfr_,fC,:_z)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) return z end # no :mpfr_z_div function end end function -(c::BigInt, x::BigFloat) z = BigFloat() ccall((:mpfr_z_sub, libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) return z end inv(x::BigFloat) = one(Clong) / x # faster than fallback one(x)/x function fma(x::BigFloat, y::BigFloat, z::BigFloat) r = BigFloat() ccall(("mpfr_fma",libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), r, x, y, z, ROUNDING_MODE[]) return r end muladd(x::BigFloat, y::BigFloat, z::BigFloat) = fma(x, y, z) # div # BigFloat function div(x::BigFloat, y::BigFloat) z = BigFloat() ccall((:mpfr_div,libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Unsigned Int function div(x::BigFloat, c::CulongMax) z = BigFloat() ccall((:mpfr_div_ui, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::CulongMax, x::BigFloat) z = BigFloat() ccall((:mpfr_ui_div, libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Signed Integer function div(x::BigFloat, c::ClongMax) z = BigFloat() ccall((:mpfr_div_si, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::ClongMax, x::BigFloat) z = BigFloat() ccall((:mpfr_si_div, libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # Float32/Float64 function div(x::BigFloat, c::CdoubleMax) z = BigFloat() ccall((:mpfr_div_d, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end function div(c::CdoubleMax, x::BigFloat) z = BigFloat() ccall((:mpfr_d_div, libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # BigInt function div(x::BigFloat, c::BigInt) z = BigFloat() ccall((:mpfr_div_z, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, RoundToZero) ccall((:mpfr_trunc, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end # More efficient commutative operations for (fJ, fC, fI) in ((:+, :add, 0), (:*, :mul, 1)) @eval begin function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) return z end function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, d, ROUNDING_MODE[]) return z end function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, d, ROUNDING_MODE[]) ccall(($(string(:mpfr_,fC)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, e, ROUNDING_MODE[]) return z end end end function -(x::BigFloat) z = BigFloat() ccall((:mpfr_neg, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) return z end function sqrt(x::BigFloat) isnan(x) && return x z = BigFloat() ccall((:mpfr_sqrt, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end sqrt(x::BigInt) = sqrt(BigFloat(x)) function ^(x::BigFloat, y::BigFloat) z = BigFloat() ccall((:mpfr_pow, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::CulongMax) z = BigFloat() ccall((:mpfr_pow_ui, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::ClongMax) z = BigFloat() ccall((:mpfr_pow_si, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::BigInt) z = BigFloat() ccall((:mpfr_pow_z, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end ^(x::BigFloat, y::Integer) = typemin(Clong) <= y <= typemax(Clong) ? x^Clong(y) : x^BigInt(y) ^(x::BigFloat, y::Unsigned) = typemin(Culong) <= y <= typemax(Culong) ? x^Culong(y) : x^BigInt(y) for f in (:exp, :exp2, :exp10, :expm1, :cosh, :sinh, :tanh, :sech, :csch, :coth, :cbrt) @eval function $f(x::BigFloat) z = BigFloat() ccall(($(string(:mpfr_,f)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) return z end end function sincos_fast(v::BigFloat) s = BigFloat() c = BigFloat() ccall((:mpfr_sin_cos, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), s, c, v, ROUNDING_MODE[]) return (s, c) end sincos(v::BigFloat) = sincos_fast(v) # return log(2) function big_ln2() c = BigFloat() ccall((:mpfr_const_log2, libmpfr), Cint, (Ref{BigFloat}, MPFRRoundingMode), c, MPFR.ROUNDING_MODE[]) return c end function ldexp(x::BigFloat, n::Clong) z = BigFloat() ccall((:mpfr_mul_2si, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, n, ROUNDING_MODE[]) return z end function ldexp(x::BigFloat, n::Culong) z = BigFloat() ccall((:mpfr_mul_2ui, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, n, ROUNDING_MODE[]) return z end ldexp(x::BigFloat, n::ClongMax) = ldexp(x, convert(Clong, n)) ldexp(x::BigFloat, n::CulongMax) = ldexp(x, convert(Culong, n)) ldexp(x::BigFloat, n::Integer) = x * exp2(BigFloat(n)) function factorial(x::BigFloat) if x < 0 || !isinteger(x) throw(DomainError(x, "Must be a non-negative integer.")) end ui = convert(Culong, x) z = BigFloat() ccall((:mpfr_fac_ui, libmpfr), Int32, (Ref{BigFloat}, Culong, MPFRRoundingMode), z, ui, ROUNDING_MODE[]) return z end function hypot(x::BigFloat, y::BigFloat) z = BigFloat() ccall((:mpfr_hypot, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end for f in (:log, :log2, :log10) @eval function $f(x::BigFloat) if x < 0 throw(DomainError(x, string($f, " was called with a negative real argument but ", "will only return a complex result if called ", "with a complex argument. Try ", $f, "(complex(x))."))) end z = BigFloat() ccall(($(string(:mpfr_,f)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) return z end end function log1p(x::BigFloat) if x < -1 throw(DomainError(x, string("log1p was called with a real argument < -1 but ", "will only return a complex result if called ", "with a complex argument. Try log1p(complex(x))."))) end z = BigFloat() ccall((:mpfr_log1p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) return z end # For `min`/`max`, general fallback for `AbstractFloat` is good enough. # Only implement `minmax` and `_extrema_rf` to avoid repeated calls. function minmax(x::BigFloat, y::BigFloat) isnan(x) && return x, x isnan(y) && return y, y Base.Math._isless(x, y) ? (x, y) : (y, x) end function Base._extrema_rf(x::NTuple{2,BigFloat}, y::NTuple{2,BigFloat}) (x1, x2), (y1, y2) = x, y isnan(x1) && return x isnan(y1) && return y z1 = Base.Math._isless(x1, y1) ? x1 : y1 z2 = Base.Math._isless(x2, y2) ? y2 : x2 z1, z2 end function modf(x::BigFloat) zint = BigFloat() zfloat = BigFloat() ccall((:mpfr_modf, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), zint, zfloat, x, ROUNDING_MODE[]) return (zfloat, zint) end function rem(x::BigFloat, y::BigFloat) z = BigFloat() ccall((:mpfr_fmod, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end function rem(x::BigFloat, y::BigFloat, ::RoundingMode{:Nearest}) z = BigFloat() ccall((:mpfr_remainder, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end # TODO: use a higher-precision BigFloat(pi) here? rem2pi(x::BigFloat, r::RoundingMode) = rem(x, 2*BigFloat(pi), r) function sum(arr::AbstractArray{BigFloat}) z = BigFloat(0) for i in arr ccall((:mpfr_add, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, i, ROUNDING_MODE[]) end return z end # Functions for which NaN results are converted to DomainError, following Base for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh, :sinpi, :cospi, :tanpi) @eval begin function ($f)(x::BigFloat) isnan(x) && return x z = BigFloat() ccall(($(string(:mpfr_,f)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end end end sincospi(x::BigFloat) = (sinpi(x), cospi(x)) function atan(y::BigFloat, x::BigFloat) z = BigFloat() ccall((:mpfr_atan2, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, y, x, ROUNDING_MODE[]) return z end # degree functions for f in (:sin, :cos, :tan) @eval begin function ($(Symbol(f,:d)))(x::BigFloat) isnan(x) && return x z = BigFloat() ccall(($(string(:mpfr_,f,:u)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, 360, ROUNDING_MODE[]) isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end function ($(Symbol(:a,f,:d)))(x::BigFloat) isnan(x) && return x z = BigFloat() ccall(($(string(:mpfr_a,f,:u)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, 360, ROUNDING_MODE[]) isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end end end function atand(y::BigFloat, x::BigFloat) z = BigFloat() ccall((:mpfr_atan2u, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, y, x, 360, ROUNDING_MODE[]) return z end # Utility functions ==(x::BigFloat, y::BigFloat) = ccall((:mpfr_equal_p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 <=(x::BigFloat, y::BigFloat) = ccall((:mpfr_lessequal_p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 >=(x::BigFloat, y::BigFloat) = ccall((:mpfr_greaterequal_p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 <(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 >(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 function cmp(x::BigFloat, y::BigInt) isnan(x) && return 1 ccall((:mpfr_cmp_z, libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}), x, y) end function cmp(x::BigFloat, y::ClongMax) isnan(x) && return 1 ccall((:mpfr_cmp_si, libmpfr), Int32, (Ref{BigFloat}, Clong), x, y) end function cmp(x::BigFloat, y::CulongMax) isnan(x) && return 1 ccall((:mpfr_cmp_ui, libmpfr), Int32, (Ref{BigFloat}, Culong), x, y) end cmp(x::BigFloat, y::Integer) = cmp(x,big(y)) cmp(x::Integer, y::BigFloat) = -cmp(y,x) function cmp(x::BigFloat, y::CdoubleMax) isnan(x) && return isnan(y) ? 0 : 1 isnan(y) && return -1 ccall((:mpfr_cmp_d, libmpfr), Int32, (Ref{BigFloat}, Cdouble), x, y) end cmp(x::CdoubleMax, y::BigFloat) = -cmp(y,x) ==(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) == 0 ==(x::Integer, y::BigFloat) = y == x ==(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) == 0 ==(x::CdoubleMax, y::BigFloat) = y == x <(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) < 0 <(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) > 0 <(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) < 0 <(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) > 0 <=(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) <= 0 <=(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) >= 0 <=(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) <= 0 <=(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) >= 0 signbit(x::BigFloat) = ccall((:mpfr_signbit, libmpfr), Int32, (Ref{BigFloat},), x) != 0 function sign(x::BigFloat) c = cmp(x, 0) (c == 0 || isnan(x)) && return x return c < 0 ? -one(x) : one(x) end function _precision(x::BigFloat) # precision of an object of type BigFloat return ccall((:mpfr_get_prec, libmpfr), Clong, (Ref{BigFloat},), x) end precision(x::BigFloat; base::Integer=2) = _precision(x, base) _precision(::Type{BigFloat}) = Int(DEFAULT_PRECISION[]) # default precision of the type BigFloat itself """ setprecision([T=BigFloat,] precision::Int; base=2) Set the precision (in bits, by default) to be used for `T` arithmetic. If `base` is specified, then the precision is the minimum required to give at least `precision` digits in the given `base`. !!! warning This function is not thread-safe. It will affect code running on all threads, but its behavior is undefined if called concurrently with computations that use the setting. !!! compat "Julia 1.8" The `base` keyword requires at least Julia 1.8. """ function setprecision(::Type{BigFloat}, precision::Integer; base::Integer=2) base > 1 || throw(DomainError(base, "`base` cannot be less than 2.")) precision > 0 || throw(DomainError(precision, "`precision` cannot be less than 1.")) DEFAULT_PRECISION[] = base == 2 ? precision : ceil(Int, precision * log2(base)) return precision end setprecision(precision::Integer; base::Integer=2) = setprecision(BigFloat, precision; base) maxintfloat(x::BigFloat) = BigFloat(2)^precision(x) maxintfloat(::Type{BigFloat}) = BigFloat(2)^precision(BigFloat) function copysign(x::BigFloat, y::BigFloat) z = BigFloat() ccall((:mpfr_copysign, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) return z end function exponent(x::BigFloat) if iszero(x) || !isfinite(x) throw(DomainError(x, "`x` must be non-zero and finite.")) end # The '- 1' is to make it work as Base.exponent return ccall((:mpfr_get_exp, libmpfr), Clong, (Ref{BigFloat},), x) - 1 end function frexp(x::BigFloat) z = BigFloat() c = Ref{Clong}() ccall((:mpfr_frexp, libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), c, z, x, ROUNDING_MODE[]) return (z, c[]) end function significand(x::BigFloat) z = BigFloat() c = Ref{Clong}() ccall((:mpfr_frexp, libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), c, z, x, ROUNDING_MODE[]) # Double the significand to make it work as Base.significand ccall((:mpfr_mul_si, libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, z, 2, ROUNDING_MODE[]) return z end function isinteger(x::BigFloat) return ccall((:mpfr_integer_p, libmpfr), Int32, (Ref{BigFloat},), x) != 0 end for (f,R) in ((:roundeven, :Nearest), (:ceil, :Up), (:floor, :Down), (:trunc, :ToZero), (:round, :NearestTiesAway)) @eval begin function round(x::BigFloat, ::RoundingMode{$(QuoteNode(R))}) z = BigFloat() ccall(($(string(:mpfr_,f)), libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z end end end function isinf(x::BigFloat) return ccall((:mpfr_inf_p, libmpfr), Int32, (Ref{BigFloat},), x) != 0 end function isnan(x::BigFloat) return ccall((:mpfr_nan_p, libmpfr), Int32, (Ref{BigFloat},), x) != 0 end isfinite(x::BigFloat) = !isinf(x) && !isnan(x) iszero(x::BigFloat) = x == Clong(0) isone(x::BigFloat) = x == Clong(1) @eval typemax(::Type{BigFloat}) = $(BigFloat(Inf)) @eval typemin(::Type{BigFloat}) = $(BigFloat(-Inf)) function nextfloat!(x::BigFloat, n::Integer=1) signbit(n) && return prevfloat!(x, abs(n)) for i = 1:n ccall((:mpfr_nextabove, libmpfr), Int32, (Ref{BigFloat},), x) end return x end function prevfloat!(x::BigFloat, n::Integer=1) signbit(n) && return nextfloat!(x, abs(n)) for i = 1:n ccall((:mpfr_nextbelow, libmpfr), Int32, (Ref{BigFloat},), x) end return x end nextfloat(x::BigFloat, n::Integer=1) = n == 0 ? x : nextfloat!(_duplicate(x), n) prevfloat(x::BigFloat, n::Integer=1) = n == 0 ? x : prevfloat!(_duplicate(x), n) eps(::Type{BigFloat}) = nextfloat(BigFloat(1)) - BigFloat(1) floatmin(::Type{BigFloat}) = nextfloat(zero(BigFloat)) floatmax(::Type{BigFloat}) = prevfloat(BigFloat(Inf)) """ setprecision(f::Function, [T=BigFloat,] precision::Integer; base=2) Change the `T` arithmetic precision (in the given `base`) for the duration of `f`. It is logically equivalent to: old = precision(BigFloat) setprecision(BigFloat, precision) f() setprecision(BigFloat, old) Often used as `setprecision(T, precision) do ... end` Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by `setprecision`. !!! compat "Julia 1.8" The `base` keyword requires at least Julia 1.8. """ function setprecision(f::Function, ::Type{T}, prec::Integer; kws...) where T old_prec = precision(T) setprecision(T, prec; kws...) try return f() finally setprecision(T, old_prec) end end setprecision(f::Function, prec::Integer; base::Integer=2) = setprecision(f, BigFloat, prec; base) function string_mpfr(x::BigFloat, fmt::String) pc = Ref{Ptr{UInt8}}() n = ccall((:mpfr_asprintf,libmpfr), Cint, (Ptr{Ptr{UInt8}}, Ptr{UInt8}, Ref{BigFloat}...), pc, fmt, x) p = pc[] # convert comma decimal separator to dot for i = 1:n if unsafe_load(p, i) == UInt8(',') unsafe_store!(p, '.', i) break end end str = unsafe_string(p) ccall((:mpfr_free_str, libmpfr), Cvoid, (Ptr{UInt8},), p) return str end function _prettify_bigfloat(s::String)::String mantissa, exponent = eachsplit(s, 'e') if !occursin('.', mantissa) mantissa = string(mantissa, '.') end mantissa = rstrip(mantissa, '0') if endswith(mantissa, '.') mantissa = string(mantissa, '0') end expo = parse(Int, exponent) if -5 < expo < 6 expo == 0 && return mantissa int, frac = eachsplit(mantissa, '.') if expo > 0 expo < length(frac) ? string(int, frac[1:expo], '.', frac[expo+1:end]) : string(int, frac, '0'^(expo-length(frac)), '.', '0') else neg = startswith(int, '-') neg == true && (int = lstrip(int, '-')) @assert length(int) == 1 string(neg ? '-' : "", '0', '.', '0'^(-expo-1), int, frac == "0" ? "" : frac) end else string(mantissa, 'e', exponent) end end function _string(x::BigFloat, fmt::String)::String isfinite(x) || return string(Float64(x)) _prettify_bigfloat(string_mpfr(x, fmt)) end _string(x::BigFloat) = _string(x, "%Re") _string(x::BigFloat, k::Integer) = _string(x, "%.$(k)Re") string(b::BigFloat) = _string(b) print(io::IO, b::BigFloat) = print(io, string(b)) function show(io::IO, b::BigFloat) if get(io, :compact, false)::Bool print(io, _string(b, 5)) else print(io, _string(b)) end end # get/set exponent min/max get_emax() = ccall((:mpfr_get_emax, libmpfr), Clong, ()) get_emax_min() = ccall((:mpfr_get_emax_min, libmpfr), Clong, ()) get_emax_max() = ccall((:mpfr_get_emax_max, libmpfr), Clong, ()) get_emin() = ccall((:mpfr_get_emin, libmpfr), Clong, ()) get_emin_min() = ccall((:mpfr_get_emin_min, libmpfr), Clong, ()) get_emin_max() = ccall((:mpfr_get_emin_max, libmpfr), Clong, ()) check_exponent_err(ret) = ret == 0 || throw(ArgumentError("Invalid MPFR exponent range")) set_emax!(x) = check_exponent_err(ccall((:mpfr_set_emax, libmpfr), Cint, (Clong,), x)) set_emin!(x) = check_exponent_err(ccall((:mpfr_set_emin, libmpfr), Cint, (Clong,), x)) function Base.deepcopy_internal(x::BigFloat, stackdict::IdDict) get!(stackdict, x) do # d = copy(x._d) d = x._d dโ€ฒ = GC.@preserve d unsafe_string(pointer(d), sizeof(d)) # creates a definitely-new String y = _BigFloat(x.prec, x.sign, x.exp, dโ€ฒ) #ccall((:mpfr_custom_move,libmpfr), Cvoid, (Ref{BigFloat}, Ptr{Limb}), y, d) # unnecessary return y end end function decompose(x::BigFloat)::Tuple{BigInt, Int, Int} isnan(x) && return 0, 0, 0 isinf(x) && return x.sign, 0, 0 x == 0 && return 0, 0, x.sign s = BigInt() s.size = cld(x.prec, 8*sizeof(Limb)) # limbs b = s.size * sizeof(Limb) # bytes ccall((:__gmpz_realloc2, libgmp), Cvoid, (Ref{BigInt}, Culong), s, 8b) # bits memcpy(s.d, x.d, b) s, x.exp - 8b, x.sign end function lerpi(j::Integer, d::Integer, a::BigFloat, b::BigFloat) t = BigFloat(j)/d fma(t, b, fma(-t, a, a)) end # flags clear_flags() = ccall((:mpfr_clear_flags, libmpfr), Cvoid, ()) had_underflow() = ccall((:mpfr_underflow_p, libmpfr), Cint, ()) != 0 had_overflow() = ccall((:mpfr_underflow_p, libmpfr), Cint, ()) != 0 had_nan() = ccall((:mpfr_nanflag_p, libmpfr), Cint, ()) != 0 had_inexact_exception() = ccall((:mpfr_inexflag_p, libmpfr), Cint, ()) != 0 had_range_exception() = ccall((:mpfr_erangeflag_p, libmpfr), Cint, ()) != 0 end #module T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/combinatorics.jlŒ$# This file is a part of Julia. License is MIT: https://julialang.org/license # Factorials const _fact_table64 = Vector{Int64}(undef, 20) _fact_table64[1] = 1 for n in 2:20 _fact_table64[n] = _fact_table64[n-1] * n end const _fact_table128 = Vector{UInt128}(undef, 34) _fact_table128[1] = 1 for n in 2:34 _fact_table128[n] = _fact_table128[n-1] * n end function factorial_lookup(n::Integer, table, lim) n < 0 && throw(DomainError(n, "`n` must not be negative.")) n > lim && throw(OverflowError(string(n, " is too large to look up in the table; consider using `factorial(big(", n, "))` instead"))) n == 0 && return one(n) @inbounds f = table[n] return oftype(n, f) end factorial(n::Int128) = factorial_lookup(n, _fact_table128, 33) factorial(n::UInt128) = factorial_lookup(n, _fact_table128, 34) factorial(n::Union{Int64,UInt64}) = factorial_lookup(n, _fact_table64, 20) if Int === Int32 factorial(n::Union{Int8,UInt8,Int16,UInt16}) = factorial(Int32(n)) factorial(n::Union{Int32,UInt32}) = factorial_lookup(n, _fact_table64, 12) else factorial(n::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32}) = factorial(Int64(n)) end # Basic functions for working with permutations @inline function _foldoneto(op, acc, ::Val{N}) where N @assert N::Integer > 0 if @generated quote acc_0 = acc Base.Cartesian.@nexprs $N i -> acc_{i} = op(acc_{i-1}, i) return $(Symbol(:acc_, N)) end else for i in 1:N acc = op(acc, i) end return acc end end """ isperm(v) -> Bool Return `true` if `v` is a valid permutation. # Examples ```jldoctest julia> isperm([1; 2]) true julia> isperm([1; 3]) false ``` """ isperm(A) = _isperm(A) function _isperm(A) n = length(A) used = falses(n) for a in A (0 < a <= n) && (used[a] โŠป= true) || return false end true end isperm(p::Tuple{}) = true isperm(p::Tuple{Int}) = p[1] == 1 isperm(p::Tuple{Int,Int}) = ((p[1] == 1) & (p[2] == 2)) | ((p[1] == 2) & (p[2] == 1)) function isperm(P::Tuple) valn = Val(length(P)) _foldoneto(true, valn) do b,i s = _foldoneto(false, valn) do s, j s || P[j]==i end b&s end end isperm(P::Any32) = _isperm(P) # swap columns i and j of a, in-place function swapcols!(a::AbstractMatrix, i, j) i == j && return cols = axes(a,2) @boundscheck i in cols || throw(BoundsError(a, (:,i))) @boundscheck j in cols || throw(BoundsError(a, (:,j))) for k in axes(a,1) @inbounds a[k,i],a[k,j] = a[k,j],a[k,i] end end # swap rows i and j of a, in-place function swaprows!(a::AbstractMatrix, i, j) i == j && return rows = axes(a,1) @boundscheck i in rows || throw(BoundsError(a, (:,i))) @boundscheck j in rows || throw(BoundsError(a, (:,j))) for k in axes(a,2) @inbounds a[i,k],a[j,k] = a[j,k],a[i,k] end end # like permute!! applied to each row of a, in-place in a (overwriting p). function permutecols!!(a::AbstractMatrix, p::AbstractVector{<:Integer}) require_one_based_indexing(a, p) count = 0 start = 0 while count < length(p) ptr = start = findnext(!iszero, p, start+1)::Int next = p[start] count += 1 while next != start swapcols!(a, ptr, next) p[ptr] = 0 ptr = next next = p[next] count += 1 end p[ptr] = 0 end a end function permute!!(a, p::AbstractVector{<:Integer}) require_one_based_indexing(a, p) count = 0 start = 0 while count < length(a) ptr = start = findnext(!iszero, p, start+1)::Int temp = a[start] next = p[start] count += 1 while next != start a[ptr] = a[next] p[ptr] = 0 ptr = next next = p[next] count += 1 end a[ptr] = temp p[ptr] = 0 end a end """ permute!(v, p) Permute vector `v` in-place, according to permutation `p`. No checking is done to verify that `p` is a permutation. To return a new permutation, use `v[p]`. This is generally faster than `permute!(v, p)`; it is even faster to write into a pre-allocated output array with `u .= @view v[p]`. (Even though `permute!` overwrites `v` in-place, it internally requires some allocation to keep track of which elements have been moved.) $(_DOCS_ALIASING_WARNING) See also [`invpermute!`](@ref). # Examples ```jldoctest julia> A = [1, 1, 3, 4]; julia> perm = [2, 4, 3, 1]; julia> permute!(A, perm); julia> A 4-element Vector{Int64}: 1 4 3 1 ``` """ permute!(v, p::AbstractVector) = (v .= v[p]) function invpermute!!(a, p::AbstractVector{<:Integer}) require_one_based_indexing(a, p) count = 0 start = 0 while count < length(a) start = findnext(!iszero, p, start+1)::Int temp = a[start] next = p[start] count += 1 while next != start temp_next = a[next] a[next] = temp temp = temp_next ptr = p[next] p[next] = 0 next = ptr count += 1 end a[next] = temp p[next] = 0 end a end """ invpermute!(v, p) Like [`permute!`](@ref), but the inverse of the given permutation is applied. Note that if you have a pre-allocated output array (e.g. `u = similar(v)`), it is quicker to instead employ `u[p] = v`. (`invpermute!` internally allocates a copy of the data.) $(_DOCS_ALIASING_WARNING) # Examples ```jldoctest julia> A = [1, 1, 3, 4]; julia> perm = [2, 4, 3, 1]; julia> invpermute!(A, perm); julia> A 4-element Vector{Int64}: 4 1 3 1 ``` """ invpermute!(v, p::AbstractVector) = (v[p] = v; v) """ invperm(v) Return the inverse permutation of `v`. If `B = A[v]`, then `A == B[invperm(v)]`. See also [`sortperm`](@ref), [`invpermute!`](@ref), [`isperm`](@ref), [`permutedims`](@ref). # Examples ```jldoctest julia> p = (2, 3, 1); julia> invperm(p) (3, 1, 2) julia> v = [2; 4; 3; 1]; julia> invperm(v) 4-element Vector{Int64}: 4 1 3 2 julia> A = ['a','b','c','d']; julia> B = A[v] 4-element Vector{Char}: 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) julia> B[invperm(v)] 4-element Vector{Char}: 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) ``` """ function invperm(a::AbstractVector) require_one_based_indexing(a) b = zero(a) # similar vector of zeros n = length(a) @inbounds for (i, j) in enumerate(a) ((1 <= j <= n) && b[j] == 0) || throw(ArgumentError("argument is not a permutation")) b[j] = i end b end function invperm(p::Union{Tuple{},Tuple{Int},Tuple{Int,Int}}) isperm(p) || throw(ArgumentError("argument is not a permutation")) p # in dimensions 0-2, every permutation is its own inverse end function invperm(P::Tuple) valn = Val(length(P)) ntuple(valn) do i s = _foldoneto(nothing, valn) do s, j s !== nothing && return s P[j]==i && return j nothing end s === nothing && throw(ArgumentError("argument is not a permutation")) s end end invperm(P::Any32) = Tuple(invperm(collect(P))) #XXX This function should be moved to Combinatorics.jl but is currently used by Base.DSP. """ nextprod(factors::Union{Tuple,AbstractVector}, n) Next integer greater than or equal to `n` that can be written as ``\\prod k_i^{p_i}`` for integers ``p_1``, ``p_2``, etcetera, for factors ``k_i`` in `factors`. # Examples ```jldoctest julia> nextprod((2, 3), 105) 108 julia> 2^2 * 3^3 108 ``` !!! compat "Julia 1.6" The method that accepts a tuple requires Julia 1.6 or later. """ function nextprod(a::Union{Tuple{Vararg{Integer}},AbstractVector{<:Integer}}, x::Real) if x > typemax(Int) throw(ArgumentError("unsafe for x > typemax(Int), got $x")) end k = length(a) v = fill(1, k) # current value of each counter mx = map(a -> nextpow(a,x), a) # maximum value of each counter v[1] = mx[1] # start at first case that is >= x p::widen(Int) = mx[1] # initial value of product in this case best = p icarry = 1 while v[end] < mx[end] if p >= x best = p < best ? p : best # keep the best found yet carrytest = true while carrytest p = div(p, v[icarry]) v[icarry] = 1 icarry += 1 p *= a[icarry] v[icarry] *= a[icarry] carrytest = v[icarry] > mx[icarry] && icarry < k end if p < x icarry = 1 end else while p < x p *= a[1] v[1] *= a[1] end end end # might overflow, but want predictable return type return mx[end] < best ? Int(mx[end]) : Int(best) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/irrationals.jl๖## This file is a part of Julia. License is MIT: https://julialang.org/license ## general machinery for irrational mathematical constants """ AbstractIrrational <: Real Number type representing an exact irrational value, which is automatically rounded to the correct precision in arithmetic operations with other numeric quantities. Subtypes `MyIrrational <: AbstractIrrational` should implement at least `==(::MyIrrational, ::MyIrrational)`, `hash(x::MyIrrational, h::UInt)`, and `convert(::Type{F}, x::MyIrrational) where {F <: Union{BigFloat,Float32,Float64}}`. If a subtype is used to represent values that may occasionally be rational (e.g. a square-root type that represents `โˆšn` for integers `n` will give a rational result when `n` is a perfect square), then it should also implement `isinteger`, `iszero`, `isone`, and `==` with `Real` values (since all of these default to `false` for `AbstractIrrational` types), as well as defining [`hash`](@ref) to equal that of the corresponding `Rational`. """ abstract type AbstractIrrational <: Real end """ Irrational{sym} <: AbstractIrrational Number type representing an exact irrational value denoted by the symbol `sym`, such as [`ฯ€`](@ref pi), [`โ„ฏ`](@ref) and [`ฮณ`](@ref Base.MathConstants.eulergamma). See also [`AbstractIrrational`](@ref). """ struct Irrational{sym} <: AbstractIrrational end show(io::IO, x::Irrational{sym}) where {sym} = print(io, sym) function show(io::IO, ::MIME"text/plain", x::Irrational{sym}) where {sym} if get(io, :compact, false)::Bool print(io, sym) else print(io, sym, " = ", string(float(x))[1:min(end,15)], "...") end end promote_rule(::Type{<:AbstractIrrational}, ::Type{Float16}) = Float16 promote_rule(::Type{<:AbstractIrrational}, ::Type{Float32}) = Float32 promote_rule(::Type{<:AbstractIrrational}, ::Type{<:AbstractIrrational}) = Float64 promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where {T<:Real} = promote_type(Float64, T) promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = promote_type(promote_type(S, real(T)), T) AbstractFloat(x::AbstractIrrational) = Float64(x)::Float64 Float16(x::AbstractIrrational) = Float16(Float32(x)::Float32) Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) # XXX this may change `DEFAULT_PRECISION`, thus not effect free @assume_effects :total function Rational{T}(x::AbstractIrrational) where T<:Integer o = precision(BigFloat) p = 256 while true setprecision(BigFloat, p) bx = BigFloat(x) r = rationalize(T, bx, tol=0) if abs(BigFloat(r) - bx) > eps(bx) setprecision(BigFloat, o) return r end p += 32 end end Rational{BigInt}(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(BigInt, x) instead")) @assume_effects :total function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} setprecision(BigFloat, 256) do T(BigFloat(x)::BigFloat, r) end end float(::Type{<:AbstractIrrational}) = Float64 ==(::Irrational{s}, ::Irrational{s}) where {s} = true ==(::AbstractIrrational, ::AbstractIrrational) = false <(::Irrational{s}, ::Irrational{s}) where {s} = false function <(x::AbstractIrrational, y::AbstractIrrational) Float64(x) != Float64(y) || throw(MethodError(<, (x, y))) return Float64(x) < Float64(y) end <=(::Irrational{s}, ::Irrational{s}) where {s} = true <=(x::AbstractIrrational, y::AbstractIrrational) = x==y || x Base.@irrational twoฯ€ 2*big(ฯ€) julia> twoฯ€ twoฯ€ = 6.2831853071795... julia> Base.@irrational sqrt2 1.4142135623730950488 โˆšbig(2) julia> sqrt2 sqrt2 = 1.4142135623730... julia> Base.@irrational sqrt2 1.4142135623730950488 big(2) ERROR: AssertionError: big($(Expr(:escape, :sqrt2))) isa BigFloat julia> Base.@irrational sqrt2 1.41421356237309 โˆšbig(2) ERROR: AssertionError: Float64($(Expr(:escape, :sqrt2))) == Float64(big($(Expr(:escape, :sqrt2)))) ``` """ macro irrational(sym, val, def) irrational(sym, val, def) end macro irrational(sym, def) irrational(sym, :(big($(esc(sym)))), def) end function irrational(sym, val, def) esym = esc(sym) qsym = esc(Expr(:quote, sym)) bigconvert = isa(def,Symbol) ? quote function Base.BigFloat(::Irrational{$qsym}, r::MPFR.MPFRRoundingMode=MPFR.ROUNDING_MODE[]; precision=precision(BigFloat)) c = BigFloat(;precision=precision) ccall(($(string("mpfr_const_", def)), :libmpfr), Cint, (Ref{BigFloat}, MPFR.MPFRRoundingMode), c, r) return c end end : quote function Base.BigFloat(::Irrational{$qsym}; precision=precision(BigFloat)) setprecision(BigFloat, precision) do $(esc(def)) end end end quote const $esym = Irrational{$qsym}() $bigconvert let v = $val, v64 = Float64(v), v32 = Float32(v) Base.Float64(::Irrational{$qsym}) = v64 Base.Float32(::Irrational{$qsym}) = v32 end @assert isa(big($esym), BigFloat) @assert Float64($esym) == Float64(big($esym)) @assert Float32($esym) == Float32(big($esym)) end end big(x::AbstractIrrational) = BigFloat(x) big(::Type{<:AbstractIrrational}) = BigFloat # align along = for nice Array printing function alignment(io::IO, x::AbstractIrrational) m = match(r"^(.*?)(=.*)$", sprint(show, x, context=io, sizehint=0)) m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : (length(something(m.captures[1])), length(something(m.captures[2]))) end # inv inv(x::AbstractIrrational) = 1/x T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/mathconstants.jlŒ # This file is a part of Julia. License is MIT: https://julialang.org/license """ Base.MathConstants Module containing the mathematical constants. See [`ฯ€`](@ref), [`โ„ฏ`](@ref), [`ฮณ`](@ref), [`ฯ†`](@ref) and [`catalan`](@ref). """ module MathConstants export ฯ€, pi, โ„ฏ, e, ฮณ, eulergamma, catalan, ฯ†, golden Base.@irrational ฯ€ pi Base.@irrational โ„ฏ exp(big(1)) Base.@irrational ฮณ euler Base.@irrational ฯ† (1+sqrt(big(5)))/2 Base.@irrational catalan catalan # aliases """ ฯ€ pi The constant pi. Unicode `ฯ€` can be typed by writing `\\pi` then pressing tab in the Julia REPL, and in many editors. See also: [`sinpi`](@ref), [`sincospi`](@ref), [`deg2rad`](@ref). # Examples ```jldoctest julia> pi ฯ€ = 3.1415926535897... julia> 1/2pi 0.15915494309189535 ``` """ ฯ€, const pi = ฯ€ """ โ„ฏ e The constant โ„ฏ. Unicode `โ„ฏ` can be typed by writing `\\euler` and pressing tab in the Julia REPL, and in many editors. See also: [`exp`](@ref), [`cis`](@ref), [`cispi`](@ref). # Examples ```jldoctest julia> โ„ฏ โ„ฏ = 2.7182818284590... julia> log(โ„ฏ) 1 julia> โ„ฏ^(im)ฯ€ โ‰ˆ -1 true ``` """ โ„ฏ, const e = โ„ฏ """ ฮณ eulergamma Euler's constant. # Examples ```jldoctest julia> Base.MathConstants.eulergamma ฮณ = 0.5772156649015... julia> dx = 10^-6; julia> sum(-exp(-x) * log(x) for x in dx:dx:100) * dx 0.5772078382499133 ``` """ ฮณ, const eulergamma = ฮณ """ ฯ† golden The golden ratio. # Examples ```jldoctest julia> Base.MathConstants.golden ฯ† = 1.6180339887498... julia> (2ans - 1)^2 โ‰ˆ 5 true ``` """ ฯ†, const golden = ฯ† """ catalan Catalan's constant. # Examples ```jldoctest julia> Base.MathConstants.catalan catalan = 0.9159655941772... julia> sum(log(x)/(1+x^2) for x in 1:0.01:10^6) * 0.01 0.9159466120554123 ``` """ catalan # loop over types to prevent ambiguities for ^(::Number, x) for T in (AbstractIrrational, Rational, Integer, Number, Complex) Base.:^(::Irrational{:โ„ฏ}, x::T) = exp(x) end Base.literal_pow(::typeof(^), ::Irrational{:โ„ฏ}, ::Val{p}) where {p} = exp(p) Base.log(::Irrational{:โ„ฏ}) = 1 # use 1 to correctly promote expressions like log(x)/log(โ„ฏ) Base.log(::Irrational{:โ„ฏ}, x::Number) = log(x) Base.sin(::Irrational{:ฯ€}) = 0.0 Base.cos(::Irrational{:ฯ€}) = -1.0 Base.sincos(::Irrational{:ฯ€}) = (0.0, -1.0) Base.tan(::Irrational{:ฯ€}) = 0.0 Base.cot(::Irrational{:ฯ€}) = -1/0 end # module K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/meta.jlฝ># This file is a part of Julia. License is MIT: https://julialang.org/license """ Convenience functions for metaprogramming. """ module Meta using ..CoreLogging export quot, isexpr, isidentifier, isoperator, isunaryoperator, isbinaryoperator, ispostfixoperator, replace_sourceloc!, show_sexpr, @dump using Base: isidentifier, isoperator, isunaryoperator, isbinaryoperator, ispostfixoperator import Base: isexpr """ Meta.quot(ex)::Expr Quote expression `ex` to produce an expression with head `quote`. This can for instance be used to represent objects of type `Expr` in the AST. See also the manual section about [QuoteNode](@ref man-quote-node). # Examples ```jldoctest julia> eval(Meta.quot(:x)) :x julia> dump(Meta.quot(:x)) Expr head: Symbol quote args: Array{Any}((1,)) 1: Symbol x julia> eval(Meta.quot(:(1+2))) :(1 + 2) ``` """ quot(ex) = Expr(:quote, ex) """ Meta.isexpr(ex, head[, n])::Bool Return `true` if `ex` is an `Expr` with the given type `head` and optionally that the argument list is of length `n`. `head` may be a `Symbol` or collection of `Symbol`s. For example, to check that a macro was passed a function call expression, you might use `isexpr(ex, :call)`. # Examples ```jldoctest julia> ex = :(f(x)) :(f(x)) julia> Meta.isexpr(ex, :block) false julia> Meta.isexpr(ex, :call) true julia> Meta.isexpr(ex, [:block, :call]) # multiple possible heads true julia> Meta.isexpr(ex, :call, 1) false julia> Meta.isexpr(ex, :call, 2) true ``` """ isexpr """ replace_sourceloc!(location, expr) Overwrite the caller source location for each macro call in `expr`, returning the resulting AST. This is useful when you need to wrap a macro inside a macro, and want the inner macro to see the `__source__` location of the outer macro. For example: ``` macro test_is_one(ex) replace_sourceloc!(__source__, :(@test \$(esc(ex)) == 1)) end @test_is_one 2 ``` `@test` now reports the location of the call `@test_is_one 2` to the user, rather than line 2 where `@test` is used as an implementation detail. """ function replace_sourceloc!(sourceloc, @nospecialize(ex)) if ex isa Expr if ex.head === :macrocall ex.args[2] = sourceloc end map!(e -> replace_sourceloc!(sourceloc, e), ex.args, ex.args) end return ex end """ Meta.show_sexpr([io::IO,], ex) Show expression `ex` as a lisp style S-expression. # Examples ```jldoctest julia> Meta.show_sexpr(:(f(x, g(y,z)))) (:call, :f, :x, (:call, :g, :y, :z)) ``` """ show_sexpr(ex) = show_sexpr(stdout, ex) show_sexpr(io::IO, ex) = show_sexpr(io, ex, 0) show_sexpr(io::IO, ex, indent::Int) = show(io, ex) const sexpr_indent_width = 2 function show_sexpr(io::IO, ex::QuoteNode, indent::Int) inner = indent + sexpr_indent_width print(io, "(:quote, #QuoteNode\n", " "^inner) show_sexpr(io, ex.value, inner) print(io, '\n', " "^indent, ')') end function show_sexpr(io::IO, ex::Expr, indent::Int) inner = indent + sexpr_indent_width print(io, '(') show_sexpr(io, ex.head, inner) for arg in ex.args print(io, ex.head === :block ? ",\n"*" "^inner : ", ") show_sexpr(io, arg, inner) end if isempty(ex.args) print(io, ",)") else print(io, (ex.head === :block ? "\n"*" "^indent : ""), ')') end end """ @dump expr Show every part of the representation of the given expression. Equivalent to [`dump(:(expr))`](@ref dump). """ macro dump(expr) return :(dump($(QuoteNode(expr)))) end """ lower(m, x) Takes the expression `x` and returns an equivalent expression in lowered form for executing in module `m`. See also [`code_lowered`](@ref). """ lower(m::Module, @nospecialize(x)) = ccall(:jl_expand, Any, (Any, Any), x, m) """ @lower [m] x Return lowered form of the expression `x` in module `m`. By default `m` is the module in which the macro is called. See also [`lower`](@ref). """ macro lower(code) return :(lower($__module__, $(QuoteNode(code)))) end macro lower(mod, code) return :(lower($(esc(mod)), $(QuoteNode(code)))) end ## interface to parser ## """ ParseError(msg) The expression passed to the [`parse`](@ref) function could not be interpreted as a valid Julia expression. """ struct ParseError <: Exception msg::String detail::Any end ParseError(msg::AbstractString) = ParseError(msg, nothing) function _parse_string(text::AbstractString, filename::AbstractString, lineno::Integer, index::Integer, options) if index < 1 || index > ncodeunits(text) + 1 throw(BoundsError(text, index)) end ex, offset::Int = Core._parse(text, filename, lineno, index-1, options) ex, offset+1 end """ parse(str, start; greedy=true, raise=true, depwarn=true, filename="none") Parse the expression string and return an expression (which could later be passed to eval for execution). `start` is the code unit index into `str` of the first character to start parsing at (as with all string indexing, these are not character indices). If `greedy` is `true` (default), `parse` will try to consume as much input as it can; otherwise, it will stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true` (default), syntax errors other than incomplete expressions will raise an error. If `raise` is `false`, `parse` will return an expression that will raise an error upon evaluation. If `depwarn` is `false`, deprecation warnings will be suppressed. The `filename` argument is used to display diagnostics when an error is raised. ```jldoctest julia> Meta.parse("(ฮฑ, ฮฒ) = 3, 5", 1) # start of string (:((ฮฑ, ฮฒ) = (3, 5)), 16) julia> Meta.parse("(ฮฑ, ฮฒ) = 3, 5", 1, greedy=false) (:((ฮฑ, ฮฒ)), 9) julia> Meta.parse("(ฮฑ, ฮฒ) = 3, 5", 16) # end of string (nothing, 16) julia> Meta.parse("(ฮฑ, ฮฒ) = 3, 5", 11) # index of 3 (:((3, 5)), 16) julia> Meta.parse("(ฮฑ, ฮฒ) = 3, 5", 11, greedy=false) (3, 13) ``` """ function parse(str::AbstractString, pos::Integer; filename="none", greedy::Bool=true, raise::Bool=true, depwarn::Bool=true) ex, pos = _parse_string(str, String(filename), 1, pos, greedy ? :statement : :atom) if raise && isexpr(ex, :error) err = ex.args[1] if err isa String err = ParseError(err) # For flisp parser end throw(err) end return ex, pos end """ parse(str; raise=true, depwarn=true, filename="none") Parse the expression string greedily, returning a single expression. An error is thrown if there are additional characters after the first expression. If `raise` is `true` (default), syntax errors will raise an error; otherwise, `parse` will return an expression that will raise an error upon evaluation. If `depwarn` is `false`, deprecation warnings will be suppressed. The `filename` argument is used to display diagnostics when an error is raised. ```jldoctest; filter=r"(?<=Expr\\(:error).*|(?<=Expr\\(:incomplete).*" julia> Meta.parse("x = 3") :(x = 3) julia> Meta.parse("1.0.2") ERROR: ParseError: # Error @ none:1:1 1.0.2 โ””โ”€โ”€โ”˜ โ”€โ”€ invalid numeric constant [...] julia> Meta.parse("1.0.2"; raise = false) :(\$(Expr(:error, "invalid numeric constant \"1.0.\""))) julia> Meta.parse("x = ") :(\$(Expr(:incomplete, "incomplete: premature end of input"))) ``` """ function parse(str::AbstractString; filename="none", raise::Bool=true, depwarn::Bool=true) ex, pos = parse(str, 1; filename, greedy=true, raise, depwarn) if isexpr(ex, :error) return ex end if pos <= ncodeunits(str) raise && throw(ParseError("extra token after end of expression")) return Expr(:error, "extra token after end of expression") end return ex end function parseatom(text::AbstractString, pos::Integer; filename="none", lineno=1) return _parse_string(text, String(filename), lineno, pos, :atom) end function parseall(text::AbstractString; filename="none", lineno=1) ex,_ = _parse_string(text, String(filename), lineno, 1, :all) return ex end """ partially_inline!(code::Vector{Any}, slot_replacements::Vector{Any}, type_signature::Type{<:Tuple}, static_param_values::Vector{Any}, slot_offset::Int, statement_offset::Int, boundscheck::Symbol) Return `code` after performing an in-place partial inlining pass on the Julia IR stored within it. The kind of inlining transformations performed by this function are those that are generally possible given only a runtime type signature for a method invocation and the corresponding method's lowered IR. Thus, this function is mainly useful when preparing Julia IR to be emitted from a `@generated` function. The performed transformations are: - replace slot numbers in the range `1:length(slot_replacements)` with the corresponding items in `slot_replacements` - increment other slot numbers by `slot_offset` - substitute static parameter placeholders (e.g. `Expr(:static_parameter, 1)`) with the corresponding values in `static_param_values` - increment any statement indices present in the IR (`GotoNode`s, `SSAValue`s, etc.) by `statement_offset` (useful when the caller plans to prepend new statements to the IR) - turn off boundschecking (if `boundscheck === :off`) or propagate boundschecking (if `boundscheck === :propagate`) This function is similar to `Core.Compiler.ssa_substitute!`, but works on pre-type-inference IR instead of the optimizer's IR. """ function partially_inline!(code::Vector{Any}, slot_replacements::Vector{Any}, @nospecialize(type_signature)#=::Type{<:Tuple}=#, static_param_values::Vector{Any}, slot_offset::Int, statement_offset::Int, boundscheck::Symbol) for i = 1:length(code) isassigned(code, i) || continue code[i] = _partially_inline!(code[i], slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) end return code end function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, @nospecialize(type_signature), static_param_values::Vector{Any}, slot_offset::Int, statement_offset::Int, boundscheck::Symbol) if isa(x, Core.SSAValue) return Core.SSAValue(x.id + statement_offset) end if isa(x, Core.GotoNode) return Core.GotoNode(x.label + statement_offset) end if isa(x, Core.SlotNumber) id = x.id if 1 <= id <= length(slot_replacements) return slot_replacements[id] end return Core.SlotNumber(id + slot_offset) end if isa(x, Core.NewvarNode) return Core.NewvarNode(_partially_inline!(x.slot, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck)) end if isa(x, Core.PhiNode) partially_inline!(x.values, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) x.edges .+= slot_offset return x end if isa(x, Core.ReturnNode) return Core.ReturnNode( _partially_inline!(x.val, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck), ) end if isa(x, Core.GotoIfNot) return Core.GotoIfNot( _partially_inline!(x.cond, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck), x.dest + statement_offset, ) end if isa(x, Expr) head = x.head if head === :static_parameter if isassigned(static_param_values, x.args[1]) return QuoteNode(static_param_values[x.args[1]]) end return x elseif head === :cfunction @assert !isa(type_signature, UnionAll) || !isempty(spvals) if !isa(x.args[2], QuoteNode) # very common no-op x.args[2] = _partially_inline!(x.args[2], slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) end x.args[3] = _instantiate_type_in_env(x.args[3], type_signature, static_param_values) x.args[4] = Core.svec(Any[_instantiate_type_in_env(argt, type_signature, static_param_values) for argt in x.args[4]]...) elseif head === :foreigncall @assert !isa(type_signature, UnionAll) || !isempty(static_param_values) for i = 1:length(x.args) if i == 2 x.args[2] = _instantiate_type_in_env(x.args[2], type_signature, static_param_values) elseif i == 3 x.args[3] = Core.svec(Any[_instantiate_type_in_env(argt, type_signature, static_param_values) for argt in x.args[3]]...) elseif i == 4 @assert isa(x.args[4], Int) elseif i == 5 @assert isa((x.args[5]::QuoteNode).value, Union{Symbol, Tuple{Symbol, UInt8}}) else x.args[i] = _partially_inline!(x.args[i], slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) end end elseif head === :boundscheck if boundscheck === :propagate return x elseif boundscheck === :off return false else return true end elseif head === :gotoifnot x.args[1] = _partially_inline!(x.args[1], slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) x.args[2] += statement_offset elseif head === :enter x.args[1] += statement_offset elseif head === :isdefined arg = x.args[1] # inlining a QuoteNode or literal into `Expr(:isdefined, x)` is invalid, replace with true if isa(arg, Core.SlotNumber) id = arg.id if 1 <= id <= length(slot_replacements) replacement = slot_replacements[id] if isa(replacement, Union{Core.SlotNumber, GlobalRef, Symbol}) return Expr(:isdefined, replacement) else @assert !isa(replacement, Expr) return true end end return Expr(:isdefined, Core.SlotNumber(id + slot_offset)) elseif isexpr(arg, :static_parameter) if isassigned(static_param_values, arg.args[1]) return true end return x else @assert isa(arg, Union{GlobalRef, Symbol}) return x end elseif !Core.Compiler.is_meta_expr_head(head) partially_inline!(x.args, slot_replacements, type_signature, static_param_values, slot_offset, statement_offset, boundscheck) end end return x end _instantiate_type_in_env(x, spsig, spvals) = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), x, spsig, spvals) end # module R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/stacktraces.jlบ)# This file is a part of Julia. License is MIT: https://julialang.org/license """ Tools for collecting and manipulating stack traces. Mainly used for building errors. """ module StackTraces import Base: hash, ==, show import Core: CodeInfo, MethodInstance export StackTrace, StackFrame, stacktrace """ StackFrame Stack information representing execution context, with the following fields: - `func::Symbol` The name of the function containing the execution context. - `linfo::Union{Core.MethodInstance, Method, Module, Core.CodeInfo, Nothing}` The MethodInstance or CodeInfo containing the execution context (if it could be found), \ or Module (for macro expansions)" - `file::Symbol` The path to the file containing the execution context. - `line::Int` The line number in the file containing the execution context. - `from_c::Bool` True if the code is from C. - `inlined::Bool` True if the code is from an inlined frame. - `pointer::UInt64` Representation of the pointer to the execution context as returned by `backtrace`. """ struct StackFrame # this type should be kept platform-agnostic so that profiles can be dumped on one machine and read on another "the name of the function containing the execution context" func::Symbol "the path to the file containing the execution context" file::Symbol "the line number in the file containing the execution context" line::Int "the MethodInstance or CodeInfo containing the execution context (if it could be found), \ or Module (for macro expansions)" linfo::Union{MethodInstance, Method, Module, CodeInfo, Nothing} "true if the code is from C" from_c::Bool "true if the code is from an inlined frame" inlined::Bool "representation of the pointer to the execution context as returned by `backtrace`" pointer::UInt64 # Large enough to be read losslessly on 32- and 64-bit machines. end StackFrame(func, file, line) = StackFrame(Symbol(func), Symbol(file), line, nothing, false, false, 0) """ StackTrace An alias for `Vector{StackFrame}` provided for convenience; returned by calls to `stacktrace`. """ const StackTrace = Vector{StackFrame} const empty_sym = Symbol("") const UNKNOWN = StackFrame(empty_sym, empty_sym, -1, nothing, true, false, 0) # === lookup(C_NULL) #= If the StackFrame has function and line information, we consider two of them the same if they share the same function/line information. =# function ==(a::StackFrame, b::StackFrame) return a.line == b.line && a.from_c == b.from_c && a.func == b.func && a.file == b.file && a.inlined == b.inlined # excluding linfo and pointer end function hash(frame::StackFrame, h::UInt) h += 0xf4fbda67fe20ce88 % UInt h = hash(frame.line, h) h = hash(frame.file, h) h = hash(frame.func, h) h = hash(frame.from_c, h) h = hash(frame.inlined, h) return h end """ lookup(pointer::Ptr{Cvoid}) -> Vector{StackFrame} Given a pointer to an execution context (usually generated by a call to `backtrace`), looks up stack frame context information. Returns an array of frame information for all functions inlined at that point, innermost function first. """ Base.@constprop :none function lookup(pointer::Ptr{Cvoid}) infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer, false)::Core.SimpleVector pointer = convert(UInt64, pointer) isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, pointer)] # this is equal to UNKNOWN res = Vector{StackFrame}(undef, length(infos)) for i in 1:length(infos) info = infos[i]::Core.SimpleVector @assert(length(info) == 6) res[i] = StackFrame(info[1]::Symbol, info[2]::Symbol, info[3]::Int, info[4], info[5]::Bool, info[6]::Bool, pointer) end return res end const top_level_scope_sym = Symbol("top-level scope") function lookup(ip::Union{Base.InterpreterIP,Core.Compiler.InterpreterIP}) code = ip.code if code === nothing # interpreted top-level expression with no CodeInfo return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)] end codeinfo = (code isa MethodInstance ? code.uninferred : code)::CodeInfo # prepare approximate code info if code isa MethodInstance && (meth = code.def; meth isa Method) func = meth.name file = meth.file line = meth.line else func = top_level_scope_sym file = empty_sym line = Int32(0) end i = max(ip.stmt+1, 1) # ip.stmt is 0-indexed if i > length(codeinfo.codelocs) || codeinfo.codelocs[i] == 0 return [StackFrame(func, file, line, code, false, false, 0)] end lineinfo = codeinfo.linetable[codeinfo.codelocs[i]]::Core.LineInfoNode scopes = StackFrame[] while true inlined = lineinfo.inlined_at != 0 push!(scopes, StackFrame(Base.IRShow.method_name(lineinfo)::Symbol, lineinfo.file, lineinfo.line, inlined ? nothing : code, false, inlined, 0)) inlined || break lineinfo = codeinfo.linetable[lineinfo.inlined_at]::Core.LineInfoNode end return scopes end """ stacktrace([trace::Vector{Ptr{Cvoid}},] [c_funcs::Bool=false]) -> StackTrace Return a stack trace in the form of a vector of `StackFrame`s. (By default stacktrace doesn't return C functions, but this can be enabled.) When called without specifying a trace, `stacktrace` first calls `backtrace`. """ Base.@constprop :none function stacktrace(trace::Vector{<:Union{Base.InterpreterIP,Core.Compiler.InterpreterIP,Ptr{Cvoid}}}, c_funcs::Bool=false) stack = StackTrace() for ip in trace for frame in lookup(ip) # Skip frames that come from C calls. if c_funcs || !frame.from_c push!(stack, frame) end end end return stack end Base.@constprop :none function stacktrace(c_funcs::Bool=false) stack = stacktrace(backtrace(), c_funcs) # Remove frame for this function (and any functions called by this function). remove_frames!(stack, :stacktrace) # also remove all of the non-Julia functions that led up to this point (if that list is non-empty) c_funcs && deleteat!(stack, 1:(something(findfirst(frame -> !frame.from_c, stack), 1) - 1)) return stack end """ remove_frames!(stack::StackTrace, name::Symbol) Takes a `StackTrace` (a vector of `StackFrames`) and a function name (a `Symbol`) and removes the `StackFrame` specified by the function name from the `StackTrace` (also removing all frames above the specified function). Primarily used to remove `StackTraces` functions from the `StackTrace` prior to returning it. """ function remove_frames!(stack::StackTrace, name::Symbol) deleteat!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0)) return stack end function remove_frames!(stack::StackTrace, names::Vector{Symbol}) deleteat!(stack, 1:something(findlast(frame -> frame.func in names, stack), 0)) return stack end """ remove_frames!(stack::StackTrace, m::Module) Return the `StackTrace` with all `StackFrame`s from the provided `Module` removed. """ function remove_frames!(stack::StackTrace, m::Module) filter!(f -> !from(f, m), stack) return stack end is_top_level_frame(f::StackFrame) = f.linfo isa CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) function show_spec_linfo(io::IO, frame::StackFrame) linfo = frame.linfo if linfo === nothing if frame.func === empty_sym print(io, "ip:0x", string(frame.pointer, base=16)) elseif frame.func === top_level_scope_sym print(io, "top-level scope") else Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true) end elseif linfo isa CodeInfo print(io, "top-level scope") elseif linfo isa Module Base.print_within_stacktrace(io, Base.demangle_function_name(string(frame.func)), bold=true) elseif linfo isa MethodInstance def = linfo.def if def isa Module Base.show_mi(io, linfo, #=from_stackframe=#true) else show_spec_sig(io, def, linfo.specTypes) end else m = linfo::Method show_spec_sig(io, m, m.sig) end end function show_spec_sig(io::IO, m::Method, @nospecialize(sig::Type)) if get(io, :limit, :false)::Bool if !haskey(io, :displaysize) io = IOContext(io, :displaysize => displaysize(io)) end end argnames = Base.method_argnames(m) argnames = replace(argnames, :var"#unused#" => :var"") if m.nkw > 0 # rearrange call kw_impl(kw_args..., func, pos_args...) to func(pos_args...; kw_args) kwarg_types = Any[ fieldtype(sig, i) for i = 2:(1+m.nkw) ] uw = Base.unwrap_unionall(sig)::DataType pos_sig = Base.rewrap_unionall(Tuple{uw.parameters[(m.nkw+2):end]...}, sig) kwnames = argnames[2:(m.nkw+1)] for i = 1:length(kwnames) str = string(kwnames[i])::String if endswith(str, "...") kwnames[i] = Symbol(str[1:end-3]) end end Base.show_tuple_as_call(io, m.name, pos_sig; demangle=true, kwargs=zip(kwnames, kwarg_types), argnames=argnames[m.nkw+2:end]) else Base.show_tuple_as_call(io, m.name, sig; demangle=true, argnames) end end function show(io::IO, frame::StackFrame) show_spec_linfo(io, frame) if frame.file !== empty_sym file_info = basename(string(frame.file)) print(io, " at ") print(io, file_info, ":") if frame.line >= 0 print(io, frame.line) else print(io, "?") end end if frame.inlined print(io, " [inlined]") end end function Base.parentmodule(frame::StackFrame) linfo = frame.linfo if linfo isa MethodInstance def = linfo.def if def isa Module return def else return (def::Method).module end elseif linfo isa Method return linfo.module elseif linfo isa Module return linfo else # The module is not always available (common reasons include # frames arising from the interpreter) nothing end end """ from(frame::StackFrame, filter_mod::Module) -> Bool Return whether the `frame` is from the provided `Module` """ function from(frame::StackFrame, m::Module) return parentmodule(frame) === m end end S/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/experimental.jl’0# This file is a part of Julia. License is MIT: https://julialang.org/license """ Experimental !!! warning Types, methods, or macros defined in this module are experimental and subject to change and will not have deprecations. Caveat emptor. """ module Experimental using Base: Threads, sync_varname using Base.Meta """ Const(A::Array) Mark an Array as constant/read-only. The invariant guaranteed is that you will not modify an Array (through another reference) within an `@aliasscope` scope. !!! warning Experimental API. Subject to change without deprecation. """ struct Const{T,N} <: DenseArray{T,N} a::Array{T,N} end Base.IndexStyle(::Type{<:Const}) = IndexLinear() Base.size(C::Const) = size(C.a) Base.axes(C::Const) = axes(C.a) @eval Base.getindex(A::Const, i1::Int) = (Base.@inline; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1)) @eval Base.getindex(A::Const, i1::Int, i2::Int, I::Int...) = (Base.@inline; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1, i2, I...)) """ @aliasscope expr Allows the compiler to assume that all `Const`s are not being modified through stores within this scope, even if the compiler can't prove this to be the case. !!! warning Experimental API. Subject to change without deprecation. """ macro aliasscope(body) sym = gensym() quote $(Expr(:aliasscope)) $sym = $(esc(body)) $(Expr(:popaliasscope)) $sym end end function sync_end(c::Channel{Any}) if !isready(c) # there must be at least one item to begin with close(c) return end nremaining::Int = 0 while true event = take!(c) if event === :__completion__ nremaining -= 1 if nremaining == 0 break end else nremaining += 1 schedule(Task(()->begin try wait(event) put!(c, :__completion__) catch e close(c, e) end end)) end end close(c) nothing end """ Experimental.@sync Wait until all lexically-enclosed uses of `@async`, `@spawn`, `@spawnat` and `@distributed` are complete, or at least one of them has errored. The first exception is immediately rethrown. It is the responsibility of the user to cancel any still-running operations during error handling. !!! Note This interface is experimental and subject to change or removal without notice. """ macro sync(block) var = esc(sync_varname) quote let $var = Channel(Inf) v = $(esc(block)) sync_end($var) v end end end """ Experimental.@optlevel n::Int Set the optimization level (equivalent to the `-O` command line argument) for code in the current module. Submodules inherit the setting of their parent module. Supported values are 0, 1, 2, and 3. The effective optimization level is the minimum of that specified on the command line and in per-module settings. If a `--min-optlevel` value is set on the command line, that is enforced as a lower bound. """ macro optlevel(n::Int) return Expr(:meta, :optlevel, n) end """ Experimental.@max_methods n::Int Set the maximum number of potentially-matching methods considered when running inference for methods defined in the current module. This setting affects inference of calls with incomplete knowledge of the argument types. The benefit of this setting is to avoid excessive compilation and reduce invalidation risks in poorly-inferred cases. For example, when `@max_methods 2` is set and there are two potentially-matching methods returning different types inside a function body, then Julia will compile subsequent calls for both types so that the compiled function body accounts for both possibilities. Also the compiled code is vulnerable to invalidations that would happen when either of the two methods gets invalidated. This speculative compilation and these invalidations can be avoided by setting `@max_methods 1` and allowing the compiled code to resort to runtime dispatch instead. Supported values are `1`, `2`, `3`, `4`, and `default` (currently equivalent to `3`). """ macro max_methods(n::Int) 0 < n < 5 || error("We must have that `1 <= max_methods <= 4`, but `max_methods = $n`.") return Expr(:meta, :max_methods, n) end """ Experimental.@max_methods n::Int function fname end Set the maximum number of potentially-matching methods considered when running inference for the generic function `fname`. Overrides any module-level or global inference settings for max_methods. This setting is global for the entire generic function (or more precisely the MethodTable). """ macro max_methods(n::Int, fdef::Expr) 0 < n <= 255 || error("We must have that `1 <= max_methods <= 255`, but `max_methods = $n`.") (fdef.head === :function && length(fdef.args) == 1) || error("Second argument must be a function forward declaration") return :(typeof($(esc(fdef))).name.max_methods = $(UInt8(n))) end """ Experimental.@compiler_options optimize={0,1,2,3} compile={yes,no,all,min} infer={yes,no} max_methods={default,1,2,3,...} Set compiler options for code in the enclosing module. Options correspond directly to command-line options with the same name, where applicable. The following options are currently supported: * `optimize`: Set optimization level. * `compile`: Toggle native code compilation. Currently only `min` is supported, which requests the minimum possible amount of compilation. * `infer`: Enable or disable type inference. If disabled, implies [`@nospecialize`](@ref). * `max_methods`: Maximum number of matching methods considered when running type inference. """ macro compiler_options(args...) opts = Expr(:block) for ex in args if isa(ex, Expr) && ex.head === :(=) && length(ex.args) == 2 if ex.args[1] === :optimize push!(opts.args, Expr(:meta, :optlevel, ex.args[2]::Int)) elseif ex.args[1] === :compile a = ex.args[2] a = #a === :no ? 0 : #a === :yes ? 1 : #a === :all ? 2 : a === :min ? 3 : error("invalid argument to \"compile\" option") push!(opts.args, Expr(:meta, :compile, a)) elseif ex.args[1] === :infer a = ex.args[2] a = a === false || a === :no ? 0 : a === true || a === :yes ? 1 : error("invalid argument to \"infer\" option") push!(opts.args, Expr(:meta, :infer, a)) elseif ex.args[1] === :max_methods a = ex.args[2] a = a === :default ? 3 : a isa Int ? ((0 < a < 5) ? a : error("We must have that `1 <= max_methods <= 4`, but `max_methods = $a`.")) : error("invalid argument to \"max_methods\" option") push!(opts.args, Expr(:meta, :max_methods, a)) else error("unknown option \"$(ex.args[1])\"") end else error("invalid option syntax") end end return opts end """ Experimental.@force_compile Force compilation of the block or function (Julia's built-in interpreter is blocked from executing it). # Examples ``` julia> occursin("interpreter", string(stacktrace(begin # with forced compilation Base.Experimental.@force_compile backtrace() end, true))) false julia> occursin("interpreter", string(stacktrace(begin # without forced compilation backtrace() end, true))) true ``` """ macro force_compile() Expr(:meta, :force_compile) end # UI features for errors """ Experimental.register_error_hint(handler, exceptiontype) Register a "hinting" function `handler(io, exception)` that can suggest potential ways for users to circumvent errors. `handler` should examine `exception` to see whether the conditions appropriate for a hint are met, and if so generate output to `io`. Packages should call `register_error_hint` from within their `__init__` function. For specific exception types, `handler` is required to accept additional arguments: - `MethodError`: provide `handler(io, exc::MethodError, argtypes, kwargs)`, which splits the combined arguments into positional and keyword arguments. When issuing a hint, the output should typically start with `\\n`. If you define custom exception types, your `showerror` method can support hints by calling [`Experimental.show_error_hints`](@ref). # Example ``` julia> module Hinter only_int(x::Int) = 1 any_number(x::Number) = 2 function __init__() Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs if exc.f == only_int # Color is not necessary, this is just to show it's possible. print(io, "\\nDid you mean to call ") printstyled(io, "`any_number`?", color=:cyan) end end end end ``` Then if you call `Hinter.only_int` on something that isn't an `Int` (thereby triggering a `MethodError`), it issues the hint: ``` julia> Hinter.only_int(1.0) ERROR: MethodError: no method matching only_int(::Float64) Did you mean to call `any_number`? Closest candidates are: ... ``` !!! compat "Julia 1.5" Custom error hints are available as of Julia 1.5. !!! warning This interface is experimental and subject to change or removal without notice. To insulate yourself against changes, consider putting any registrations inside an `if isdefined(Base.Experimental, :register_error_hint) ... end` block. """ function register_error_hint(@nospecialize(handler), @nospecialize(exct::Type)) list = get!(Vector{Any}, _hint_handlers, exct) push!(list, handler) return nothing end const _hint_handlers = IdDict{Type,Vector{Any}}() """ Experimental.show_error_hints(io, ex, args...) Invoke all handlers from [`Experimental.register_error_hint`](@ref) for the particular exception type `typeof(ex)`. `args` must contain any other arguments expected by the handler for that type. !!! compat "Julia 1.5" Custom error hints are available as of Julia 1.5. !!! warning This interface is experimental and subject to change or removal without notice. """ function show_error_hints(io, ex, args...) hinters = get(_hint_handlers, typeof(ex), nothing) isnothing(hinters) && return for handler in hinters try Base.invokelatest(handler, io, ex, args...) catch err tn = typeof(handler).name @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" end end end # OpaqueClosure include("opaque_closure.jl") """ Experimental.@overlay mt [function def] Define a method and add it to the method table `mt` instead of to the global method table. This can be used to implement a method override mechanism. Regular compilation will not consider these methods, and you should customize the compilation flow to look in these method tables (e.g., using [`Core.Compiler.OverlayMethodTable`](@ref)). """ macro overlay(mt, def) def = macroexpand(__module__, def) # to expand @inline, @generated, etc if !isexpr(def, [:function, :(=)]) error("@overlay requires a function Expr") end if isexpr(def.args[1], :call) def.args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1]) elseif isexpr(def.args[1], :where) def.args[1].args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1].args[1]) else error("@overlay requires a function Expr") end esc(def) end let new_mt(name::Symbol, mod::Module) = begin ccall(:jl_check_top_level_effect, Cvoid, (Any, Cstring), mod, "@MethodTable") ccall(:jl_new_method_table, Any, (Any, Any), name, mod) end @eval macro MethodTable(name::Symbol) esc(:(const $name = $$new_mt($(quot(name)), $(__module__)))) end end """ Experimental.@MethodTable(name) Create a new MethodTable in the current module, bound to `name`. This method table can be used with the [`Experimental.@overlay`](@ref) macro to define methods for a function without adding them to the global method table. """ :@MethodTable end U/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/opaque_closure.jlฏ # This file is a part of Julia. License is MIT: https://julialang.org/license """ @opaque ([type, ]args...) -> body Marks a given closure as "opaque". Opaque closures capture the world age of their creation (as opposed to their invocation). This allows for more aggressive optimization of the capture list, but trades off against the ability to inline opaque closures at the call site, if their creation is not statically visible. An argument tuple type (`type`) may optionally be specified, to specify allowed argument types in a more flexible way. In particular, the argument type may be fixed length even if the function is variadic. !!! warning This interface is experimental and subject to change or removal without notice. """ macro opaque(ex) esc(Expr(:opaque_closure, ex)) end macro opaque(ty, ex) esc(Expr(:opaque_closure, ty, ex)) end # OpaqueClosure construction from pre-inferred CodeInfo/IRCode using Core.Compiler: IRCode using Core: CodeInfo function compute_ir_rettype(ir::IRCode) rt = Union{} for i = 1:length(ir.stmts) stmt = ir.stmts[i][:inst] if isa(stmt, Core.Compiler.ReturnNode) && isdefined(stmt, :val) rt = Core.Compiler.tmerge(Core.Compiler.argextype(stmt.val, ir), rt) end end return Core.Compiler.widenconst(rt) end function compute_oc_signature(ir::IRCode, nargs::Int, isva::Bool) argtypes = Vector{Any}(undef, nargs) for i = 1:nargs argtypes[i] = Core.Compiler.widenconst(ir.argtypes[i+1]) end if isva lastarg = pop!(argtypes) if lastarg <: Tuple append!(argtypes, lastarg.parameters) else push!(argtypes, Vararg{Any}) end end return Tuple{argtypes...} end function Core.OpaqueClosure(ir::IRCode, @nospecialize env...; isva::Bool = false, do_compile::Bool = true) # NOTE: we need ir.argtypes[1] == typeof(env) ir = Core.Compiler.copy(ir) nargs = length(ir.argtypes)-1 sig = compute_oc_signature(ir, nargs, isva) rt = compute_ir_rettype(ir) src = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) src.slotnames = fill(:none, nargs+1) src.slotflags = fill(zero(UInt8), length(ir.argtypes)) src.slottypes = copy(ir.argtypes) src.rettype = rt src = Core.Compiler.ir_to_codeinf!(src, ir) return generate_opaque_closure(sig, Union{}, rt, src, nargs, isva, env...; do_compile) end function Core.OpaqueClosure(src::CodeInfo, @nospecialize env...) src.inferred || throw(ArgumentError("Expected inferred src::CodeInfo")) mi = src.parent::Core.MethodInstance sig = Base.tuple_type_tail(mi.specTypes) method = mi.def::Method nargs = method.nargs-1 isva = method.isva return generate_opaque_closure(sig, Union{}, src.rettype, src, nargs, isva, env...) end function generate_opaque_closure(@nospecialize(sig), @nospecialize(rt_lb), @nospecialize(rt_ub), src::CodeInfo, nargs::Int, isva::Bool, @nospecialize env...; mod::Module=@__MODULE__, lineno::Int=0, file::Union{Nothing,Symbol}=nothing, do_compile::Bool=true) return ccall(:jl_new_opaque_closure_from_code_info, Any, (Any, Any, Any, Any, Any, Cint, Any, Cint, Cint, Any, Cint), sig, rt_lb, rt_ub, mod, src, lineno, file, nargs, isva, env, do_compile) end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/deepcopy.jl# This file is a part of Julia. License is MIT: https://julialang.org/license # deep copying # Note: deepcopy_internal(::Any, ::IdDict) is # only exposed for specialization by libraries """ deepcopy(x) Create a deep copy of `x`: everything is copied recursively, resulting in a fully independent object. For example, deep-copying an array produces a new array whose elements are deep copies of the original elements. Calling `deepcopy` on an object should generally have the same effect as serializing and then deserializing it. While it isn't normally necessary, user-defined types can override the default `deepcopy` behavior by defining a specialized version of the function `deepcopy_internal(x::T, dict::IdDict)` (which shouldn't otherwise be used), where `T` is the type to be specialized for, and `dict` keeps track of objects copied so far within the recursion. Within the definition, `deepcopy_internal` should be used in place of `deepcopy`, and the `dict` variable should be updated as appropriate before returning. """ function deepcopy(@nospecialize x) isbitstype(typeof(x)) && return x return deepcopy_internal(x, IdDict())::typeof(x) end deepcopy_internal(x::Union{Symbol,Core.MethodInstance,Method,GlobalRef,DataType,Union,UnionAll,Task,Regex}, stackdict::IdDict) = x deepcopy_internal(x::Tuple, stackdict::IdDict) = ntuple(i->deepcopy_internal(x[i], stackdict), length(x)) deepcopy_internal(x::Module, stackdict::IdDict) = error("deepcopy of Modules not supported") function deepcopy_internal(x::SimpleVector, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x] end y = Core.svec(Any[deepcopy_internal(x[i], stackdict) for i = 1:length(x)]...) stackdict[x] = y return y end function deepcopy_internal(x::String, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x] end y = GC.@preserve x unsafe_string(pointer(x), sizeof(x)) stackdict[x] = y return y end function deepcopy_internal(@nospecialize(x), stackdict::IdDict) T = typeof(x)::DataType nf = nfields(x) if ismutable(x) if haskey(stackdict, x) return stackdict[x] end y = ccall(:jl_new_struct_uninit, Any, (Any,), T) stackdict[x] = y for i in 1:nf if isdefined(x, i) xi = getfield(x, i) xi = deepcopy_internal(xi, stackdict)::typeof(xi) ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), y, i-1, xi) end end elseif nf == 0 || isbitstype(T) y = x else flds = Vector{Any}(undef, nf) for i in 1:nf if isdefined(x, i) xi = getfield(x, i) xi = deepcopy_internal(xi, stackdict)::typeof(xi) flds[i] = xi else nf = i - 1 # rest of tail must be undefined values break end end y = ccall(:jl_new_structv, Any, (Any, Ptr{Any}, UInt32), T, flds, nf) end return y::T end function deepcopy_internal(x::Array, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x]::typeof(x) end _deepcopy_array_t(x, eltype(x), stackdict) end function _deepcopy_array_t(@nospecialize(x::Array), T, stackdict::IdDict) if isbitstype(T) return (stackdict[x]=copy(x)) end dest = similar(x) stackdict[x] = dest for i = 1:length(x) if ccall(:jl_array_isassigned, Cint, (Any, Csize_t), x, i-1) != 0 xi = ccall(:jl_arrayref, Any, (Any, Csize_t), x, i-1) if !isbits(xi) xi = deepcopy_internal(xi, stackdict)::typeof(xi) end ccall(:jl_arrayset, Cvoid, (Any, Any, Csize_t), dest, xi, i-1) end end return dest end function deepcopy_internal(x::Union{Dict,IdDict}, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x]::typeof(x) end if isbitstype(eltype(x)) return (stackdict[x] = copy(x)) end dest = empty(x) stackdict[x] = dest for (k, v) in x dest[deepcopy_internal(k, stackdict)] = deepcopy_internal(v, stackdict) end dest end function deepcopy_internal(x::AbstractLock, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x] end y = typeof(x)() stackdict[x] = y return y end function deepcopy_internal(x::GenericCondition, stackdict::IdDict) if haskey(stackdict, x) return stackdict[x] end y = typeof(x)(deepcopy_internal(x.lock, stackdict)) stackdict[x] = y return y end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/download.jl# This file is a part of Julia. License is MIT: https://julialang.org/license Downloads() = require(PkgId( UUID((0xf43a241f_c20a_4ad4, 0x852c_f6b1247861c6)), "Downloads", )) """ download(url::AbstractString, [path::AbstractString = tempname()]) -> path Download a file from the given url, saving it to the location `path`, or if not specified, a temporary path. Returns the path of the downloaded file. !!! note Since Julia 1.6, this function is deprecated and is just a thin wrapper around `Downloads.download`. In new code, you should use that function directly instead of calling this. """ download(url::AbstractString, path::AbstractString) = do_download(url, path) download(url::AbstractString) = do_download(url, nothing) function do_download(url::AbstractString, path::Union{AbstractString, Nothing}) depwarn("Base.download is deprecated; use Downloads.download instead", :download) invokelatest(Downloads().download, url, path) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/summarysize.jlฆ# This file is a part of Julia. License is MIT: https://julialang.org/license struct SummarySize seen::IdDict{Any,Any} frontier_x::Vector{Any} frontier_i::Vector{Int} exclude::Any chargeall::Any end """ Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int Compute the amount of memory, in bytes, used by all unique objects reachable from the argument. # Keyword Arguments - `exclude`: specifies the types of objects to exclude from the traversal. - `chargeall`: specifies the types of objects to always charge the size of all of their fields, even if those fields would normally be excluded. See also [`sizeof`](@ref). # Examples ```jldoctest julia> Base.summarysize(1.0) 8 julia> Base.summarysize(Ref(rand(100))) 848 julia> sizeof(Ref(rand(100))) 8 ``` """ function summarysize(obj; exclude = Union{DataType, Core.TypeName, Core.MethodInstance}, chargeall = Union{Core.TypeMapEntry, Method}) @nospecialize obj exclude chargeall ss = SummarySize(IdDict(), Any[], Int[], exclude, chargeall) size::Int = ss(obj) while !isempty(ss.frontier_x) # DFS heap traversal of everything without a specialization # BFS heap traversal of anything with a specialization x = ss.frontier_x[end] i = ss.frontier_i[end] val = nothing if isa(x, SimpleVector) nf = length(x) if isassigned(x, i) val = x[i] end elseif isa(x, Array) nf = length(x) if ccall(:jl_array_isassigned, Cint, (Any, UInt), x, i - 1) != 0 val = x[i] end else nf = nfields(x) ft = typeof(x).types if !isbitstype(ft[i]) && isdefined(x, i) val = getfield(x, i) end end if nf > i ss.frontier_i[end] = i + 1 else pop!(ss.frontier_x) pop!(ss.frontier_i) end if val !== nothing && !isa(val, Module) && (!isa(val, ss.exclude) || isa(x, ss.chargeall)) size += ss(val)::Int end end return size end (ss::SummarySize)(@nospecialize obj) = _summarysize(ss, obj) # define the general case separately to make sure it is not specialized for every type @noinline function _summarysize(ss::SummarySize, @nospecialize obj) issingletontype(typeof(obj)) && return 0 # NOTE: this attempts to discover multiple copies of the same immutable value, # and so is somewhat approximate. key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) if nfields(obj) > 0 push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end if isa(obj, UnionAll) || isa(obj, Union) # black-list of items that don't have a Core.sizeof sz = 2 * sizeof(Int) else sz = Core.sizeof(obj) end if sz == 0 # 0-field mutable structs are not unique return gc_alignment(0) end return sz end (::SummarySize)(obj::Symbol) = 0 (::SummarySize)(obj::SummarySize) = 0 function (ss::SummarySize)(obj::String) key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) return Core.sizeof(Int) + Core.sizeof(obj) end function (ss::SummarySize)(obj::DataType) key = pointer_from_objref(obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) size::Int = 7 * Core.sizeof(Int) + 6 * Core.sizeof(Int32) size += 4 * nfields(obj) + ifelse(Sys.WORD_SIZE == 64, 4, 0) size += ss(obj.parameters)::Int if isdefined(obj, :types) size += ss(obj.types)::Int end return size end function (ss::SummarySize)(obj::Core.TypeName) key = pointer_from_objref(obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) return Core.sizeof(obj) + (isdefined(obj, :mt) ? ss(obj.mt) : 0) end function (ss::SummarySize)(obj::Array) haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) headersize = 4*sizeof(Int) + 8 + max(0, ndims(obj)-2)*sizeof(Int) size::Int = headersize datakey = unsafe_convert(Ptr{Cvoid}, obj) if !haskey(ss.seen, datakey) ss.seen[datakey] = true dsize = Core.sizeof(obj) T = eltype(obj) if isbitsunion(T) # add 1 union selector byte for each element dsize += length(obj) end size += dsize if !isempty(obj) && T !== Symbol && (!Base.allocatedinline(T) || (T isa DataType && !Base.datatype_pointerfree(T))) push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end end return size end function (ss::SummarySize)(obj::SimpleVector) key = pointer_from_objref(obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) size::Int = Core.sizeof(obj) if !isempty(obj) push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end return size end function (ss::SummarySize)(obj::Module) haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) size::Int = Core.sizeof(obj) for binding in names(obj, all = true) if isdefined(obj, binding) && !isdeprecated(obj, binding) value = getfield(obj, binding) if !isa(value, Module) || parentmodule(value) === obj size += ss(value)::Int if isa(value, UnionAll) value = unwrap_unionall(value) end if isa(value, DataType) && parentmodule(value) === obj && nameof(value) === binding # charge a TypeName to its module (but not to the type) size += ss(value.name)::Int end end end end return size end function (ss::SummarySize)(obj::Task) haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) size::Int = Core.sizeof(obj) if isdefined(obj, :code) size += ss(obj.code)::Int end size += ss(obj.storage)::Int size += ss(obj.donenotify)::Int size += ss(obj.result)::Int # TODO: add stack size, and possibly traverse stack roots return size end (ss::SummarySize)(obj::BigInt) = _summarysize(ss, obj) + obj.alloc*sizeof(Base.GMP.Limb) P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/errorshow.jl<˜# This file is a part of Julia. License is MIT: https://julialang.org/license """ showerror(io, e) Show a descriptive representation of an exception object `e`. This method is used to display the exception after a call to [`throw`](@ref). # Examples ```jldoctest julia> struct MyException <: Exception msg::String end julia> function Base.showerror(io::IO, err::MyException) print(io, "MyException: ") print(io, err.msg) end julia> err = MyException("test exception") MyException("test exception") julia> sprint(showerror, err) "MyException: test exception" julia> throw(MyException("test exception")) ERROR: MyException: test exception ``` """ showerror(io::IO, ex) = show(io, ex) show_index(io::IO, x::Any) = show(io, x) show_index(io::IO, x::Slice) = show_index(io, x.indices) show_index(io::IO, x::LogicalIndex) = summary(io, x.mask) show_index(io::IO, x::OneTo) = print(io, "1:", x.stop) show_index(io::IO, x::Colon) = print(io, ':') function showerror(io::IO, ex::Meta.ParseError) if isnothing(ex.detail) print(io, "ParseError(", repr(ex.msg), ")") else showerror(io, ex.detail) end end function showerror(io::IO, ex::BoundsError) print(io, "BoundsError") if isdefined(ex, :a) print(io, ": attempt to access ") summary(io, ex.a) if isdefined(ex, :i) print(io, " at index [") if ex.i isa AbstractRange print(io, ex.i) elseif ex.i isa AbstractString show(io, ex.i) else for (i, x) in enumerate(ex.i) i > 1 && print(io, ", ") show_index(io, x) end end print(io, ']') end end Experimental.show_error_hints(io, ex) end function showerror(io::IO, ex::TypeError) print(io, "TypeError: ") if ex.expected === Bool print(io, "non-boolean (", typeof(ex.got), ") used in boolean context") else if isvarargtype(ex.got) targs = (ex.got,) elseif isa(ex.got, Type) targs = ("Type{", ex.got, "}") else targs = ("a value of type $(typeof(ex.got))",) end if ex.context == "" ctx = "in $(ex.func)" elseif ex.func === Symbol("keyword argument") ctx = "in keyword argument $(ex.context)" else ctx = "in $(ex.func), in $(ex.context)" end print(io, ctx, ", expected ", ex.expected, ", got ", targs...) end Experimental.show_error_hints(io, ex) end function showerror(io::IO, ex, bt; backtrace=true) try showerror(io, ex) finally backtrace && show_backtrace(io, bt) end end function showerror(io::IO, ex::LoadError, bt; backtrace=true) !isa(ex.error, LoadError) && print(io, "LoadError: ") showerror(io, ex.error, bt, backtrace=backtrace) print(io, "\nin expression starting at $(ex.file):$(ex.line)") end showerror(io::IO, ex::LoadError) = showerror(io, ex, []) function showerror(io::IO, ex::InitError, bt; backtrace=true) print(io, "InitError: ") showerror(io, ex.error, bt, backtrace=backtrace) print(io, "\nduring initialization of module ", ex.mod) end showerror(io::IO, ex::InitError) = showerror(io, ex, []) function showerror(io::IO, ex::DomainError) if isa(ex.val, AbstractArray) compact = get(io, :compact, true)::Bool limit = get(io, :limit, true)::Bool print(IOContext(io, :compact => compact, :limit => limit), "DomainError with ", ex.val) else print(io, "DomainError with ", ex.val) end if isdefined(ex, :msg) print(io, ":\n", ex.msg) end Experimental.show_error_hints(io, ex) nothing end function showerror(io::IO, ex::SystemError) if @static(Sys.iswindows() ? ex.extrainfo isa WindowsErrorInfo : false) errstring = Libc.FormatMessage(ex.extrainfo.errnum) extrainfo = ex.extrainfo.extrainfo else errstring = Libc.strerror(ex.errnum) extrainfo = ex.extrainfo end if extrainfo === nothing print(io, "SystemError: $(ex.prefix): ", errstring) else print(io, "SystemError (with $extrainfo): $(ex.prefix): ", errstring) end end showerror(io::IO, ::DivideError) = print(io, "DivideError: integer division error") showerror(io::IO, ::StackOverflowError) = print(io, "StackOverflowError:") showerror(io::IO, ::UndefRefError) = print(io, "UndefRefError: access to undefined reference") showerror(io::IO, ::EOFError) = print(io, "EOFError: read end of file") function showerror(io::IO, ex::ErrorException) print(io, ex.msg) if ex.msg == "type String has no field data" println(io) print(io, "Use `codeunits(str)` instead.") end end showerror(io::IO, ex::KeyError) = (print(io, "KeyError: key "); show(io, ex.key); print(io, " not found")) showerror(io::IO, ex::InterruptException) = print(io, "InterruptException:") showerror(io::IO, ex::ArgumentError) = print(io, "ArgumentError: ", ex.msg) showerror(io::IO, ex::DimensionMismatch) = print(io, "DimensionMismatch: ", ex.msg) showerror(io::IO, ex::AssertionError) = print(io, "AssertionError: ", ex.msg) showerror(io::IO, ex::OverflowError) = print(io, "OverflowError: ", ex.msg) showerror(io::IO, ex::UndefKeywordError) = print(io, "UndefKeywordError: keyword argument `$(ex.var)` not assigned") function showerror(io::IO, ex::UndefVarError) print(io, "UndefVarError: `$(ex.var)` not defined") Experimental.show_error_hints(io, ex) end function showerror(io::IO, ex::InexactError) print(io, "InexactError: ", ex.func, '(') nameof(ex.T) === ex.func || print(io, ex.T, ", ") print(io, ex.val, ')') Experimental.show_error_hints(io, ex) end function showerror(io::IO, ex::CanonicalIndexError) print(io, "CanonicalIndexError: ", ex.func, " not defined for ", ex.type) end typesof(@nospecialize args...) = Tuple{Any[ Core.Typeof(args[i]) for i in 1:length(args) ]...} function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b::DataType), color::Symbol) if a.name === b.name Base.show_type_name(io, a.name) n = length(a.parameters) n > 0 || return print(io, '{') for i = 1:n if i > length(b.parameters) printstyled(io, a.parameters[i], color=color) else print_with_compare(io::IO, a.parameters[i], b.parameters[i], color) end i < n && print(io, ',') end print(io, '}') else printstyled(io, a; color=color) end end function print_with_compare(io::IO, @nospecialize(a), @nospecialize(b), color::Symbol) if a === b print(io, a) else printstyled(io, a; color=color) end end function show_convert_error(io::IO, ex::MethodError, arg_types_param) # See #13033 T = striptype(ex.args[1]) if T === nothing print(io, "First argument to `convert` must be a Type, got ", ex.args[1]) else p2 = arg_types_param[2] print_one_line = isa(T, DataType) && isa(p2, DataType) && T.name != p2.name printstyled(io, "Cannot `convert` an object of type ") print_one_line || printstyled(io, "\n ") print_with_compare(io, p2, T, :light_green) printstyled(io, " to an object of type ") print_one_line || printstyled(io, "\n ") print_with_compare(io, T, p2, :light_red) end end function showerror(io::IO, ex::MethodError) # ex.args is a tuple type if it was thrown from `invoke` and is # a tuple of the arguments otherwise. is_arg_types = isa(ex.args, DataType) arg_types = (is_arg_types ? ex.args : typesof(ex.args...))::DataType f = ex.f meth = methods_including_ambiguous(f, arg_types) if isa(meth, MethodList) && length(meth) > 1 return showerror_ambiguous(io, meth, f, arg_types) end arg_types_param::SimpleVector = arg_types.parameters show_candidates = true print(io, "MethodError: ") ft = typeof(f) f_is_function = false kwargs = () if f === Core.kwcall && !is_arg_types f = (ex.args::Tuple)[2] ft = typeof(f) arg_types_param = arg_types_param[3:end] kwargs = pairs(ex.args[1]) ex = MethodError(f, ex.args[3:end::Int], ex.world) end name = ft.name.mt.name if f === Base.convert && length(arg_types_param) == 2 && !is_arg_types f_is_function = true show_convert_error(io, ex, arg_types_param) elseif f === mapreduce_empty || f === reduce_empty print(io, "reducing over an empty collection is not allowed; consider supplying `init` to the reducer") show_candidates = false elseif isempty(methods(f)) && isa(f, DataType) && isabstracttype(f) print(io, "no constructors have been defined for ", f) elseif isempty(methods(f)) && !isa(f, Function) && !isa(f, Type) print(io, "objects of type ", ft, " are not callable") else if ft <: Function && isempty(ft.parameters) && _isself(ft) f_is_function = true end print(io, "no method matching ") iob = IOContext(IOBuffer(), io) # for type abbreviation as in #49795; some, like `convert(T, x)`, should not abbreviate show_signature_function(iob, isa(f, Type) ? Type{f} : typeof(f)) print(iob, "(") for (i, typ) in enumerate(arg_types_param) print(iob, "::", typ) i == length(arg_types_param) || print(iob, ", ") end if !isempty(kwargs) print(iob, "; ") for (i, (k, v)) in enumerate(kwargs) print(iob, k, "::", typeof(v)) i == length(kwargs)::Int || print(iob, ", ") end end print(iob, ")") str = String(take!(unwrapcontext(iob)[1])) str = type_limited_string_from_context(io, str) print(io, str) end # catch the two common cases of element-wise addition and subtraction if (f === Base.:+ || f === Base.:-) && length(arg_types_param) == 2 # we need one array of numbers and one number, in any order if any(x -> x <: AbstractArray{<:Number}, arg_types_param) && any(x -> x <: Number, arg_types_param) nounf = f === Base.:+ ? "addition" : "subtraction" varnames = ("scalar", "array") first, second = arg_types_param[1] <: Number ? varnames : reverse(varnames) fstring = f === Base.:+ ? "+" : "-" # avoid depending on show_default for functions (invalidation) print(io, "\nFor element-wise $nounf, use broadcasting with dot syntax: $first .$fstring $second") end end if ft <: AbstractArray print(io, "\nUse square brackets [] for indexing an Array.") end # Check for local functions that shadow methods in Base if f_is_function && isdefined(Base, name) basef = getfield(Base, name) if basef !== ex.f && hasmethod(basef, arg_types) print(io, "\nYou may have intended to import ") show_unquoted(io, Expr(:., :Base, QuoteNode(name))) end end if (ex.world != typemax(UInt) && hasmethod(ex.f, arg_types) && !hasmethod(ex.f, arg_types, world = ex.world)) curworld = get_world_counter() print(io, "\nThe applicable method may be too new: running in world age $(ex.world), while current world is $(curworld).") end if !is_arg_types # Check for row vectors used where a column vector is intended. vec_args = [] hasrows = false for arg in ex.args isrow = isa(arg,Array) && ndims(arg)::Int==2 && size(arg,1)::Int==1 hasrows |= isrow push!(vec_args, isrow ? vec(arg) : arg) end if hasrows && applicable(f, vec_args...) && isempty(kwargs) print(io, "\n\nYou might have used a 2d row vector where a 1d column vector was required.", "\nNote the difference between 1d column vector [1,2,3] and 2d row vector [1 2 3].", "\nYou can convert to a column vector with the vec() function.") end end Experimental.show_error_hints(io, ex, arg_types_param, kwargs) show_candidates && try show_method_candidates(io, ex, kwargs) catch ex @error "Error showing method candidates, aborted" exception=ex,catch_backtrace() end end striptype(::Type{T}) where {T} = T striptype(::Any) = nothing function showerror_ambiguous(io::IO, meths, f, args) print(io, "MethodError: ") show_signature_function(io, isa(f, Type) ? Type{f} : typeof(f)) print(io, "(") p = args.parameters for (i,a) in enumerate(p) print(io, "::", a) i < length(p) && print(io, ", ") end println(io, ") is ambiguous.\n\nCandidates:") sigfix = Any for m in meths print(io, " ") show_method(io, m; digit_align_width=0) println(io) sigfix = typeintersect(m.sig, sigfix) end if isa(unwrap_unionall(sigfix), DataType) && sigfix <: Tuple let sigfix=sigfix if all(m->morespecific(sigfix, m.sig), meths) print(io, "\nPossible fix, define\n ") Base.show_tuple_as_call(io, :function, sigfix) else print(io, "To resolve the ambiguity, try making one of the methods more specific, or ") print(io, "adding a new method more specific than any of the existing applicable methods.") end end println(io) end nothing end #Show an error by directly calling jl_printf. #Useful in Base submodule __init__ functions where stderr isn't defined yet. function showerror_nostdio(err, msg::AbstractString) stderr_stream = ccall(:jl_stderr_stream, Ptr{Cvoid}, ()) ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, msg) ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, ":\n") ccall(:jl_static_show, Csize_t, (Ptr{Cvoid},Any), stderr_stream, err) ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, "\n") end stacktrace_expand_basepaths()::Bool = Base.get_bool_env("JULIA_STACKTRACE_EXPAND_BASEPATHS", false) === true stacktrace_contract_userdir()::Bool = Base.get_bool_env("JULIA_STACKTRACE_CONTRACT_HOMEDIR", true) === true stacktrace_linebreaks()::Bool = Base.get_bool_env("JULIA_STACKTRACE_LINEBREAKS", false) === true function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=()) is_arg_types = isa(ex.args, DataType) arg_types = is_arg_types ? ex.args : typesof(ex.args...) arg_types_param = Any[arg_types.parameters...] # Displays the closest candidates of the given function by looping over the # functions methods and counting the number of matching arguments. f = ex.f ft = typeof(f) lines = [] # These functions are special cased to only show if first argument is matched. special = f === convert || f === getindex || f === setindex! funcs = Tuple{Any,Vector{Any}}[(f, arg_types_param)] # An incorrect call method produces a MethodError for convert. # It also happens that users type convert when they mean call. So # pool MethodErrors for these two functions. if f === convert && !isempty(arg_types_param) at1 = arg_types_param[1] if isType(at1) && !Core.Compiler.has_free_typevars(at1) push!(funcs, (at1.parameters[1], arg_types_param[2:end])) end end for (func, arg_types_param) in funcs for method in methods(func) buf = IOBuffer() iob0 = iob = IOContext(buf, io) tv = Any[] if func isa Core.OpaqueClosure sig0 = signature_type(func, typeof(func).parameters[1]) else sig0 = method.sig end while isa(sig0, UnionAll) push!(tv, sig0.var) iob = IOContext(iob, :unionall_env => sig0.var) sig0 = sig0.body end sig0 = sig0::DataType s1 = sig0.parameters[1] if sig0 === Tuple || !isa(func, rewrap_unionall(s1, method.sig)) # function itself doesn't match or is a builtin continue else print(iob, " ") show_signature_function(iob, s1) end print(iob, "(") t_i = copy(arg_types_param) right_matches = 0 sig = sig0.parameters[2:end] for i = 1 : min(length(t_i), length(sig)) i > 1 && print(iob, ", ") # If isvarargtype then it checks whether the rest of the input arguments matches # the varargtype if Base.isvarargtype(sig[i]) sigstr = (unwrapva(unwrap_unionall(sig[i])), "...") j = length(t_i) else sigstr = (sig[i],) j = i end # Checks if the type of arg 1:i of the input intersects with the current method t_in = typeintersect(rewrap_unionall(Tuple{sig[1:i]...}, method.sig), rewrap_unionall(Tuple{t_i[1:j]...}, method.sig)) # If the function is one of the special cased then it should break the loop if # the type of the first argument is not matched. t_in === Union{} && special && i == 1 && break if t_in === Union{} if get(io, :color, false)::Bool let sigstr=sigstr Base.with_output_color(Base.error_color(), iob) do iob print(iob, "::", sigstr...) end end else print(iob, "!Matched::", sigstr...) end # If there is no typeintersect then the type signature from the method is # inserted in t_i this ensures if the type at the next i matches the type # signature then there will be a type intersect t_i[i] = sig[i] else right_matches += j==i ? 1 : 0 print(iob, "::", sigstr...) end end special && right_matches == 0 && continue if length(t_i) > length(sig) && !isempty(sig) && Base.isvarargtype(sig[end]) # It ensures that methods like f(a::AbstractString...) gets the correct # number of right_matches for t in arg_types_param[length(sig):end] if t <: rewrap_unionall(unwrapva(unwrap_unionall(sig[end])), method.sig) right_matches += 1 end end end if right_matches > 0 || length(arg_types_param) < 2 if length(t_i) < length(sig) # If the methods args is longer than input then the method # arguments is printed as not a match for (k, sigtype) in enumerate(sig[length(t_i)+1:end]) sigtype = isvarargtype(sigtype) ? unwrap_unionall(sigtype) : sigtype if Base.isvarargtype(sigtype) sigstr = (unwrapva(sigtype::Core.TypeofVararg), "...") else sigstr = (sigtype,) end if !((min(length(t_i), length(sig)) == 0) && k==1) print(iob, ", ") end if k == 1 && Base.isvarargtype(sigtype) # There wasn't actually a mismatch - the method match failed for # some other reason, e.g. world age. Just print the sigstr. print(iob, sigstr...) elseif get(io, :color, false)::Bool let sigstr=sigstr Base.with_output_color(Base.error_color(), iob) do iob print(iob, "::", sigstr...) end end else print(iob, "!Matched::", sigstr...) end end end kwords = kwarg_decl(method) if !isempty(kwords) print(iob, "; ") join(iob, kwords, ", ") end print(iob, ")") show_method_params(iob0, tv) file, line = updated_methodloc(method) if file === nothing file = string(method.file) end stacktrace_contract_userdir() && (file = contractuser(file)) if !isempty(kwargs)::Bool unexpected = Symbol[] if isempty(kwords) || !(any(endswith(string(kword), "...") for kword in kwords)) for (k, v) in kwargs if !(k::Symbol in kwords) push!(unexpected, k::Symbol) end end end if !isempty(unexpected) Base.with_output_color(Base.error_color(), iob) do iob plur = length(unexpected) > 1 ? "s" : "" print(iob, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"") end end end if ex.world < reinterpret(UInt, method.primary_world) print(iob, " (method too new to be called from this world context.)") elseif ex.world > reinterpret(UInt, method.deleted_world) print(iob, " (method deleted before this world age.)") end println(iob) m = parentmodule_before_main(method) modulecolor = get!(() -> popfirst!(STACKTRACE_MODULECOLORS), STACKTRACE_FIXEDCOLORS, m) print_module_path_file(iob, m, string(file), line; modulecolor, digit_align_width = 3) # TODO: indicate if it's in the wrong world push!(lines, (buf, right_matches)) end end end if !isempty(lines) # Display up to three closest candidates Base.with_output_color(:normal, io) do io print(io, "\n\nClosest candidates are:") sort!(lines, by = x -> -x[2]) i = 0 for line in lines println(io) if i >= 3 print(io, " ...") break end i += 1 print(io, String(take!(line[1]))) end println(io) # extra newline for spacing to stacktrace end end end # In case the line numbers in the source code have changed since the code was compiled, # allow packages to set a callback function that corrects them. # (Used by Revise and perhaps other packages.) # # Set this with # Base.update_stackframes_callback[] = my_updater! # where my_updater! takes a single argument and works in-place. The argument will be a # Vector{Any} storing tuples (sf::StackFrame, nrepetitions::Int), and the updater should # replace `sf` as needed. const update_stackframes_callback = Ref{Function}(identity) const STACKTRACE_MODULECOLORS = Iterators.Stateful(Iterators.cycle([:magenta, :cyan, :green, :yellow])) const STACKTRACE_FIXEDCOLORS = IdDict(Base => :light_black, Core => :light_black) function show_full_backtrace(io::IO, trace::Vector; print_linebreaks::Bool) num_frames = length(trace) ndigits_max = ndigits(num_frames) println(io, "\nStacktrace:") for (i, (frame, n)) in enumerate(trace) print_stackframe(io, i, frame, n, ndigits_max, STACKTRACE_FIXEDCOLORS, STACKTRACE_MODULECOLORS) if i < num_frames println(io) print_linebreaks && println(io) end end end const BIG_STACKTRACE_SIZE = 50 # Arbitrary constant chosen here function show_reduced_backtrace(io::IO, t::Vector) recorded_positions = IdDict{UInt, Vector{Int}}() #= For each frame of hash h, recorded_positions[h] is the list of indices i such that hash(t[i-1]) == h, ie the list of positions in which the frame appears just before. =# displayed_stackframes = [] repeated_cycle = Tuple{Int,Int,Int}[] # First: line to introuce the "cycle repetition" message # Second: length of the cycle # Third: number of repetitions frame_counter = 1 while frame_counter < length(t) (last_frame, n) = t[frame_counter] frame_counter += 1 # Indicating the next frame current_hash = hash(last_frame) positions = get(recorded_positions, current_hash, Int[]) recorded_positions[current_hash] = push!(positions, frame_counter) repetitions = 0 for index_p in length(positions)-1:-1:1 # More recent is more likely p = positions[index_p] cycle_length = frame_counter - p i = frame_counter j = p while i < length(t) && t[i] == t[j] i += 1 j += 1 end if j >= frame_counter-1 #= At least one cycle repeated =# repetitions = div(i - frame_counter + 1, cycle_length) push!(repeated_cycle, (length(displayed_stackframes), cycle_length, repetitions)) frame_counter += cycle_length * repetitions - 1 break end end if repetitions==0 push!(displayed_stackframes, (last_frame, n)) end end try invokelatest(update_stackframes_callback[], displayed_stackframes) catch end println(io, "\nStacktrace:") ndigits_max = ndigits(length(t)) push!(repeated_cycle, (0,0,0)) # repeated_cycle is never empty frame_counter = 1 for i in 1:length(displayed_stackframes) (frame, n) = displayed_stackframes[i] print_stackframe(io, frame_counter, frame, n, ndigits_max, STACKTRACE_FIXEDCOLORS, STACKTRACE_MODULECOLORS) if i < length(displayed_stackframes) println(io) stacktrace_linebreaks() && println(io) end while repeated_cycle[1][1] == i # never empty because of the initial (0,0,0) cycle_length = repeated_cycle[1][2] repetitions = repeated_cycle[1][3] popfirst!(repeated_cycle) printstyled(io, "--- the last ", cycle_length, " lines are repeated ", repetitions, " more time", repetitions>1 ? "s" : "", " ---", color = :light_black) if i < length(displayed_stackframes) println(io) stacktrace_linebreaks() && println(io) end frame_counter += cycle_length * repetitions end frame_counter += 1 end end # Print a stack frame where the module color is determined by looking up the parent module in # `modulecolordict`. If the module does not have a color, yet, a new one can be drawn # from `modulecolorcycler`. function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulecolordict, modulecolorcycler) m = Base.parentmodule(frame) modulecolor = if m !== nothing m = parentmodule_before_main(m) get!(() -> popfirst!(modulecolorcycler), modulecolordict, m) else :default end print_stackframe(io, i, frame, n, ndigits_max, modulecolor) end # Gets the topmost parent module that isn't Main function parentmodule_before_main(m::Module) while parentmodule(m) !== m pm = parentmodule(m) pm == Main && break m = pm end m end parentmodule_before_main(x) = parentmodule_before_main(parentmodule(x)) # Print a stack frame where the module color is set manually with `modulecolor`. function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulecolor) file, line = string(frame.file), frame.line file = fixup_stdlib_path(file) stacktrace_expand_basepaths() && (file = something(find_source_file(file), file)) stacktrace_contract_userdir() && (file = contractuser(file)) # Used by the REPL to make it possible to open # the location of a stackframe/method in the editor. if haskey(io, :last_shown_line_infos) push!(io[:last_shown_line_infos], (string(frame.file), frame.line)) end inlined = getfield(frame, :inlined) modul = parentmodule(frame) digit_align_width = ndigits_max + 2 # frame number print(io, " ", lpad("[" * string(i) * "]", digit_align_width)) print(io, " ") StackTraces.show_spec_linfo(IOContext(io, :backtrace=>true), frame) if n > 1 printstyled(io, " (repeats $n times)"; color=:light_black) end println(io) # @ Module path / file : line print_module_path_file(io, modul, file, line; modulecolor, digit_align_width) # inlined printstyled(io, inlined ? " [inlined]" : "", color = :light_black) end function print_module_path_file(io, modul, file, line; modulecolor = :light_black, digit_align_width = 0) printstyled(io, " " ^ digit_align_width * "@", color = :light_black) # module if modul !== nothing && modulecolor !== nothing print(io, " ") printstyled(io, modul, color = modulecolor) end # filepath stacktrace_expand_basepaths() && (file = something(find_source_file(file), file)) stacktrace_contract_userdir() && (file = contractuser(file)) print(io, " ") dir = dirname(file) !isempty(dir) && printstyled(io, dir, Filesystem.path_separator, color = :light_black) # filename, separator, line printstyled(io, basename(file), ":", line; color = :light_black, underline = true) end function show_backtrace(io::IO, t::Vector) if haskey(io, :last_shown_line_infos) empty!(io[:last_shown_line_infos]) end # t is a pre-processed backtrace (ref #12856) if t isa Vector{Any} filtered = t else filtered = process_backtrace(t) end isempty(filtered) && return if length(filtered) == 1 && StackTraces.is_top_level_frame(filtered[1][1]) f = filtered[1][1]::StackFrame if f.line == 0 && f.file === Symbol("") # don't show a single top-level frame with no location info return end end if length(filtered) > BIG_STACKTRACE_SIZE show_reduced_backtrace(IOContext(io, :backtrace => true), filtered) return else try invokelatest(update_stackframes_callback[], filtered) catch end # process_backtrace returns a Vector{Tuple{Frame, Int}} show_full_backtrace(io, filtered; print_linebreaks = stacktrace_linebreaks()) end nothing end # For improved user experience, filter out frames for include() implementation # - see #33065. See also #35371 for extended discussion of internal frames. function _simplify_include_frames(trace) kept_frames = trues(length(trace)) first_ignored = nothing for i in length(trace):-1:1 frame::StackFrame, _ = trace[i] mod = parentmodule(frame) if first_ignored === nothing if mod === Base && frame.func === :_include # Hide include() machinery by default first_ignored = i end else first_ignored = first_ignored::Int # Hack: allow `mod==nothing` as a workaround for inlined functions. # TODO: Fix this by improving debug info. if mod in (Base,Core,nothing) && 1+first_ignored-i <= 5 if frame.func === :eval kept_frames[i:first_ignored] .= false first_ignored = nothing end else # Bail out to avoid hiding frames in unexpected circumstances first_ignored = nothing end end end if first_ignored !== nothing kept_frames[1:first_ignored] .= false end return trace[kept_frames] end # Collapse frames that have the same location (in some cases) function _collapse_repeated_frames(trace) kept_frames = trues(length(trace)) last_frame = nothing for i in 1:length(trace) frame::StackFrame, _ = trace[i] if last_frame !== nothing && frame.file == last_frame.file && frame.line == last_frame.line #= Handles this case: f(g, a; kw...) = error(); @inline f(a; kw...) = f(identity, a; kw...); f(1) which otherwise ends up as: [4] #f#4 <-- useless @ ./REPL[2]:1 [inlined] [5] f(a::Int64) @ Main ./REPL[2]:1 =# if startswith(sprint(show, last_frame), "#") kept_frames[i-1] = false end #= Handles this case g(x, y=1, z=2) = error(); g(1) which otherwise ends up as: [2] g(x::Int64, y::Int64, z::Int64) @ Main ./REPL[1]:1 [3] g(x::Int64) <-- useless @ Main ./REPL[1]:1 =# if frame.linfo isa MethodInstance && last_frame.linfo isa MethodInstance && frame.linfo.def isa Method && last_frame.linfo.def isa Method m, last_m = frame.linfo.def::Method, last_frame.linfo.def::Method params, last_params = Base.unwrap_unionall(m.sig).parameters, Base.unwrap_unionall(last_m.sig).parameters if last_m.nkw != 0 pos_sig_params = last_params[(last_m.nkw+2):end] issame = true if pos_sig_params == params kept_frames[i] = false end end if length(last_params) > length(params) issame = true for i = 1:length(params) issame &= params[i] == last_params[i] end if issame kept_frames[i] = false end end end # TODO: Detect more cases that can be collapsed end last_frame = frame end return trace[kept_frames] end function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) n = 0 last_frame = StackTraces.UNKNOWN count = 0 ret = Any[] for i in eachindex(t) lkups = t[i] if lkups isa StackFrame lkups = [lkups] else lkups = StackTraces.lookup(lkups) end for lkup in lkups if lkup === StackTraces.UNKNOWN continue end if (lkup.from_c && skipC) continue end code = lkup.linfo if code isa MethodInstance def = code.def if def isa Method && def.name !== :kwcall && def.sig <: Tuple{typeof(Core.kwcall),NamedTuple,Any,Vararg} # hide kwcall() methods, which are probably internal keyword sorter methods # (we print the internal method instead, after demangling # the argument list, since it has the right line number info) continue end elseif !lkup.from_c lkup.func === :kwcall && continue end count += 1 if count > limit break end if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== last_frame.linfo if n > 0 push!(ret, (last_frame, n)) end n = 1 last_frame = lkup else n += 1 end end count > limit && break end if n > 0 push!(ret, (last_frame, n)) end trace = _simplify_include_frames(ret) trace = _collapse_repeated_frames(trace) return trace end function show_exception_stack(io::IO, stack) # Display exception stack with the top of the stack first. This ordering # means that the user doesn't have to scroll up in the REPL to discover the # root cause. nexc = length(stack) for i = nexc:-1:1 if nexc != i printstyled(io, "\ncaused by: ", color=error_color()) end exc, bt = stack[i] showerror(io, exc, bt, backtrace = bt!==nothing) i == 1 || println(io) end end # Defined here rather than error.jl for bootstrap ordering function show(io::IO, ip::InterpreterIP) print(io, typeof(ip)) if ip.code isa Core.CodeInfo print(io, " in top-level CodeInfo for $(ip.mod) at statement $(Int(ip.stmt))") else print(io, " in $(ip.code) at statement $(Int(ip.stmt))") end end # handler for displaying a hint in case the user tries to call # the instance of a number (probably missing the operator) # eg: (1 + 2)(3 + 4) function noncallable_number_hint_handler(io, ex, arg_types, kwargs) @nospecialize if ex.f isa Number print(io, "\nMaybe you forgot to use an operator such as ") printstyled(io, "*, ^, %, / etc. ", color=:cyan) print(io, "?") end end Experimental.register_error_hint(noncallable_number_hint_handler, MethodError) # Display a hint in case the user tries to use the + operator on strings # (probably attempting concatenation) function string_concatenation_hint_handler(io, ex, arg_types, kwargs) @nospecialize if (ex.f === +) && all(i -> i <: AbstractString, arg_types) print(io, "\nString concatenation is performed with ") printstyled(io, "*", color=:cyan) print(io, " (See also: https://docs.julialang.org/en/v1/manual/strings/#man-concatenation).") end end Experimental.register_error_hint(string_concatenation_hint_handler, MethodError) # ExceptionStack implementation size(s::ExceptionStack) = size(s.stack) getindex(s::ExceptionStack, i::Int) = s.stack[i] function show(io::IO, ::MIME"text/plain", stack::ExceptionStack) nexc = length(stack) printstyled(io, nexc, "-element ExceptionStack", nexc == 0 ? "" : ":\n") show_exception_stack(io, stack) end show(io::IO, stack::ExceptionStack) = show(io, MIME("text/plain"), stack) O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/initdefs.jlภC# This file is a part of Julia. License is MIT: https://julialang.org/license ## initdefs.jl - initialization and runtime management definitions """ PROGRAM_FILE A string containing the script name passed to Julia from the command line. Note that the script name remains unchanged from within included files. Alternatively see [`@__FILE__`](@ref). """ global PROGRAM_FILE = "" """ ARGS An array of the command line arguments passed to Julia, as strings. """ const ARGS = String[] """ exit(code=0) Stop the program with an exit code. The default exit code is zero, indicating that the program completed successfully. In an interactive session, `exit()` can be called with the keyboard shortcut `^D`. """ exit(n) = ccall(:jl_exit, Cvoid, (Int32,), n) exit() = exit(0) const roottask = current_task() is_interactive = false """ isinteractive() -> Bool Determine whether Julia is running an interactive session. """ isinteractive() = (is_interactive::Bool) ## package depots (registries, packages, environments) ## """ DEPOT_PATH A stack of "depot" locations where the package manager, as well as Julia's code loading mechanisms, look for package registries, installed packages, named environments, repo clones, cached compiled package images, and configuration files. By default it includes: 1. `~/.julia` where `~` is the user home as appropriate on the system; 2. an architecture-specific shared system directory, e.g. `/usr/local/share/julia`; 3. an architecture-independent shared system directory, e.g. `/usr/share/julia`. So `DEPOT_PATH` might be: ```julia [joinpath(homedir(), ".julia"), "/usr/local/share/julia", "/usr/share/julia"] ``` The first entry is the "user depot" and should be writable by and owned by the current user. The user depot is where: registries are cloned, new package versions are installed, named environments are created and updated, package repos are cloned, newly compiled package image files are saved, log files are written, development packages are checked out by default, and global configuration data is saved. Later entries in the depot path are treated as read-only and are appropriate for registries, packages, etc. installed and managed by system administrators. `DEPOT_PATH` is populated based on the [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) environment variable if set. ## DEPOT_PATH contents Each entry in `DEPOT_PATH` is a path to a directory which contains subdirectories used by Julia for various purposes. Here is an overview of some of the subdirectories that may exist in a depot: * `artifacts`: Contains content that packages use for which Pkg manages the installation of. * `clones`: Contains full clones of package repos. Maintained by `Pkg.jl` and used as a cache. * `config`: Contains julia-level configuration such as a `startup.jl` * `compiled`: Contains precompiled `*.ji` files for packages. Maintained by Julia. * `dev`: Default directory for `Pkg.develop`. Maintained by `Pkg.jl` and the user. * `environments`: Default package environments. For instance the global environment for a specific julia version. Maintained by `Pkg.jl`. * `logs`: Contains logs of `Pkg` and `REPL` operations. Maintained by `Pkg.jl` and `Julia`. * `packages`: Contains packages, some of which were explicitly installed and some which are implicit dependencies. Maintained by `Pkg.jl`. * `registries`: Contains package registries. By default only `General`. Maintained by `Pkg.jl`. * `scratchspaces`: Contains content that a package itself installs via the [`Scratch.jl`](https://github.com/JuliaPackaging/Scratch.jl) package. `Pkg.gc()` will delete content that is known to be unused. !!! note Packages that want to store content should use the `scratchspaces` subdirectory via [`Scratch.jl`](https://github.com/JuliaPackaging/Scratch.jl) instead of creating new subdirectories in the depot root. See also [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH), and [Code Loading](@ref code-loading). """ const DEPOT_PATH = String[] function append_default_depot_path!(DEPOT_PATH) path = joinpath(homedir(), ".julia") path in DEPOT_PATH || push!(DEPOT_PATH, path) path = abspath(Sys.BINDIR, "..", "local", "share", "julia") path in DEPOT_PATH || push!(DEPOT_PATH, path) path = abspath(Sys.BINDIR, "..", "share", "julia") path in DEPOT_PATH || push!(DEPOT_PATH, path) return DEPOT_PATH end function init_depot_path() empty!(DEPOT_PATH) if haskey(ENV, "JULIA_DEPOT_PATH") str = ENV["JULIA_DEPOT_PATH"] isempty(str) && return for path in eachsplit(str, Sys.iswindows() ? ';' : ':') if isempty(path) append_default_depot_path!(DEPOT_PATH) else path = expanduser(path) path in DEPOT_PATH || push!(DEPOT_PATH, path) end end else append_default_depot_path!(DEPOT_PATH) end nothing end ## LOAD_PATH & ACTIVE_PROJECT ## # JULIA_LOAD_PATH: split on `:` (or `;` on Windows) # first empty entry is replaced with DEFAULT_LOAD_PATH, the rest are skipped # entries starting with `@` are named environments: # - the first three `#`s in a named environment are replaced with version numbers # - `@stdlib` is a special name for the standard library and expands to its path # if you want a current env setup, use direnv and # have your .envrc do something like this: # # export JULIA_LOAD_PATH="$(pwd):$JULIA_LOAD_PATH" # # this will inherit an existing JULIA_LOAD_PATH value or if there is none, leave # a trailing empty entry in JULIA_LOAD_PATH which will be replaced with defaults. const DEFAULT_LOAD_PATH = ["@", "@v#.#", "@stdlib"] """ LOAD_PATH An array of paths for `using` and `import` statements to consider as project environments or package directories when loading code. It is populated based on the [`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH) environment variable if set; otherwise it defaults to `["@", "@v#.#", "@stdlib"]`. Entries starting with `@` have special meanings: - `@` refers to the "current active environment", the initial value of which is initially determined by the [`JULIA_PROJECT`](@ref JULIA_PROJECT) environment variable or the `--project` command-line option. - `@stdlib` expands to the absolute path of the current Julia installation's standard library directory. - `@name` refers to a named environment, which are stored in depots (see [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH)) under the `environments` subdirectory. The user's named environments are stored in `~/.julia/environments` so `@name` would refer to the environment in `~/.julia/environments/name` if it exists and contains a `Project.toml` file. If `name` contains `#` characters, then they are replaced with the major, minor and patch components of the Julia version number. For example, if you are running Julia 1.2 then `@v#.#` expands to `@v1.2` and will look for an environment by that name, typically at `~/.julia/environments/v1.2`. The fully expanded value of `LOAD_PATH` that is searched for projects and packages can be seen by calling the `Base.load_path()` function. See also [`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH), [`JULIA_PROJECT`](@ref JULIA_PROJECT), [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH), and [Code Loading](@ref code-loading). """ const LOAD_PATH = copy(DEFAULT_LOAD_PATH) # HOME_PROJECT is no longer used, here just to avoid breaking things const HOME_PROJECT = Ref{Union{String,Nothing}}(nothing) const ACTIVE_PROJECT = Ref{Union{String,Nothing}}(nothing) # Modify this only via `Base.set_active_project(proj)` ## Watchers for when the active project changes (e.g., Revise) # Each should be a thunk, i.e., `f()`. To determine the current active project, # the thunk can query `Base.active_project()`. const active_project_callbacks = [] function current_project(dir::AbstractString) # look for project file in current dir and parents home = homedir() while true for proj in project_names file = joinpath(dir, proj) isfile_casesensitive(file) && return file end # bail at home directory dir == home && break old, dir = dir, dirname(dir) dir == old && break end end function current_project() dir = try pwd() catch err err isa IOError || rethrow() return nothing end return current_project(dir) end function parse_load_path(str::String) envs = String[] isempty(str) && return envs for env in eachsplit(str, Sys.iswindows() ? ';' : ':') if isempty(env) for envโ€ฒ in DEFAULT_LOAD_PATH envโ€ฒ in envs || push!(envs, envโ€ฒ) end else if env == "@." env = current_project() env === nothing && continue end env = expanduser(env) env in envs || push!(envs, env) end end return envs end function init_load_path() if haskey(ENV, "JULIA_LOAD_PATH") paths = parse_load_path(ENV["JULIA_LOAD_PATH"]) else paths = filter!(env -> env !== nothing, String[env == "@." ? current_project() : env for env in DEFAULT_LOAD_PATH]) end append!(empty!(LOAD_PATH), paths) end function init_active_project() project = (JLOptions().project != C_NULL ? unsafe_string(Base.JLOptions().project) : get(ENV, "JULIA_PROJECT", nothing)) set_active_project( project === nothing ? nothing : project == "" ? nothing : startswith(project, "@") ? load_path_expand(project) : abspath(expanduser(project)) ) end ## load path expansion: turn LOAD_PATH entries into concrete paths ## function load_path_expand(env::AbstractString)::Union{String, Nothing} # named environment? if startswith(env, '@') # `@` in JULIA_LOAD_PATH is expanded early (at startup time) # if you put a `@` in LOAD_PATH manually, it's expanded late env == "@" && return active_project(false) env == "@." && return current_project() env == "@stdlib" && return Sys.STDLIB env = replace(env, '#' => VERSION.major, count=1) env = replace(env, '#' => VERSION.minor, count=1) env = replace(env, '#' => VERSION.patch, count=1) name = env[2:end] # look for named env in each depot for depot in DEPOT_PATH path = joinpath(depot, "environments", name) isdir(path) || continue for proj in project_names file = abspath(path, proj) isfile_casesensitive(file) && return file end end isempty(DEPOT_PATH) && return nothing return abspath(DEPOT_PATH[1], "environments", name, project_names[end]) end # otherwise, it's a path path = abspath(env) if isdir(path) # directory with a project file? for proj in project_names file = joinpath(path, proj) isfile_casesensitive(file) && return file end end # package dir or path to project file return path end load_path_expand(::Nothing) = nothing """ active_project() Return the path of the active `Project.toml` file. See also [`Base.set_active_project`](@ref). """ function active_project(search_load_path::Bool=true) for project in (ACTIVE_PROJECT[],) project == "@" && continue project = load_path_expand(project) project === nothing && continue # while this seems well-inferred, nevertheless without the type annotation below # there are backedges here from abspath(::AbstractString, ::String) project = project::String if !isfile_casesensitive(project) && basename(project) โˆ‰ project_names project = abspath(project, "Project.toml") end return project end search_load_path || return for project in LOAD_PATH project == "@" && continue project = load_path_expand(project) project === nothing && continue isfile_casesensitive(project) && return project ispath(project) && continue basename(project) in project_names && return project end end """ set_active_project(projfile::Union{AbstractString,Nothing}) Set the active `Project.toml` file to `projfile`. See also [`Base.active_project`](@ref). !!! compat "Julia 1.8" This function requires at least Julia 1.8. """ function set_active_project(projfile::Union{AbstractString,Nothing}) ACTIVE_PROJECT[] = projfile for f in active_project_callbacks try Base.invokelatest(f) catch @error "active project callback $f failed" maxlog=1 end end end """ load_path() Return the fully expanded value of [`LOAD_PATH`](@ref) that is searched for projects and packages. !!! note `load_path` may return a reference to a cached value so it is not safe to modify the returned vector. """ function load_path() cache = LOADING_CACHE[] cache !== nothing && return cache.load_path paths = String[] for env in LOAD_PATH path = load_path_expand(env) path !== nothing && path โˆ‰ paths && push!(paths, path) end return paths end ## atexit: register exit hooks ## const atexit_hooks = Callable[ () -> Filesystem.temp_cleanup_purge(force=true) ] const _atexit_hooks_lock = ReentrantLock() global _atexit_hooks_finished::Bool = false """ atexit(f) Register a zero- or one-argument function `f()` to be called at process exit. `atexit()` hooks are called in last in first out (LIFO) order and run before object finalizers. If `f` has a method defined for one integer argument, it will be called as `f(n::Int32)`, where `n` is the current exit code, otherwise it will be called as `f()`. !!! compat "Julia 1.9" The one-argument form requires Julia 1.9 Exit hooks are allowed to call `exit(n)`, in which case Julia will exit with exit code `n` (instead of the original exit code). If more than one exit hook calls `exit(n)`, then Julia will exit with the exit code corresponding to the last called exit hook that calls `exit(n)`. (Because exit hooks are called in LIFO order, "last called" is equivalent to "first registered".) Note: Once all exit hooks have been called, no more exit hooks can be registered, and any call to `atexit(f)` after all hooks have completed will throw an exception. This situation may occur if you are registering exit hooks from background Tasks that may still be executing concurrently during shutdown. """ function atexit(f::Function) Base.@lock _atexit_hooks_lock begin _atexit_hooks_finished && error("cannot register new atexit hook; already exiting.") pushfirst!(atexit_hooks, f) return nothing end end function _atexit(exitcode::Cint) # Don't hold the lock around the iteration, just in case any other thread executing in # parallel tries to register a new atexit hook while this is running. We don't want to # block that thread from proceeding, and we can allow it to register its hook which we # will immediately run here. while true local f Base.@lock _atexit_hooks_lock begin # If this is the last iteration, atomically disable atexit hooks to prevent # someone from registering a hook that will never be run. # (We do this inside the loop, so that it is atomic: no one can have registered # a hook that never gets run, and we run all the hooks we know about until # the vector is empty.) if isempty(atexit_hooks) global _atexit_hooks_finished = true break end f = popfirst!(atexit_hooks) end try if hasmethod(f, (Cint,)) f(exitcode) else f() end catch ex showerror(stderr, ex) Base.show_backtrace(stderr, catch_backtrace()) println(stderr) end end end ## postoutput: register post output hooks ## ## like atexit but runs after any requested output. ## any hooks saved in the sysimage are cleared in Base._start const postoutput_hooks = Callable[] postoutput(f::Function) = (pushfirst!(postoutput_hooks, f); nothing) function _postoutput() while !isempty(postoutput_hooks) f = popfirst!(postoutput_hooks) try f() catch ex showerror(stderr, ex) Base.show_backtrace(stderr, catch_backtrace()) println(stderr) end end end ## hook for disabling threaded libraries ## library_threading_enabled = true const disable_library_threading_hooks = [] function at_disable_library_threading(f) push!(disable_library_threading_hooks, f) if !library_threading_enabled disable_library_threading() end return end function disable_library_threading() global library_threading_enabled = false while !isempty(disable_library_threading_hooks) f = pop!(disable_library_threading_hooks) try f() catch err @warn("a hook from a library to disable threading failed:", exception = (err, catch_backtrace())) end end return end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/threadcall.jl๕# This file is a part of Julia. License is MIT: https://julialang.org/license const max_ccall_threads = parse(Int, get(ENV, "UV_THREADPOOL_SIZE", "4")) const thread_notifiers = Union{Base.Condition, Nothing}[nothing for i in 1:max_ccall_threads] const threadcall_restrictor = Semaphore(max_ccall_threads) """ @threadcall((cfunc, clib), rettype, (argtypes...), argvals...) The `@threadcall` macro is called in the same way as [`ccall`](@ref) but does the work in a different thread. This is useful when you want to call a blocking C function without causing the current `julia` thread to become blocked. Concurrency is limited by size of the libuv thread pool, which defaults to 4 threads but can be increased by setting the `UV_THREADPOOL_SIZE` environment variable and restarting the `julia` process. Note that the called function should never call back into Julia. """ macro threadcall(f, rettype, argtypes, argvals...) # check for usage errors isa(argtypes,Expr) && argtypes.head === :tuple || error("threadcall: argument types must be a tuple") length(argtypes.args) == length(argvals) || error("threadcall: wrong number of arguments to C function") # hygiene escape arguments f = esc(f) rettype = esc(rettype) argtypes = map(esc, argtypes.args) argvals = map(esc, argvals) # construct non-allocating wrapper to call C function wrapper = :(function (fptr::Ptr{Cvoid}, args_ptr::Ptr{Cvoid}, retval_ptr::Ptr{Cvoid}) p = args_ptr # the rest of the body is created below end) body = wrapper.args[2].args args = Symbol[] for (i, T) in enumerate(argtypes) arg = Symbol("arg", i) push!(body, :($arg = unsafe_load(convert(Ptr{$T}, p)))) push!(body, :(p += Core.sizeof($T))) push!(args, arg) end push!(body, :(ret = ccall(fptr, $rettype, ($(argtypes...),), $(args...)))) push!(body, :(unsafe_store!(convert(Ptr{$rettype}, retval_ptr), ret))) push!(body, :(return Int(Core.sizeof($rettype)))) # return code to generate wrapper function and send work request thread queue wrapper = Expr(:var"hygienic-scope", wrapper, @__MODULE__, __source__) return :(let fun_ptr = @cfunction($wrapper, Int, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid})) # use cglobal to look up the function on the calling thread do_threadcall(fun_ptr, cglobal($f), $rettype, Any[$(argtypes...)], Any[$(argvals...)]) end) end function do_threadcall(fun_ptr::Ptr{Cvoid}, cfptr::Ptr{Cvoid}, rettype::Type, argtypes::Vector, argvals::Vector) # generate function pointer c_notify_fun = @cfunction( function notify_fun(idx) global thread_notifiers notify(thread_notifiers[idx]) return end, Cvoid, (Cint,)) # cconvert, root and unsafe_convert arguments roots = Any[] args_size = isempty(argtypes) ? 0 : sum(Core.sizeof, argtypes) args_arr = Vector{UInt8}(undef, args_size) ptr = pointer(args_arr) for (T, x) in zip(argtypes, argvals) isbitstype(T) || throw(ArgumentError("threadcall requires isbits argument types")) y = cconvert(T, x) push!(roots, y) unsafe_store!(convert(Ptr{T}, ptr), unsafe_convert(T, y)::T) ptr += Core.sizeof(T) end # create return buffer ret_arr = Vector{UInt8}(undef, Core.sizeof(rettype)) # wait for a worker thread to be available acquire(threadcall_restrictor) idx = findfirst(isequal(nothing), thread_notifiers)::Int thread_notifiers[idx] = Base.Condition() GC.@preserve args_arr ret_arr roots begin # queue up the work to be done ccall(:jl_queue_work, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Cvoid}, Cint), fun_ptr, cfptr, args_arr, ret_arr, c_notify_fun, idx) # wait for a result & return it wait(thread_notifiers[idx]) thread_notifiers[idx] = nothing release(threadcall_restrictor) r = unsafe_load(convert(Ptr{rettype}, pointer(ret_arr))) end return r end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/uuid.jlฎ # This file is a part of Julia. License is MIT: https://julialang.org/license """ Represents a Universally Unique Identifier (UUID). Can be built from one `UInt128` (all byte values), two `UInt64`, or four `UInt32`. Conversion from a string will check the UUID validity. """ struct UUID value::UInt128 end UUID(u::UUID) = u UUID(u::NTuple{2, UInt64}) = UUID((UInt128(u[1]) << 64) | UInt128(u[2])) UUID(u::NTuple{4, UInt32}) = UUID((UInt128(u[1]) << 96) | (UInt128(u[2]) << 64) | (UInt128(u[3]) << 32) | UInt128(u[4])) function convert(::Type{NTuple{2, UInt64}}, uuid::UUID) bytes = uuid.value hi = UInt64((bytes >> 64) & 0xffffffffffffffff) lo = UInt64(bytes & 0xffffffffffffffff) return (hi, lo) end function convert(::Type{NTuple{4, UInt32}}, uuid::UUID) bytes = uuid.value hh = UInt32((bytes >> 96) & 0xffffffff) hl = UInt32((bytes >> 64) & 0xffffffff) lh = UInt32((bytes >> 32) & 0xffffffff) ll = UInt32(bytes & 0xffffffff) return (hh, hl, lh, ll) end UInt128(u::UUID) = u.value let uuid_hash_seed = UInt === UInt64 ? 0xd06fa04f86f11b53 : 0x96a1f36d Base.hash(uuid::UUID, h::UInt) = hash(uuid_hash_seed, hash(convert(NTuple{2, UInt64}, uuid), h)) end let @inline function uuid_kernel(s, i, u) _c = UInt32(@inbounds codeunit(s, i)) d = __convert_digit(_c, UInt32(16)) d >= 16 && return nothing u <<= 4 return u | d end function Base.tryparse(::Type{UUID}, s::AbstractString) u = UInt128(0) ncodeunits(s) != 36 && return nothing for i in 1:8 u = uuid_kernel(s, i, u) u === nothing && return nothing end @inbounds codeunit(s, 9) == UInt8('-') || return nothing for i in 10:13 u = uuid_kernel(s, i, u) u === nothing && return nothing end @inbounds codeunit(s, 14) == UInt8('-') || return nothing for i in 15:18 u = uuid_kernel(s, i, u) u === nothing && return nothing end @inbounds codeunit(s, 19) == UInt8('-') || return nothing for i in 20:23 u = uuid_kernel(s, i, u) u === nothing && return nothing end @inbounds codeunit(s, 24) == UInt8('-') || return nothing for i in 25:36 u = uuid_kernel(s, i, u) u === nothing && return nothing end return Base.UUID(u) end end let @noinline throw_malformed_uuid(s) = throw(ArgumentError("Malformed UUID string: $(repr(s))")) function Base.parse(::Type{UUID}, s::AbstractString) uuid = tryparse(UUID, s) return uuid === nothing ? throw_malformed_uuid(s) : uuid end end UUID(s::AbstractString) = parse(UUID, s) let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] global string function string(u::UUID) u = u.value a = Base.StringVector(36) for i in groupings @inbounds a[i] = hex_chars[1 + u & 0xf] u >>= 4 end @inbounds a[24] = a[19] = a[14] = a[9] = '-' return String(a) end end print(io::IO, u::UUID) = print(io, string(u)) show(io::IO, u::UUID) = print(io, "UUID(\"", u, "\")") isless(a::UUID, b::UUID) = isless(a.value, b.value) # give UUID scalar behavior in broadcasting Base.broadcastable(x::UUID) = Ref(x) L/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/pkgid.jl้# This file is a part of Julia. License is MIT: https://julialang.org/license struct PkgId uuid::Union{UUID,Nothing} name::String PkgId(u::UUID, name::AbstractString) = new(UInt128(u) == 0 ? nothing : u, name) PkgId(::Nothing, name::AbstractString) = new(nothing, name) end PkgId(name::AbstractString) = PkgId(nothing, name) function PkgId(m::Module, name::String = String(nameof(moduleroot(m)))) uuid = UUID(ccall(:jl_module_uuid, NTuple{2, UInt64}, (Any,), m)) UInt128(uuid) == 0 ? PkgId(name) : PkgId(uuid, name) end ==(a::PkgId, b::PkgId) = a.uuid == b.uuid && a.name == b.name function hash(pkg::PkgId, h::UInt) h += 0xc9f248583a0ca36c % UInt h = hash(pkg.uuid, h) h = hash(pkg.name, h) return h end show(io::IO, pkg::PkgId) = print(io, pkg.name, " [", pkg.uuid === nothing ? "top-level" : pkg.uuid, "]") function binpack(pkg::PkgId) io = IOBuffer() write(io, UInt8(0)) uuid = pkg.uuid write(io, uuid === nothing ? UInt128(0) : UInt128(uuid)) write(io, pkg.name) return String(take!(io)) end function binunpack(s::String) io = IOBuffer(s) @assert read(io, UInt8) === 0x00 uuid = read(io, UInt128) name = read(io, String) return PkgId(UUID(uuid), name) end R/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/toml_parser.jl•# This file is a part of Julia. License is MIT: https://julialang.org/license module TOML using Base: IdSet # In case we do not have the Dates stdlib available # we parse DateTime into these internal structs, # note that these do not do any argument checking struct Date year::Int month::Int day::Int end struct Time hour::Int minute::Int second::Int ms::Int end struct DateTime date::Date time::Time end DateTime(y, m, d, h, mi, s, ms) = DateTime(Date(y,m,d), Time(h, mi, s, ms)) const EOF_CHAR = typemax(Char) const TOMLDict = Dict{String, Any} ########## # Parser # ########## mutable struct Parser str::String # 1 character look ahead current_char::Char pos::Int # prevpos equals the startbyte of the look ahead character # prevpos-1 is therefore the end byte of the character we last ate prevpos::Int # File info column::Int line::Int # The function `take_substring` takes the substring from `marker` up # to `prevpos-1`. marker::Int # The current table that `key = value` entries are inserted into active_table::TOMLDict # As we parse dotted keys we store each part of the key in this cache # A future improvement would be to also store the spans of the keys # so that in error messages we could also show the previous key # definition in case of duplicated keys dotted_keys::Vector{String} # Strings in TOML can have line continuations ('\' as the last character # on a line. We store the byte ranges for each of these "chunks" in here chunks::Vector{UnitRange{Int}} # We need to keep track of those tables / arrays that are defined # inline since we are not allowed to add keys to those inline_tables::IdSet{TOMLDict} static_arrays::IdSet{Any} # [a.b.c.d] doesn't "define" the table [a] # so keys can later be added to [a], therefore # we need to keep track of what tables are # actually defined defined_tables::IdSet{TOMLDict} # The table we will finally return to the user root::TOMLDict # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} # Get's populated with the Dates stdlib if it exists Dates::Union{Module, Nothing} end const DATES_PKGID = Base.PkgId(Base.UUID("ade2ca70-3891-5945-98fb-dc099432e06a"), "Dates") function Parser(str::String; filepath=nothing) root = TOMLDict() l = Parser( str, # str EOF_CHAR, # current_char firstindex(str), # pos 0, # prevpos 0, # column 1, # line 0, # marker root, # active_table String[], # dotted_keys UnitRange{Int}[], # chunks IdSet{TOMLDict}(), # inline_tables IdSet{Any}(), # static_arrays IdSet{TOMLDict}(), # defined_tables root, filepath, isdefined(Base, :maybe_root_module) ? Base.maybe_root_module(DATES_PKGID) : nothing, ) startup(l) return l end function startup(l::Parser) # Populate our one character look-ahead c = eat_char(l) # Skip BOM if c === '\ufeff' l.column -= 1 eat_char(l) end end Parser() = Parser("") Parser(io::IO) = Parser(read(io, String)) function reinit!(p::Parser, str::String; filepath::Union{Nothing, String}=nothing) p.str = str p.current_char = EOF_CHAR p.pos = firstindex(str) p.prevpos = 0 p.column = 0 p.line = 1 p.marker = 0 p.root = TOMLDict() p.active_table = p.root empty!(p.dotted_keys) empty!(p.chunks) empty!(p.inline_tables) empty!(p.static_arrays) empty!(p.defined_tables) p.filepath = filepath startup(p) return p end ########## # Errors # ########## throw_internal_error(msg) = error("internal TOML parser error: $msg") # Many functions return a ParserError. We want this to bubble up # all the way and have this error be returned to the user # if the parse is called with `raise=false`. This macro # makes that easier @eval macro $(:var"try")(expr) return quote v = $(esc(expr)) v isa ParserError && return v v end end # TODO: Check all of these are used @enum ErrorType begin # Toplevel # ############ ErrRedefineTableArray ErrExpectedNewLineKeyValue ErrAddKeyToInlineTable ErrAddArrayToStaticArray ErrArrayTreatedAsDictionary ErrExpectedEndOfTable ErrExpectedEndArrayOfTable # Keys # ######## ErrExpectedEqualAfterKey # Check, are these the same? ErrDuplicatedKey ErrKeyAlreadyHasValue ErrInvalidBareKeyCharacter ErrEmptyBareKey # Values # ########## ErrUnexpectedEofExpectedValue ErrUnexpectedStartOfValue ErrGenericValueError # Arrays ErrExpectedCommaBetweenItemsArray # Inline tables ErrExpectedCommaBetweenItemsInlineTable ErrTrailingCommaInlineTable ErrInlineTableRedefine # Numbers ErrUnderscoreNotSurroundedByDigits ErrLeadingZeroNotAllowedInteger ErrOverflowError ErrLeadingDot ErrNoTrailingDigitAfterDot ErrTrailingUnderscoreNumber ErrSignInNonBase10Number # DateTime ErrParsingDateTime ErrOffsetDateNotSupported # Strings ErrNewLineInString ErrUnexpectedEndString ErrInvalidEscapeCharacter ErrInvalidUnicodeScalar end const err_message = Dict( ErrTrailingCommaInlineTable => "trailing comma not allowed in inline table", ErrExpectedCommaBetweenItemsArray => "expected comma between items in array", ErrExpectedCommaBetweenItemsInlineTable => "expected comma between items in inline table", ErrExpectedEndArrayOfTable => "expected array of table to end with ']]'", ErrInvalidBareKeyCharacter => "invalid bare key character", ErrRedefineTableArray => "tried to redefine an existing table as an array", ErrDuplicatedKey => "key already defined", ErrKeyAlreadyHasValue => "key already has a value", ErrEmptyBareKey => "bare key cannot be empty", ErrExpectedNewLineKeyValue => "expected newline after key value pair", ErrNewLineInString => "newline character in single quoted string", ErrUnexpectedEndString => "string literal ended unexpectedly", ErrExpectedEndOfTable => "expected end of table ']'", ErrAddKeyToInlineTable => "tried to add a new key to an inline table", ErrInlineTableRedefine => "inline table overwrote key from other table", ErrArrayTreatedAsDictionary => "tried to add a key to an array", ErrAddArrayToStaticArray => "tried to append to a statically defined array", ErrGenericValueError => "failed to parse value", ErrLeadingZeroNotAllowedInteger => "leading zero in integer not allowed", ErrUnderscoreNotSurroundedByDigits => "underscore is not surrounded by digits", ErrUnexpectedStartOfValue => "unexpected start of value", ErrOffsetDateNotSupported => "offset date-time is not supported", ErrParsingDateTime => "parsing date/time value failed", ErrTrailingUnderscoreNumber => "trailing underscore in number", ErrLeadingDot => "floats require a leading zero", ErrExpectedEqualAfterKey => "expected equal sign after key", ErrNoTrailingDigitAfterDot => "expected digit after dot", ErrOverflowError => "overflowed when parsing integer", ErrInvalidUnicodeScalar => "invalid unicode scalar", ErrInvalidEscapeCharacter => "invalid escape character", ErrUnexpectedEofExpectedValue => "unexpected end of file, expected a value", ErrSignInNonBase10Number => "number not in base 10 is not allowed to have a sign", ) for err in instances(ErrorType) @assert haskey(err_message, err) "$err does not have an error message" end mutable struct ParserError <: Exception type::ErrorType # Arbitrary data to store at the # call site to be used when formatting # the error data # These are filled in before returning from parse function str ::Union{String, Nothing} filepath ::Union{String, Nothing} line ::Union{Int, Nothing} column ::Union{Int, Nothing} pos ::Union{Int, Nothing} # position of parser when table ::Union{TOMLDict, Nothing} # result parsed until error end ParserError(type, data) = ParserError(type, data, nothing, nothing, nothing, nothing, nothing, nothing) ParserError(type) = ParserError(type, nothing) # Defining these below can be useful when debugging code that erroneously returns a # ParserError because you get a stacktrace to where the ParserError was created #ParserError(type) = error(type) #ParserError(type, data) = error(type,data) # Many functions return either a T or a ParserError const Err{T} = Union{T, ParserError} function format_error_message_for_err_type(error::ParserError) msg = err_message[error.type] if error.type == ErrInvalidBareKeyCharacter c_escaped = escape_string(string(error.data)::String) msg *= ": '$c_escaped'" end return msg end # This is used in error formatting, for example, # point_to_line("aa\nfoobar\n\bb", 4, 6) would return the strings: # str1 = "foobar" # str2 = "^^^" # used to show the interval where an error happened # Right now, it is only called with a == b function point_to_line(str::AbstractString, a::Int, b::Int, context) @assert b >= a a = thisind(str, a) b = thisind(str, b) pos = something(findprev('\n', str, prevind(str, a)), 0) + 1 io1 = IOContext(IOBuffer(), context) io2 = IOContext(IOBuffer(), context) while true if a <= pos <= b printstyled(io2, "^"; color=:light_green) else print(io2, " ") end it = iterate(str, pos) it === nothing && break c, pos = it c == '\n' && break print(io1, c) end return String(take!(io1.io)), String(take!(io2.io)) end function Base.showerror(io::IO, err::ParserError) printstyled(io, "TOML Parser error:\n"; color=Base.error_color()) f = something(err.filepath, "none") printstyled(io, f, ':', err.line, ':', err.column; bold=true) printstyled(io, " error: "; color=Base.error_color()) println(io, format_error_message_for_err_type(err)) # In this case we want the arrow to point one character pos = err.pos::Int err.type == ErrUnexpectedEofExpectedValue && (pos += 1) str1, err1 = point_to_line(err.str::String, pos, pos, io) @static if VERSION <= v"1.6.0-DEV.121" # See https://github.com/JuliaLang/julia/issues/36015 format_fixer = get(io, :color, false)::Bool == true ? "\e[0m" : "" println(io, "$format_fixer ", str1) print(io, "$format_fixer ", err1) else println(io, " ", str1) print(io, " ", err1) end end ################ # Parser utils # ################ @inline function next_char(l::Parser)::Char state = iterate(l.str, l.pos) l.prevpos = l.pos l.column += 1 state === nothing && return EOF_CHAR c, pos = state l.pos = pos if c == '\n' l.line += 1 l.column = 0 end return c end @inline function eat_char(l::Parser)::Char c = l.current_char l.current_char = next_char(l) return c end @inline peek(l::Parser) = l.current_char # Return true if the character was accepted. When a character # is accepted it get's eaten and we move to the next character @inline function accept(l::Parser, f::Union{Function, Char})::Bool c = peek(l) c == EOF_CHAR && return false ok = false if isa(f, Function) ok = f(c) elseif isa(f, Char) ok = c === f end ok && eat_char(l) return ok end # Return true if any character was accepted function accept_batch(l::Parser, f::F)::Bool where {F} ok = false while accept(l, f) ok = true end return ok end # Return true if `f` was accepted `n` times @inline function accept_n(l::Parser, n, f::F)::Bool where {F} for i in 1:n if !accept(l, f) return false end end return true end @inline iswhitespace(c::Char) = c == ' ' || c == '\t' @inline isnewline(c::Char) = c == '\n' || c == '\r' skip_ws(l::Parser) = accept_batch(l, iswhitespace) skip_ws_nl_no_comment(l::Parser)::Bool = accept_batch(l, x -> iswhitespace(x) || isnewline(x)) function skip_ws_nl(l::Parser)::Bool skipped = false while true skipped_ws = accept_batch(l, x -> iswhitespace(x) || isnewline(x)) skipped_comment = skip_comment(l) if !skipped_ws && !skipped_comment break end skipped = true end return skipped end # Returns true if a comment was skipped function skip_comment(l::Parser)::Bool found_comment = accept(l, '#') if found_comment accept_batch(l, !isnewline) end return found_comment end skip_ws_comment(l::Parser) = skip_ws(l) && skip_comment(l) @inline set_marker!(l::Parser) = l.marker = l.prevpos take_substring(l::Parser) = SubString(l.str, l.marker:(l.prevpos-1)) ############ # Toplevel # ############ # Driver, keeps parsing toplevel until we either get # a `ParserError` or eof. function parse(l::Parser)::TOMLDict v = tryparse(l) v isa ParserError && throw(v) return v end function tryparse(l::Parser)::Err{TOMLDict} while true skip_ws_nl(l) peek(l) == EOF_CHAR && break v = parse_toplevel(l) if v isa ParserError v.str = l.str v.pos = l.prevpos-1 v.table = l.root v.filepath = l.filepath v.line = l.line v.column = l.column-1 return v end end return l.root end # Top level can be either a table key, an array of table statement # or a key/value entry. function parse_toplevel(l::Parser)::Err{Nothing} if accept(l, '[') l.active_table = l.root @try parse_table(l) skip_ws_comment(l) if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == '#' || peek(l) == EOF_CHAR) eat_char(l) return ParserError(ErrExpectedNewLineKeyValue) end else @try parse_entry(l, l.active_table) skip_ws_comment(l) # SPEC: "There must be a newline (or EOF) after a key/value pair." if !(peek(l) == '\n' || peek(l) == '\r' || peek(l) == '#' || peek(l) == EOF_CHAR) c = eat_char(l) return ParserError(ErrExpectedNewLineKeyValue) end end end function recurse_dict!(l::Parser, d::Dict, dotted_keys::AbstractVector{String}, check=true)::Err{TOMLDict} for i in 1:length(dotted_keys) d = d::TOMLDict key = dotted_keys[i] d = get!(TOMLDict, d, key) if d isa Vector d = d[end] end check && @try check_allowed_add_key(l, d, i == length(dotted_keys)) end return d::TOMLDict end function check_allowed_add_key(l::Parser, d, check_defined=true)::Err{Nothing} if !(d isa Dict) return ParserError(ErrKeyAlreadyHasValue) elseif d isa Dict && d in l.inline_tables return ParserError(ErrAddKeyToInlineTable) elseif check_defined && d in l.defined_tables return ParserError(ErrDuplicatedKey) end return nothing end # Can only enter here from toplevel function parse_table(l) if accept(l, '[') return parse_array_table(l) end table_key = @try parse_key(l) skip_ws(l) if !accept(l, ']') return ParserError(ErrExpectedEndOfTable) end l.active_table = @try recurse_dict!(l, l.root, table_key) push!(l.defined_tables, l.active_table) return end function parse_array_table(l)::Union{Nothing, ParserError} table_key = @try parse_key(l) skip_ws(l) if !(accept(l, ']') && accept(l, ']')) return ParserError(ErrExpectedEndArrayOfTable) end d = @try recurse_dict!(l, l.root, @view(table_key[1:end-1]), false) k = table_key[end] old = get!(() -> [], d, k) if old isa Vector if old in l.static_arrays return ParserError(ErrAddArrayToStaticArray) end else return ParserError(ErrArrayTreatedAsDictionary) end d_new = TOMLDict() push!(old, d_new) push!(l.defined_tables, d_new) l.active_table = d_new return end function parse_entry(l::Parser, d)::Union{Nothing, ParserError} key = @try parse_key(l) skip_ws(l) if !accept(l, '=') return ParserError(ErrExpectedEqualAfterKey) end if length(key) > 1 d = @try recurse_dict!(l, d, @view(key[1:end-1])) end last_key_part = l.dotted_keys[end] v = get(d, last_key_part, nothing) if v !== nothing @try check_allowed_add_key(l, v) end skip_ws(l) value = @try parse_value(l) # Not allowed to overwrite a value with an inline dict if value isa Dict && haskey(d, last_key_part) return ParserError(ErrInlineTableRedefine) end # TODO: Performance, hashing `last_key_part` again here d[last_key_part] = value return end ######## # Keys # ######## # SPEC: "Bare keys may only contain ASCII letters, ASCII digits, underscores, # and dashes (A-Za-z0-9_-). # Note that bare keys are allowed to be composed of only ASCII digits, e.g. 1234, # but are always interpreted as strings." @inline isvalid_barekey_char(c::Char) = 'a' <= c <= 'z' || 'A' <= c <= 'Z' || isdigit(c) || c == '-' || c == '_' # Current key... function parse_key(l::Parser) empty!(l.dotted_keys) _parse_key(l) end # Recursively add dotted keys to `l.dotted_key` function _parse_key(l::Parser) skip_ws(l) # SPEC: "A bare key must be non-empty," if isempty(l.dotted_keys) && accept(l, '=') return ParserError(ErrEmptyBareKey) end keyval = if accept(l, '"') @try parse_string_start(l, false) elseif accept(l, '\'') @try parse_string_start(l, true) else set_marker!(l) if accept_batch(l, isvalid_barekey_char) if !(peek(l) == '.' || iswhitespace(peek(l)) || peek(l) == ']' || peek(l) == '=') c = eat_char(l) return ParserError(ErrInvalidBareKeyCharacter, c) end String(take_substring(l)) else c = eat_char(l) return ParserError(ErrInvalidBareKeyCharacter, c) end end new_key = keyval push!(l.dotted_keys, new_key) # SPEC: "Whitespace around dot-separated parts is ignored." skip_ws(l) if accept(l, '.') skip_ws(l) @try _parse_key(l) end return l.dotted_keys end ########## # Values # ########## function parse_value(l::Parser) val = if accept(l, '[') parse_array(l) elseif accept(l, '{') parse_inline_table(l) elseif accept(l, '"') parse_string_start(l, false) elseif accept(l, '\'') parse_string_start(l, true) elseif accept(l, 't') parse_bool(l, true) elseif accept(l, 'f') parse_bool(l, false) else parse_number_or_date_start(l) end if val === nothing return ParserError(ErrGenericValueError) end return val end ######### # Array # ######### function push!!(v::Vector, el) # Since these types are typically non-inferrable, they are a big invalidation risk, # and since it's used by the package-loading infrastructure the cost of invalidation # is high. Therefore, this is written to reduce the "exposed surface area": e.g., rather # than writing `T[el]` we write it as `push!(Vector{T}(undef, 1), el)` so that there # is no ambiguity about what types of objects will be created. T = eltype(v) t = typeof(el) if el isa T || t === T push!(v, el::T) return v elseif T === Union{} out = Vector{t}(undef, 1) out[1] = el return out else if T isa Union newT = Any else newT = Union{T, typeof(el)} end new = Array{newT}(undef, length(v)) copy!(new, v) return push!(new, el) end end function parse_array(l::Parser)::Err{Vector} skip_ws_nl(l) array = Vector{Union{}}() empty_array = accept(l, ']') while !empty_array v = @try parse_value(l) # TODO: Worth to function barrier this? array = push!!(array, v) # There can be an arbitrary number of newlines and comments before a value and before the closing bracket. skip_ws_nl(l) comma = accept(l, ',') skip_ws_nl(l) accept(l, ']') && break if !comma return ParserError(ErrExpectedCommaBetweenItemsArray) end end push!(l.static_arrays, array) return array end ################ # Inline table # ################ function parse_inline_table(l::Parser)::Err{TOMLDict} dict = TOMLDict() push!(l.inline_tables, dict) skip_ws(l) accept(l, '}') && return dict while true @try parse_entry(l, dict) # SPEC: No newlines are allowed between the curly braces unless they are valid within a value. skip_ws(l) accept(l, '}') && return dict if accept(l, ',') skip_ws(l) if accept(l, '}') return ParserError(ErrTrailingCommaInlineTable) end else return ParserError(ErrExpectedCommaBetweenItemsInlineTable) end end end ########### # Numbers # ########### parse_inf(l::Parser, sgn::Int) = accept(l, 'n') && accept(l, 'f') ? sgn * Inf : nothing parse_nan(l::Parser) = accept(l, 'a') && accept(l, 'n') ? NaN : nothing function parse_bool(l::Parser, v::Bool)::Union{Bool, Nothing} # Have eaten a 't' if `v` is true, otherwise have eaten a `f`. v ? (accept(l, 'r') && accept(l, 'u') && accept(l, 'e') && return true) : (accept(l, 'a') && accept(l, 'l') && accept(l, 's') && accept(l, 'e') && return false) return nothing end isvalid_hex(c::Char) = isdigit(c) || ('a' <= c <= 'f') || ('A' <= c <= 'F') isvalid_oct(c::Char) = '0' <= c <= '7' isvalid_binary(c::Char) = '0' <= c <= '1' const ValidSigs = Union{typeof.([isvalid_hex, isvalid_oct, isvalid_binary, isdigit])...} # This function eats things accepted by `f` but also allows eating `_` in between # digits. Returns if it ate at lest one character and if it ate an underscore function accept_batch_underscore(l::Parser, f::ValidSigs, fail_if_underscore=true)::Err{Tuple{Bool, Bool}} contains_underscore = false at_least_one = false last_underscore = false while true c = peek(l) if c == '_' contains_underscore = true if fail_if_underscore return ParserError(ErrUnderscoreNotSurroundedByDigits) end eat_char(l) fail_if_underscore = true last_underscore = true else # SPEC: "Each underscore must be surrounded by at least one digit on each side." fail_if_underscore = false if f(c) at_least_one = true eat_char(l) else if last_underscore return ParserError(ErrTrailingUnderscoreNumber) end return at_least_one, contains_underscore end last_underscore = false end end end function parse_number_or_date_start(l::Parser) integer = true read_dot = false set_marker!(l) sgn = 1 parsed_sign = false if accept(l, '+') parsed_sign = true elseif accept(l, '-') parsed_sign = true sgn = -1 end if accept(l, 'i') return parse_inf(l, sgn) elseif accept(l, 'n') return parse_nan(l) end if accept(l, '.') return ParserError(ErrLeadingDot) end # Zero is allowed to follow by a end value char, a base x, o, b or a dot readed_zero = false if accept(l, '0') readed_zero = true # Intentional bad grammar to remove the ambiguity in "read"... if ok_end_value(peek(l)) return Int64(0) elseif accept(l, 'x') parsed_sign && return ParserError(ErrSignInNonBase10Number) ate, contains_underscore = @try accept_batch_underscore(l, isvalid_hex) ate && return parse_hex(l, contains_underscore) elseif accept(l, 'o') parsed_sign && return ParserError(ErrSignInNonBase10Number) ate, contains_underscore = @try accept_batch_underscore(l, isvalid_oct) ate && return parse_oct(l, contains_underscore) elseif accept(l, 'b') parsed_sign && return ParserError(ErrSignInNonBase10Number) ate, contains_underscore = @try accept_batch_underscore(l, isvalid_binary) ate && return parse_bin(l, contains_underscore) elseif accept(l, isdigit) return parse_local_time(l) end end read_underscore = false read_digit = accept(l, isdigit) if !readed_zero && !read_digit if peek(l) == EOF_CHAR return ParserError(ErrUnexpectedEofExpectedValue) else return ParserError(ErrUnexpectedStartOfValue) end end ate, contains_underscore = @try accept_batch_underscore(l, isdigit, readed_zero) read_underscore |= contains_underscore if (read_digit || ate) && ok_end_value(peek(l)) return parse_int(l, contains_underscore) end # Done with integers here if !read_underscore # No underscores in date / times if peek(l) == '-' return parse_datetime(l) elseif peek(l) == ':' return parse_local_time(l) end end # Done with datetime / localtime here # can optionally read a . + digits and then exponent ate_dot = accept(l, '.') ate, contains_underscore = @try accept_batch_underscore(l, isdigit, true) if ate_dot && !ate return ParserError(ErrNoTrailingDigitAfterDot) end read_underscore |= contains_underscore if accept(l, x -> x == 'e' || x == 'E') accept(l, x-> x == '+' || x == '-') # SPEC: (which follows the same rules as decimal integer values but may include leading zeros) read_digit = accept_batch(l, isdigit) ate, read_underscore = @try accept_batch_underscore(l, isdigit, !read_digit) contains_underscore |= read_underscore end if !ok_end_value(peek(l)) eat_char(l) return ParserError(ErrGenericValueError) end return parse_float(l, read_underscore) end function take_string_or_substring(l, contains_underscore)::SubString subs = take_substring(l) # Need to pass a AbstractString to `parse` so materialize it in case it # contains underscore. return contains_underscore ? SubString(filter(!=('_'), subs)) : subs end function parse_float(l::Parser, contains_underscore)::Err{Float64} s = take_string_or_substring(l, contains_underscore) v = Base.tryparse(Float64, s) v === nothing && return(ParserError(ErrGenericValueError)) return v end for (name, T1, T2, n1, n2) in (("int", Int64, Int128, 17, 33), ("hex", UInt64, UInt128, 18, 34), ("oct", UInt64, UInt128, 24, 45), ("bin", UInt64, UInt128, 66, 130), ) @eval function $(Symbol("parse_", name))(l::Parser, contains_underscore, base=nothing)::Err{Union{$(T1), $(T2), BigInt}} s = take_string_or_substring(l, contains_underscore) len = length(s) v = try if len โ‰ค $(n1) Base.parse($(T1), s; base) elseif $(n1) < len โ‰ค $(n2) Base.parse($(T2), s; base) else Base.parse(BigInt, s; base) end catch e e isa Base.OverflowError && return(ParserError(ErrOverflowError)) error("internal parser error: did not correctly discredit $(repr(s)) as an int") end return v end end ########################## # Date / Time / DateTime # ########################## ok_end_value(c::Char) = iswhitespace(c) || c == '#' || c == EOF_CHAR || c == ']' || c == '}' || c == ',' || c == '\n' || c == '\r' #= # https://tools.ietf.org/html/rfc3339 # Internet Protocols MUST generate four digit years in dates. date-fullyear = 4DIGIT date-month = 2DIGIT ; 01-12 date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on ; month/year time-hour = 2DIGIT ; 00-23 time-minute = 2DIGIT ; 00-59 time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second ; rules time-secfrac = "." 1*DIGIT time-numoffset = ("+" / "-") time-hour ":" time-minute time-offset = "Z" / time-numoffset partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] full-date = date-fullyear "-" date-month "-" date-mday full-time = partial-time time-offset date-time = full-date "T" full-time =# accept_two(l, f::F) where {F} = accept_n(l, 2, f) || return(ParserError(ErrParsingDateTime)) function parse_datetime(l) # Year has already been eaten when we reach here year = @try parse_int(l, false) year in 0:9999 || return ParserError(ErrParsingDateTime) # Month accept(l, '-') || return ParserError(ErrParsingDateTime) set_marker!(l) @try accept_two(l, isdigit) month = @try parse_int(l, false) month in 1:12 || return ParserError(ErrParsingDateTime) accept(l, '-') || return ParserError(ErrParsingDateTime) # Day set_marker!(l) @try accept_two(l, isdigit) day = @try parse_int(l, false) # Verify the real range in the constructor below day in 1:31 || return ParserError(ErrParsingDateTime) # We might have a local date now read_space = false if ok_end_value(peek(l)) if (read_space = accept(l, ' ')) if !isdigit(peek(l)) return try_return_date(l, year, month, day) end else return try_return_date(l, year, month, day) end end if !read_space accept(l, 'T') || accept(l, 't') || return ParserError(ErrParsingDateTime) end h, m, s, ms = @try _parse_local_time(l) # Julia doesn't support offset times if !accept(l, 'Z') if accept(l, '+') || accept(l, '-') return ParserError(ErrOffsetDateNotSupported) end end if !ok_end_value(peek(l)) return ParserError(ErrParsingDateTime) end # The DateTime parser verifies things like leap year for us return try_return_datetime(l, year, month, day, h, m, s, ms) end function try_return_datetime(p, year, month, day, h, m, s, ms) Dates = p.Dates if Dates !== nothing try return Dates.DateTime(year, month, day, h, m, s, ms) catch return ParserError(ErrParsingDateTime) end else return DateTime(year, month, day, h, m, s, ms) end end function try_return_date(p, year, month, day) Dates = p.Dates if Dates !== nothing try return Dates.Date(year, month, day) catch return ParserError(ErrParsingDateTime) end else return Date(year, month, day) end end function parse_local_time(l::Parser) h = @try parse_int(l, false) h in 0:23 || return ParserError(ErrParsingDateTime) _, m, s, ms = @try _parse_local_time(l, true) # TODO: Could potentially parse greater accuracy for the # fractional seconds here. return try_return_time(l, h, m, s, ms) end function try_return_time(p, h, m, s, ms) Dates = p.Dates if Dates !== nothing try return Dates.Time(h, m, s, ms) catch return ParserError(ErrParsingDateTime) end else return Time(h, m, s, ms) end end function _parse_local_time(l::Parser, skip_hour=false)::Err{NTuple{4, Int64}} # Hour has potentially been already parsed in # `parse_number_or_date_start` already if skip_hour hour = Int64(0) else set_marker!(l) @try accept_two(l, isdigit) hour = parse_int(l, false) hour in 0:23 || return ParserError(ErrParsingDateTime) end accept(l, ':') || return ParserError(ErrParsingDateTime) # minute set_marker!(l) @try accept_two(l, isdigit) minute = parse_int(l, false) minute in 0:59 || return ParserError(ErrParsingDateTime) accept(l, ':') || return ParserError(ErrParsingDateTime) # second set_marker!(l) @try accept_two(l, isdigit) second = parse_int(l, false) second in 0:59 || return ParserError(ErrParsingDateTime) # optional fractional second fractional_second = Int64(0) if accept(l, '.') set_marker!(l) found_fractional_digit = false for i in 1:3 found_fractional_digit |= accept(l, isdigit) end if !found_fractional_digit return ParserError(ErrParsingDateTime) end # DateTime in base only manages 3 significant digits in fractional # second fractional_second = parse_int(l, false) # Truncate off the rest eventual digits accept_batch(l, isdigit) end return hour, minute, second, fractional_second end ########## # String # ########## function parse_string_start(l::Parser, quoted::Bool)::Err{String} # Have eaten a `'` if `quoted` is true, otherwise have eaten a `"` multiline = false c = quoted ? '\'' : '"' if accept(l, c) # Eat second quote if !accept(l, c) return "" end accept(l, '\r') # Eat third quote accept(l, '\n') # Eat third quote multiline = true end return parse_string_continue(l, multiline, quoted) end @inline stop_candidates_multiline(x) = x != '"' && x != '\\' @inline stop_candidates_singleline(x) = x != '"' && x != '\\' && x != '\n' @inline stop_candidates_multiline_quoted(x) = x != '\'' && x != '\\' @inline stop_candidates_singleline_quoted(x) = x != '\'' && x != '\\' && x != '\n' function parse_string_continue(l::Parser, multiline::Bool, quoted::Bool)::Err{String} start_chunk = l.prevpos q = quoted ? '\'' : '"' contains_backslash = false offset = multiline ? 3 : 1 while true if peek(l) == EOF_CHAR return ParserError(ErrUnexpectedEndString) end if quoted accept_batch(l, multiline ? stop_candidates_multiline_quoted : stop_candidates_singleline_quoted) else accept_batch(l, multiline ? stop_candidates_multiline : stop_candidates_singleline) end if !multiline && peek(l) == '\n' return ParserError(ErrNewLineInString) end next_slash = peek(l) == '\\' if !next_slash # TODO: Doesn't handle values with e.g. format `""""str""""` if accept(l, q) && (!multiline || (accept(l, q) && accept(l, q))) push!(l.chunks, start_chunk:(l.prevpos-offset-1)) return take_chunks(l, contains_backslash) end end c = eat_char(l) # eat the character we stopped at next_slash = c == '\\' if next_slash && !quoted if peek(l) == '\n' || peek(l) == '\r' push!(l.chunks, start_chunk:(l.prevpos-1-1)) # -1 due to eating the slash skip_ws_nl_no_comment(l) start_chunk = l.prevpos else c = eat_char(l) # eat the escaped character if c == 'u' || c == 'U' n = c == 'u' ? 4 : 6 set_marker!(l) if !accept_n(l, n, isvalid_hex) return ParserError(ErrInvalidUnicodeScalar) end codepoint = parse_int(l, false, 16)::Int64 #= Unicode Scalar Value --------------------- Any Unicode code point except high-surrogate and low-surrogate code points. In other words, the ranges of integers 0 to D7FF16 and E00016 to 10FFFF16 inclusive. =# if !(codepoint <= 0xD7FF || 0xE000 <= codepoint <= 0x10FFFF) return ParserError(ErrInvalidUnicodeScalar) end elseif c != 'b' && c != 't' && c != 'n' && c != 'f' && c != 'r' && c != '"' && c!= '\\' return ParserError(ErrInvalidEscapeCharacter) end contains_backslash = true end end end end function take_chunks(l::Parser, unescape::Bool)::String nbytes = sum(length, l.chunks; init=0) str = Base._string_n(nbytes) offset = 1 for chunk in l.chunks # The SubString constructor takes as an index the first byte of the # last character but we have the last byte. n = length(chunk) GC.@preserve str begin unsafe_copyto!(pointer(str, offset), pointer(l.str, first(chunk)), n) end offset += n end empty!(l.chunks) return unescape ? unescape_string(str) : str end end N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/linking.jl=# This file is a part of Julia. License is MIT: https://julialang.org/license module Linking import Base.Libc: Libdl # inlined LLD_jll # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") const PATH_list = String[] const LIBPATH_list = String[] const lld_path = Ref{String}() const lld_exe = Sys.iswindows() ? "lld.exe" : "lld" const dsymutil_path = Ref{String}() const dsymutil_exe = Sys.iswindows() ? "dsymutil.exe" : "dsymutil" if Sys.iswindows() const LIBPATH_env = "PATH" const LIBPATH_default = "" const pathsep = ';' elseif Sys.isapple() const LIBPATH_env = "DYLD_FALLBACK_LIBRARY_PATH" const LIBPATH_default = "~/lib:/usr/local/lib:/lib:/usr/lib" const pathsep = ':' else const LIBPATH_env = "LD_LIBRARY_PATH" const LIBPATH_default = "" const pathsep = ':' end function adjust_ENV!(env::Dict, PATH::String, LIBPATH::String, adjust_PATH::Bool, adjust_LIBPATH::Bool) if adjust_LIBPATH LIBPATH_base = get(env, LIBPATH_env, expanduser(LIBPATH_default)) if !isempty(LIBPATH_base) env[LIBPATH_env] = string(LIBPATH, pathsep, LIBPATH_base) else env[LIBPATH_env] = LIBPATH end end if adjust_PATH && (LIBPATH_env != "PATH" || !adjust_LIBPATH) if !isempty(get(env, "PATH", "")) env["PATH"] = string(PATH, pathsep, env["PATH"]) else env["PATH"] = PATH end end return env end function __init_lld_path() # Prefer our own bundled lld, but if we don't have one, pick it up off of the PATH # If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `private_libexecdir` for bundled_lld_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, lld_exe), joinpath(Sys.BINDIR, "..", "tools", lld_exe), joinpath(Sys.BINDIR, lld_exe)) if isfile(bundled_lld_path) lld_path[] = abspath(bundled_lld_path) return end end lld_path[] = something(Sys.which(lld_exe), lld_exe) return end function __init_dsymutil_path() #Same as with lld but for dsymutil for bundled_dsymutil_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, dsymutil_exe), joinpath(Sys.BINDIR, "..", "tools", dsymutil_exe), joinpath(Sys.BINDIR, dsymutil_exe)) if isfile(bundled_dsymutil_path) dsymutil_path[] = abspath(bundled_dsymutil_path) return end end dsymutil_path[] = something(Sys.which(dsymutil_exe), dsymutil_exe) return end const VERBOSE = Ref{Bool}(false) function __init__() VERBOSE[] = Base.get_bool_env("JULIA_VERBOSE_LINKING", false) __init_lld_path() __init_dsymutil_path() PATH[] = dirname(lld_path[]) if Sys.iswindows() # On windows, the dynamic libraries (.dll) are in Sys.BINDIR ("usr\\bin") append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), Sys.BINDIR]) else append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), abspath(Sys.BINDIR, Base.LIBDIR)]) end LIBPATH[] = join(LIBPATH_list, pathsep) return end function lld(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH) return Cmd(Cmd([lld_path[]]); env) end function dsymutil(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH) return Cmd(Cmd([dsymutil_path[]]); env) end function ld() default_args = `` @static if Sys.iswindows() # LLD supports mingw style linking flavor = "gnu" m = Sys.ARCH == :x86_64 ? "i386pep" : "i386pe" default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition` elseif Sys.isapple() flavor = "darwin" arch = Sys.ARCH == :aarch64 ? :arm64 : Sys.ARCH default_args = `-arch $arch -undefined dynamic_lookup -platform_version macos $(Base.MACOS_PRODUCT_VERSION) $(Base.MACOS_PLATFORM_VERSION)` else flavor = "gnu" end `$(lld()) -flavor $flavor $default_args` end const WHOLE_ARCHIVE = if Sys.isapple() "-all_load" else "--whole-archive" end const NO_WHOLE_ARCHIVE = if Sys.isapple() "" else "--no-whole-archive" end const SHARED = if Sys.isapple() "-dylib" else "-shared" end is_debug() = ccall(:jl_is_debugbuild, Cint, ()) == 1 libdir() = abspath(Sys.BINDIR, Base.LIBDIR) private_libdir() = abspath(Sys.BINDIR, Base.PRIVATE_LIBDIR) if Sys.iswindows() shlibdir() = Sys.BINDIR else shlibdir() = libdir() end function link_image_cmd(path, out) PRIVATE_LIBDIR = "-L$(private_libdir())" SHLIBDIR = "-L$(shlibdir())" LIBS = is_debug() ? ("-ljulia-debug", "-ljulia-internal-debug") : ("-ljulia", "-ljulia-internal") @static if Sys.iswindows() LIBS = (LIBS..., "-lopenlibm", "-lssp", "-lgcc_s", "-lgcc", "-lmsvcrt") end V = VERBOSE[] ? "--verbose" : "" `$(ld()) $V $SHARED -o $out $WHOLE_ARCHIVE $path $NO_WHOLE_ARCHIVE $PRIVATE_LIBDIR $SHLIBDIR $LIBS` end function link_image(path, out, internal_stderr::IO=stderr, internal_stdout::IO=stdout) run(link_image_cmd(path, out), Base.DevNull(), internal_stderr, internal_stdout) end end # module Linking N/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/loading.jlศ์# This file is a part of Julia. License is MIT: https://julialang.org/license # Base.require is the implementation for the `import` statement const require_lock = ReentrantLock() # Cross-platform case-sensitive path canonicalization if Sys.isunix() && !Sys.isapple() # assume case-sensitive filesystems, don't have to do anything isfile_casesensitive(path) = isaccessiblefile(path) elseif Sys.iswindows() # GetLongPathName Win32 function returns the case-preserved filename on NTFS. function isfile_casesensitive(path) isaccessiblefile(path) || return false # Fail fast basename(Filesystem.longpath(path)) == basename(path) end elseif Sys.isapple() # HFS+ filesystem is case-preserving. The getattrlist API returns # a case-preserved filename. In the rare event that HFS+ is operating # in case-sensitive mode, this will still work but will be redundant. # Constants from const ATRATTR_BIT_MAP_COUNT = 5 const ATTR_CMN_NAME = 1 const BITMAPCOUNT = 1 const COMMONATTR = 5 const FSOPT_NOFOLLOW = 1 # Don't follow symbolic links const attr_list = zeros(UInt8, 24) attr_list[BITMAPCOUNT] = ATRATTR_BIT_MAP_COUNT attr_list[COMMONATTR] = ATTR_CMN_NAME # This essentially corresponds to the following C code: # attrlist attr_list; # memset(&attr_list, 0, sizeof(attr_list)); # attr_list.bitmapcount = ATTR_BIT_MAP_COUNT; # attr_list.commonattr = ATTR_CMN_NAME; # struct Buffer { # u_int32_t total_length; # u_int32_t filename_offset; # u_int32_t filename_length; # char filename[max_filename_length]; # }; # Buffer buf; # getattrpath(path, &attr_list, &buf, sizeof(buf), FSOPT_NOFOLLOW); function isfile_casesensitive(path) isaccessiblefile(path) || return false path_basename = String(basename(path)) local casepreserved_basename header_size = 12 buf = Vector{UInt8}(undef, length(path_basename) + header_size + 1) while true ret = ccall(:getattrlist, Cint, (Cstring, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Culong), path, attr_list, buf, sizeof(buf), FSOPT_NOFOLLOW) systemerror(:getattrlist, ret โ‰  0) filename_length = GC.@preserve buf unsafe_load( convert(Ptr{UInt32}, pointer(buf) + 8)) if (filename_length + header_size) > length(buf) resize!(buf, filename_length + header_size) continue end casepreserved_basename = view(buf, (header_size+1):(header_size+filename_length-1)) break end # Hack to compensate for inability to create a string from a subarray with no allocations. codeunits(path_basename) == casepreserved_basename && return true # If there is no match, it's possible that the file does exist but HFS+ # performed unicode normalization. See https://developer.apple.com/library/mac/qa/qa1235/_index.html. isascii(path_basename) && return false codeunits(Unicode.normalize(path_basename, :NFD)) == casepreserved_basename end else # Generic fallback that performs a slow directory listing. function isfile_casesensitive(path) isaccessiblefile(path) || return false dir, filename = splitdir(path) any(readdir(dir) .== filename) end end # Check if the file is accessible. If stat fails return `false` function isaccessibledir(dir) return try isdir(dir) catch err err isa IOError || rethrow() false end end function isaccessiblefile(file) return try isfile(file) catch err err isa IOError || rethrow() false end end function isaccessiblepath(path) return try ispath(path) catch err err isa IOError || rethrow() false end end ## SHA1 ## struct SHA1 bytes::NTuple{20, UInt8} end function SHA1(bytes::Vector{UInt8}) length(bytes) == 20 || throw(ArgumentError("wrong number of bytes for SHA1 hash: $(length(bytes))")) return SHA1(ntuple(i->bytes[i], Val(20))) end SHA1(s::AbstractString) = SHA1(hex2bytes(s)) parse(::Type{SHA1}, s::AbstractString) = SHA1(s) function tryparse(::Type{SHA1}, s::AbstractString) try return parse(SHA1, s) catch e if isa(e, ArgumentError) return nothing end rethrow(e) end end string(hash::SHA1) = bytes2hex(hash.bytes) print(io::IO, hash::SHA1) = bytes2hex(io, hash.bytes) show(io::IO, hash::SHA1) = print(io, "SHA1(\"", hash, "\")") isless(a::SHA1, b::SHA1) = isless(a.bytes, b.bytes) hash(a::SHA1, h::UInt) = hash((SHA1, a.bytes), h) ==(a::SHA1, b::SHA1) = a.bytes == b.bytes # fake uuid5 function (for self-assigned UUIDs) # TODO: delete and use real uuid5 once it's in stdlib function uuid5(namespace::UUID, key::String) u::UInt128 = 0 h = hash(namespace) for _ = 1:sizeof(u)รทsizeof(h) u <<= sizeof(h) << 3 u |= (h = hash(key, h)) end u &= 0xffffffffffff0fff3fffffffffffffff u |= 0x00000000000050008000000000000000 return UUID(u) end const ns_dummy_uuid = UUID("fe0723d6-3a44-4c41-8065-ee0f42c8ceab") function dummy_uuid(project_file::String) @lock require_lock begin cache = LOADING_CACHE[] if cache !== nothing uuid = get(cache.dummy_uuid, project_file, nothing) uuid === nothing || return uuid end project_path = try realpath(project_file) catch ex ex isa IOError || rethrow() project_file end uuid = uuid5(ns_dummy_uuid, project_path) if cache !== nothing cache.dummy_uuid[project_file] = uuid end return uuid end end ## package path slugs: turning UUID + SHA1 into a pair of 4-byte "slugs" ## const slug_chars = String(['A':'Z'; 'a':'z'; '0':'9']) function slug(x::UInt32, p::Int) y::UInt32 = x sprint(sizehint=p) do io n = length(slug_chars) for i = 1:p y, d = divrem(y, n) write(io, slug_chars[1+d]) end end end function package_slug(uuid::UUID, p::Int=5) crc = _crc32c(uuid) return slug(crc, p) end function version_slug(uuid::UUID, sha1::SHA1, p::Int=5) crc = _crc32c(uuid) crc = _crc32c(sha1.bytes, crc) return slug(crc, p) end mutable struct CachedTOMLDict path::String inode::UInt64 mtime::Float64 size::Int64 hash::UInt32 d::Dict{String, Any} end function CachedTOMLDict(p::TOML.Parser, path::String) s = stat(path) content = read(path) crc32 = _crc32c(content) TOML.reinit!(p, String(content); filepath=path) d = TOML.parse(p) return CachedTOMLDict( path, s.inode, s.mtime, s.size, crc32, d, ) end function get_updated_dict(p::TOML.Parser, f::CachedTOMLDict) s = stat(f.path) # note, this might miss very rapid in-place updates, such that mtime is # identical but that is solvable by not doing in-place updates, and not # rapidly changing these files if s.inode != f.inode || s.mtime != f.mtime || f.size != s.size content = read(f.path) new_hash = _crc32c(content) if new_hash != f.hash f.inode = s.inode f.mtime = s.mtime f.size = s.size f.hash = new_hash TOML.reinit!(p, String(content); filepath=f.path) return f.d = TOML.parse(p) end end return f.d end struct LoadingCache load_path::Vector{String} dummy_uuid::Dict{String, UUID} env_project_file::Dict{String, Union{Bool, String}} project_file_manifest_path::Dict{String, Union{Nothing, String}} require_parsed::Set{String} identified_where::Dict{Tuple{PkgId, String}, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}} identified::Dict{String, Union{Nothing, Tuple{PkgId, Union{Nothing, String}}}} located::Dict{Tuple{PkgId, Union{String, Nothing}}, Union{Tuple{Union{String, Nothing}, Union{String, Nothing}}, Nothing}} end const LOADING_CACHE = Ref{Union{LoadingCache, Nothing}}(nothing) LoadingCache() = LoadingCache(load_path(), Dict(), Dict(), Dict(), Set(), Dict(), Dict(), Dict()) struct TOMLCache p::TOML.Parser d::Dict{String, CachedTOMLDict} end const TOML_CACHE = TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}()) parsed_toml(project_file::AbstractString) = parsed_toml(project_file, TOML_CACHE, require_lock) function parsed_toml(project_file::AbstractString, toml_cache::TOMLCache, toml_lock::ReentrantLock) lock(toml_lock) do cache = LOADING_CACHE[] dd = if !haskey(toml_cache.d, project_file) d = CachedTOMLDict(toml_cache.p, project_file) toml_cache.d[project_file] = d d.d else d = toml_cache.d[project_file] # We are in a require call and have already parsed this TOML file # assume that it is unchanged to avoid hitting disk if cache !== nothing && project_file in cache.require_parsed d.d else get_updated_dict(toml_cache.p, d) end end if cache !== nothing push!(cache.require_parsed, project_file) end return dd end end ## package identification: determine unique identity of package to be loaded ## # Used by Pkg but not used in loading itself function find_package(arg) pkgenv = identify_package_env(arg) pkgenv === nothing && return nothing pkg, env = pkgenv return locate_package(pkg, env) end """ Base.identify_package_env(name::String)::Union{Tuple{PkgId, String}, Nothing} Base.identify_package_env(where::Union{Module,PkgId}, name::String)::Union{Tuple{PkgId, String} Nothing} Same as [`Base.identify_package`](@ref) except that the path to the environment where the package is identified is also returned. """ identify_package_env(where::Module, name::String) = identify_package_env(PkgId(where), name) function identify_package_env(where::PkgId, name::String) cache = LOADING_CACHE[] if cache !== nothing pkg_env = get(cache.identified_where, (where, name), nothing) pkg_env === nothing || return pkg_env end pkg_env = nothing if where.name === name pkg_env = where, nothing elseif where.uuid === nothing pkg_env = identify_package_env(name) # ignore `where` else for env in load_path() pkgid = manifest_deps_get(env, where, name) pkgid === nothing && continue # not found--keep looking if pkgid.uuid !== nothing pkg_env = pkgid, env # found in explicit environment--use it end break # found in implicit environment--return "not found" end end if cache !== nothing cache.identified_where[(where, name)] = pkg_env end return pkg_env end function identify_package_env(name::String) cache = LOADING_CACHE[] if cache !== nothing pkg_env = get(cache.identified, name, nothing) pkg_env === nothing || return pkg_env end pkg_env = nothing for env in load_path() pkg = project_deps_get(env, name) if pkg !== nothing pkg_env = pkg, env # found--return it break end end if cache !== nothing cache.identified[name] = pkg_env end return pkg_env end _nothing_or_first(x) = x === nothing ? nothing : first(x) """ Base.identify_package(name::String)::Union{PkgId, Nothing} Base.identify_package(where::Union{Module,PkgId}, name::String)::Union{PkgId, Nothing} Identify the package by its name from the current environment stack, returning its `PkgId`, or `nothing` if it cannot be found. If only the `name` argument is provided, it searches each environment in the stack and its named direct dependencies. There `where` argument provides the context from where to search for the package: in this case it first checks if the name matches the context itself, otherwise it searches all recursive dependencies (from the resolved manifest of each environment) until it locates the context `where`, and from there identifies the dependency with the corresponding name. ```julia-repl julia> Base.identify_package("Pkg") # Pkg is a dependency of the default environment Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f] julia> using LinearAlgebra julia> Base.identify_package(LinearAlgebra, "Pkg") # Pkg is not a dependency of LinearAlgebra ``` """ identify_package(where::Module, name::String) = _nothing_or_first(identify_package_env(where, name)) identify_package(where::PkgId, name::String) = _nothing_or_first(identify_package_env(where, name)) identify_package(name::String) = _nothing_or_first(identify_package_env(name)) function locate_package_env(pkg::PkgId, stopenv::Union{String, Nothing}=nothing) cache = LOADING_CACHE[] if cache !== nothing pathenv = get(cache.located, (pkg, stopenv), nothing) pathenv === nothing || return pathenv end path = nothing envโ€ฒ = nothing if pkg.uuid === nothing for env in load_path() envโ€ฒ = env # look for the toplevel pkg `pkg.name` in this entry found = project_deps_get(env, pkg.name) if found !== nothing @assert found.name == pkg.name if found.uuid === nothing # pkg.name is present in this directory or project file, # return the path the entry point for the code, if it could be found # otherwise, signal failure path = implicit_manifest_uuid_path(env, pkg) @goto done end end if !(loading_extension || precompiling_extension) stopenv == env && @goto done end end else for env in load_path() envโ€ฒ = env path = manifest_uuid_path(env, pkg) # missing is used as a sentinel to stop looking further down in envs if path === missing path = nothing @goto done end if path !== nothing path = entry_path(path, pkg.name) @goto done end if !(loading_extension || precompiling_extension) stopenv == env && break end end # Allow loading of stdlibs if the name/uuid are given # e.g. if they have been explicitly added to the project/manifest mbypath = manifest_uuid_path(Sys.STDLIB, pkg) if mbypath isa String path = entry_path(mbypath, pkg.name) @goto done end end @label done if cache !== nothing cache.located[(pkg, stopenv)] = path, envโ€ฒ end return path, envโ€ฒ end """ Base.locate_package(pkg::PkgId)::Union{String, Nothing} The path to the entry-point file for the package corresponding to the identifier `pkg`, or `nothing` if not found. See also [`identify_package`](@ref). ```julia-repl julia> pkg = Base.identify_package("Pkg") Pkg [44cfe95a-1eb2-52ea-b672-e2afdf69b78f] julia> Base.locate_package(pkg) "/path/to/julia/stdlib/v$(VERSION.major).$(VERSION.minor)/Pkg/src/Pkg.jl" ``` """ function locate_package(pkg::PkgId, stopenv::Union{String, Nothing}=nothing)::Union{Nothing,String} _nothing_or_first(locate_package_env(pkg, stopenv)) end """ pathof(m::Module) Return the path of the `m.jl` file that was used to `import` module `m`, or `nothing` if `m` was not imported from a package. Use [`dirname`](@ref) to get the directory part and [`basename`](@ref) to get the file name part of the path. """ function pathof(m::Module) @lock require_lock begin pkgid = get(module_keys, m, nothing) pkgid === nothing && return nothing origin = get(pkgorigins, pkgid, nothing) origin === nothing && return nothing path = origin.path path === nothing && return nothing return fixup_stdlib_path(path) end end """ pkgdir(m::Module[, paths::String...]) Return the root directory of the package that declared module `m`, or `nothing` if `m` was not declared in a package. Optionally further path component strings can be provided to construct a path within the package root. To get the root directory of the package that implements the current module the form `pkgdir(@__MODULE__)` can be used. ```julia-repl julia> pkgdir(Foo) "/path/to/Foo.jl" julia> pkgdir(Foo, "src", "file.jl") "/path/to/Foo.jl/src/file.jl" ``` !!! compat "Julia 1.7" The optional argument `paths` requires at least Julia 1.7. """ function pkgdir(m::Module, paths::String...) rootmodule = moduleroot(m) path = pathof(rootmodule) path === nothing && return nothing return joinpath(dirname(dirname(path)), paths...) end function get_pkgversion_from_path(path) project_file = locate_project_file(path) if project_file isa String d = parsed_toml(project_file) v = get(d, "version", nothing) if v !== nothing return VersionNumber(v::String) end end return nothing end """ pkgversion(m::Module) Return the version of the package that imported module `m`, or `nothing` if `m` was not imported from a package, or imported from a package without a version field set. The version is read from the package's Project.toml during package load. To get the version of the package that imported the current module the form `pkgversion(@__MODULE__)` can be used. !!! compat "Julia 1.9" This function was introduced in Julia 1.9. """ function pkgversion(m::Module) path = pkgdir(m) path === nothing && return nothing @lock require_lock begin v = get_pkgversion_from_path(path) pkgorigin = get(pkgorigins, PkgId(moduleroot(m)), nothing) # Cache the version if pkgorigin !== nothing && pkgorigin.version === nothing pkgorigin.version = v end return v end end ## generic project & manifest API ## const project_names = ("JuliaProject.toml", "Project.toml") const manifest_names = ("JuliaManifest.toml", "Manifest.toml") const preferences_names = ("JuliaLocalPreferences.toml", "LocalPreferences.toml") function locate_project_file(env::String) for proj in project_names project_file = joinpath(env, proj) if isfile_casesensitive(project_file) return project_file end end return true end # classify the LOAD_PATH entry to be one of: # - `false`: nonexistent / nothing to see here # - `true`: `env` is an implicit environment # - `path`: the path of an explicit project file function env_project_file(env::String)::Union{Bool,String} @lock require_lock begin cache = LOADING_CACHE[] if cache !== nothing project_file = get(cache.env_project_file, env, nothing) project_file === nothing || return project_file end if isdir(env) project_file = locate_project_file(env) elseif basename(env) in project_names && isfile_casesensitive(env) project_file = env else project_file = false end if cache !== nothing cache.env_project_file[env] = project_file end return project_file end end function project_deps_get(env::String, name::String)::Union{Nothing,PkgId} project_file = env_project_file(env) if project_file isa String pkg_uuid = explicit_project_deps_get(project_file, name) pkg_uuid === nothing || return PkgId(pkg_uuid, name) elseif project_file return implicit_project_deps_get(env, name) end return nothing end function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothing,PkgId} uuid = where.uuid @assert uuid !== nothing project_file = env_project_file(env) if project_file isa String # first check if `where` names the Project itself proj = project_file_name_uuid(project_file, where.name) if proj == where # if `where` matches the project, use [deps] section as manifest, and stop searching pkg_uuid = explicit_project_deps_get(project_file, name) return PkgId(pkg_uuid, name) end d = parsed_toml(project_file) exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} if exts !== nothing # Check if `where` is an extension of the project if where.name in keys(exts) && where.uuid == uuid5(proj.uuid::UUID, where.name) # Extensions can load weak deps... weakdeps = get(d, "weakdeps", nothing)::Union{Dict{String, Any}, Nothing} if weakdeps !== nothing wuuid = get(weakdeps, name, nothing)::Union{String, Nothing} if wuuid !== nothing return PkgId(UUID(wuuid), name) end end # ... and they can load same deps as the project itself mby_uuid = explicit_project_deps_get(project_file, name) mby_uuid === nothing || return PkgId(mby_uuid, name) end end # look for manifest file and `where` stanza return explicit_manifest_deps_get(project_file, where, name) elseif project_file # if env names a directory, search it return implicit_manifest_deps_get(env, where, name) end return nothing end function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missing} project_file = env_project_file(env) if project_file isa String proj = project_file_name_uuid(project_file, pkg.name) if proj == pkg # if `pkg` matches the project, return the project itself return project_file_path(project_file) end mby_ext = project_file_ext_path(project_file, pkg.name) mby_ext === nothing || return mby_ext # look for manifest file and `where` stanza return explicit_manifest_uuid_path(project_file, pkg) elseif project_file # if env names a directory, search it return implicit_manifest_uuid_path(env, pkg) end return nothing end function find_ext_path(project_path::String, extname::String) extfiledir = joinpath(project_path, "ext", extname, extname * ".jl") isfile(extfiledir) && return extfiledir return joinpath(project_path, "ext", extname * ".jl") end function project_file_ext_path(project_file::String, name::String) d = parsed_toml(project_file) p = project_file_path(project_file) exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} if exts !== nothing if name in keys(exts) return find_ext_path(p, name) end end return nothing end # find project file's top-level UUID entry (or nothing) function project_file_name_uuid(project_file::String, name::String)::PkgId d = parsed_toml(project_file) uuidโ€ฒ = get(d, "uuid", nothing)::Union{String, Nothing} uuid = uuidโ€ฒ === nothing ? dummy_uuid(project_file) : UUID(uuidโ€ฒ) name = get(d, "name", name)::String return PkgId(uuid, name) end function project_file_path(project_file::String) d = parsed_toml(project_file) joinpath(dirname(project_file), get(d, "path", "")::String) end # find project file's corresponding manifest file function project_file_manifest_path(project_file::String)::Union{Nothing,String} @lock require_lock begin cache = LOADING_CACHE[] if cache !== nothing manifest_path = get(cache.project_file_manifest_path, project_file, missing) manifest_path === missing || return manifest_path end dir = abspath(dirname(project_file)) d = parsed_toml(project_file) explicit_manifest = get(d, "manifest", nothing)::Union{String, Nothing} manifest_path = nothing if explicit_manifest !== nothing manifest_file = normpath(joinpath(dir, explicit_manifest)) if isfile_casesensitive(manifest_file) manifest_path = manifest_file end end if manifest_path === nothing for mfst in manifest_names manifest_file = joinpath(dir, mfst) if isfile_casesensitive(manifest_file) manifest_path = manifest_file break end end end if cache !== nothing cache.project_file_manifest_path[project_file] = manifest_path end return manifest_path end end # given a directory (implicit env from LOAD_PATH) and a name, # check if it is an implicit package function entry_point_and_project_file_inside(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}} path = normpath(joinpath(dir, "src", "$name.jl")) isfile_casesensitive(path) || return nothing, nothing for proj in project_names project_file = normpath(joinpath(dir, proj)) isfile_casesensitive(project_file) || continue return path, project_file end return path, nothing end # given a project directory (implicit env from LOAD_PATH) and a name, # find an entry point for `name`, and see if it has an associated project file function entry_point_and_project_file(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}} path = normpath(joinpath(dir, "$name.jl")) isfile_casesensitive(path) && return path, nothing dir = joinpath(dir, name) path, project_file = entry_point_and_project_file_inside(dir, name) path === nothing || return path, project_file dir = dir * ".jl" path, project_file = entry_point_and_project_file_inside(dir, name) path === nothing || return path, project_file return nothing, nothing end # given a path and a name, return the entry point function entry_path(path::String, name::String)::Union{Nothing,String} isfile_casesensitive(path) && return normpath(path) path = normpath(joinpath(path, "src", "$name.jl")) isfile_casesensitive(path) && return path return nothing # source not found end ## explicit project & manifest API ## # find project file root or deps `name => uuid` mapping # return `nothing` if `name` is not found function explicit_project_deps_get(project_file::String, name::String)::Union{Nothing,UUID} d = parsed_toml(project_file) root_uuid = dummy_uuid(project_file) if get(d, "name", nothing)::Union{String, Nothing} === name uuid = get(d, "uuid", nothing)::Union{String, Nothing} return uuid === nothing ? root_uuid : UUID(uuid) end deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing} if deps !== nothing uuid = get(deps, name, nothing)::Union{String, Nothing} uuid === nothing || return UUID(uuid) end return nothing end function is_v1_format_manifest(raw_manifest::Dict{String}) if haskey(raw_manifest, "manifest_format") mf = raw_manifest["manifest_format"] if mf isa Dict{String} && haskey(mf, "uuid") # the off-chance where an old format manifest has a dep called "manifest_format" return true end return false else return true end end # returns a deps list for both old and new manifest formats function get_deps(raw_manifest::Dict) if is_v1_format_manifest(raw_manifest) return raw_manifest else # if the manifest has no deps, there won't be a `deps` field return get(Dict{String, Any}, raw_manifest, "deps")::Dict{String, Any} end end # find `where` stanza and return the PkgId for `name` # return `nothing` if it did not find `where` (indicating caller should continue searching) function explicit_manifest_deps_get(project_file::String, where::PkgId, name::String)::Union{Nothing,PkgId} manifest_file = project_file_manifest_path(project_file) manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH d = get_deps(parsed_toml(manifest_file)) found_where = false found_name = false for (dep_name, entries) in d entries::Vector{Any} for entry in entries entry = entry::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{String, Nothing} uuid === nothing && continue if UUID(uuid) === where.uuid found_where = true # deps is either a list of names (deps = ["DepA", "DepB"]) or # a table of entries (deps = {"DepA" = "6ea...", "DepB" = "55d..."} deps = get(entry, "deps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} if deps isa Vector{String} found_name = name in deps break elseif deps isa Dict{String, Any} deps = deps::Dict{String, Any} for (dep, uuid) in deps uuid::String if dep === name return PkgId(UUID(uuid), name) end end end else # Check for extensions extensions = get(entry, "extensions", nothing) if extensions !== nothing if haskey(extensions, where.name) && where.uuid == uuid5(UUID(uuid), where.name) found_where = true if name == dep_name return PkgId(UUID(uuid), name) end exts = extensions[where.name]::Union{String, Vector{String}} if (exts isa String && name == exts) || (exts isa Vector{String} && name in exts) weakdeps = get(entry, "weakdeps", nothing)::Union{Vector{String}, Dict{String, Any}, Nothing} if weakdeps !== nothing if weakdeps isa Vector{String} found_name = name in weakdeps break elseif weakdeps isa Dict{String, Any} weakdeps = weakdeps::Dict{String, Any} for (dep, uuid) in weakdeps uuid::String if dep === name return PkgId(UUID(uuid), name) end end end end end # `name` is not an ext, do standard lookup as if this was the parent return identify_package(PkgId(UUID(uuid), dep_name), name) end end end end end found_where || return nothing found_name || return PkgId(name) # Only reach here if deps was not a dict which mean we have a unique name for the dep name_deps = get(d, name, nothing)::Union{Nothing, Vector{Any}} if name_deps === nothing || length(name_deps) != 1 error("expected a single entry for $(repr(name)) in $(repr(project_file))") end entry = first(name_deps::Vector{Any})::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{String, Nothing} uuid === nothing && return nothing return PkgId(UUID(uuid), name) end # find `uuid` stanza, return the corresponding path function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{Nothing,String,Missing} manifest_file = project_file_manifest_path(project_file) manifest_file === nothing && return nothing # no manifest, skip env d = get_deps(parsed_toml(manifest_file)) entries = get(d, pkg.name, nothing)::Union{Nothing, Vector{Any}} if entries !== nothing for entry in entries entry = entry::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{Nothing, String} uuid === nothing && continue if UUID(uuid) === pkg.uuid return explicit_manifest_entry_path(manifest_file, pkg, entry) end end end # Extensions for (name, entries) in d entries = entries::Vector{Any} for entry in entries uuid = get(entry, "uuid", nothing)::Union{Nothing, String} extensions = get(entry, "extensions", nothing)::Union{Nothing, Dict{String, Any}} if extensions !== nothing && haskey(extensions, pkg.name) && uuid !== nothing && uuid5(UUID(uuid), pkg.name) == pkg.uuid parent_path = locate_package(PkgId(UUID(uuid), name)) if parent_path === nothing error("failed to find source of parent package: \"$name\"") end p = normpath(dirname(parent_path), "..") return find_ext_path(p, pkg.name) end end end return nothing end function explicit_manifest_entry_path(manifest_file::String, pkg::PkgId, entry::Dict{String,Any}) path = get(entry, "path", nothing)::Union{Nothing, String} if path !== nothing path = normpath(abspath(dirname(manifest_file), path)) return path end hash = get(entry, "git-tree-sha1", nothing)::Union{Nothing, String} if hash === nothing mbypath = manifest_uuid_path(Sys.STDLIB, pkg) if mbypath isa String return entry_path(mbypath, pkg.name) end return nothing end hash = SHA1(hash) # Keep the 4 since it used to be the default uuid = pkg.uuid::UUID # checked within `explicit_manifest_uuid_path` for slug in (version_slug(uuid, hash), version_slug(uuid, hash, 4)) for depot in DEPOT_PATH path = joinpath(depot, "packages", pkg.name, slug) ispath(path) && return abspath(path) end end # no depot contains the package, return missing to stop looking return missing end ## implicit project & manifest API ## # look for an entry point for `name` from a top-level package (no environment) # otherwise return `nothing` to indicate the caller should keep searching function implicit_project_deps_get(dir::String, name::String)::Union{Nothing,PkgId} path, project_file = entry_point_and_project_file(dir, name) if project_file === nothing path === nothing && return nothing return PkgId(name) end proj = project_file_name_uuid(project_file, name) proj.name == name || return nothing return proj end # look for an entry-point for `name`, check that UUID matches # if there's a project file, look up `name` in its deps and return that # otherwise return `nothing` to indicate the caller should keep searching function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Union{Nothing,PkgId} @assert where.uuid !== nothing project_file = entry_point_and_project_file(dir, where.name)[2] project_file === nothing && return nothing # a project file is mandatory for a package with a uuid proj = project_file_name_uuid(project_file, where.name) proj == where || return nothing # verify that this is the correct project file # this is the correct project, so stop searching here pkg_uuid = explicit_project_deps_get(project_file, name) return PkgId(pkg_uuid, name) end # look for an entry-point for `pkg` and return its path if UUID matches function implicit_manifest_uuid_path(dir::String, pkg::PkgId)::Union{Nothing,String} path, project_file = entry_point_and_project_file(dir, pkg.name) if project_file === nothing pkg.uuid === nothing || return nothing return path end proj = project_file_name_uuid(project_file, pkg.name) proj == pkg || return nothing return path end ## other code loading functionality ## function find_source_file(path::AbstractString) (isabspath(path) || isfile(path)) && return path base_path = joinpath(Sys.BINDIR, DATAROOTDIR, "julia", "base", path) return isfile(base_path) ? normpath(base_path) : nothing end cache_file_entry(pkg::PkgId) = joinpath( "compiled", "v$(VERSION.major).$(VERSION.minor)", pkg.uuid === nothing ? "" : pkg.name), pkg.uuid === nothing ? pkg.name : package_slug(pkg.uuid) function find_all_in_cache_path(pkg::PkgId) paths = String[] entrypath, entryfile = cache_file_entry(pkg) for path in joinpath.(DEPOT_PATH, entrypath) isdir(path) || continue for file in readdir(path, sort = false) # no sort given we sort later if !((pkg.uuid === nothing && file == entryfile * ".ji") || (pkg.uuid !== nothing && startswith(file, entryfile * "_") && endswith(file, ".ji"))) continue end filepath = joinpath(path, file) isfile_casesensitive(filepath) && push!(paths, filepath) end end if length(paths) > 1 # allocating the sort vector is less expensive than using sort!(.. by=mtime), which would # call the relatively slow mtime multiple times per path p = sortperm(mtime.(paths), rev = true) return paths[p] else return paths end end ocachefile_from_cachefile(cachefile) = string(chopsuffix(cachefile, ".ji"), ".", Base.Libc.dlext) cachefile_from_ocachefile(cachefile) = string(chopsuffix(cachefile, ".$(Base.Libc.dlext)"), ".ji") # use an Int counter so that nested @time_imports calls all remain open const TIMING_IMPORTS = Threads.Atomic{Int}(0) # these return either the array of modules loaded from the path / content given # or an Exception that describes why it couldn't be loaded # and it reconnects the Base.Docs.META function _include_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}, depmods::Vector{Any}) assert_havelock(require_lock) timing_imports = TIMING_IMPORTS[] > 0 try if timing_imports t_before = time_ns() cumulative_compile_timing(true) t_comp_before = cumulative_compile_time_ns() end if ocachepath !== nothing @debug "Loading object cache file $ocachepath for $pkg" sv = ccall(:jl_restore_package_image_from_file, Any, (Cstring, Any, Cint, Cstring), ocachepath, depmods, false, pkg.name) else @debug "Loading cache file $path for $pkg" sv = ccall(:jl_restore_incremental, Any, (Cstring, Any, Cint, Cstring), path, depmods, false, pkg.name) end if isa(sv, Exception) return sv end restored = register_restored_modules(sv, pkg, path) for M in restored M = M::Module if parentmodule(M) === M && PkgId(M) == pkg if timing_imports elapsed = round((time_ns() - t_before) / 1e6, digits = 1) comp_time, recomp_time = cumulative_compile_time_ns() .- t_comp_before print(lpad(elapsed, 9), " ms ") parentid = get(EXT_PRIMED, pkg, nothing) if parentid !== nothing print(parentid.name, " โ†’ ") end print(pkg.name) if comp_time > 0 printstyled(" ", Ryu.writefixed(Float64(100 * comp_time / (elapsed * 1e6)), 2), "% compilation time", color = Base.info_color()) end if recomp_time > 0 perc = Float64(100 * recomp_time / comp_time) printstyled(" (", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% recompilation)", color = Base.warn_color()) end println() end return M end end return ErrorException("Required dependency $pkg failed to load from a cache file.") finally timing_imports && cumulative_compile_timing(false) end end function register_restored_modules(sv::SimpleVector, pkg::PkgId, path::String) # This function is also used by PkgCacheInspector.jl restored = sv[1]::Vector{Any} for M in restored M = M::Module if isdefined(M, Base.Docs.META) && getfield(M, Base.Docs.META) !== nothing push!(Base.Docs.modules, M) end if parentmodule(M) === M register_root_module(M) end end # Register this cache path now - If Requires.jl is loaded, Revise may end # up looking at the cache path during the init callback. get!(PkgOrigin, pkgorigins, pkg).cachepath = path inits = sv[2]::Vector{Any} if !isempty(inits) unlock(require_lock) # temporarily _unlock_ during these callbacks try for (i, mod) in pairs(inits) run_module_init(mod, i) end finally lock(require_lock) end end return restored end function run_module_init(mod::Module, i::Int=1) # `i` informs ordering for the `@time_imports` report formatting if TIMING_IMPORTS[] == 0 ccall(:jl_init_restored_module, Cvoid, (Any,), mod) else if isdefined(mod, :__init__) connector = i > 1 ? "โ”œ" : "โ”Œ" printstyled(" $connector ", color = :light_black) elapsedtime = time_ns() cumulative_compile_timing(true) compile_elapsedtimes = cumulative_compile_time_ns() ccall(:jl_init_restored_module, Cvoid, (Any,), mod) elapsedtime = (time_ns() - elapsedtime) / 1e6 cumulative_compile_timing(false); comp_time, recomp_time = (cumulative_compile_time_ns() .- compile_elapsedtimes) ./ 1e6 print(round(elapsedtime, digits=1), " ms $mod.__init__() ") if comp_time > 0 printstyled(Ryu.writefixed(Float64(100 * comp_time / elapsedtime), 2), "% compilation time", color = Base.info_color()) end if recomp_time > 0 perc = Float64(100 * recomp_time / comp_time) printstyled(" (", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% recompilation)", color = Base.warn_color()) end println() end end end function run_package_callbacks(modkey::PkgId) run_extension_callbacks(modkey) assert_havelock(require_lock) unlock(require_lock) try for callback in package_callbacks invokelatest(callback, modkey) end catch # Try to continue loading if a callback errors errs = current_exceptions() @error "Error during package callback" exception=errs finally lock(require_lock) end nothing end ############## # Extensions # ############## mutable struct ExtensionId const id::PkgId const parentid::PkgId # just need the name, for printing ntriggers::Int # how many more packages must be defined until this is loaded end const EXT_PRIMED = Dict{PkgId, PkgId}() # Extension -> Parent const EXT_DORMITORY = Dict{PkgId,Vector{ExtensionId}}() # Trigger -> Extensions that can be triggered by it const EXT_DORMITORY_FAILED = ExtensionId[] function insert_extension_triggers(pkg::PkgId) pkg.uuid === nothing && return path_env_loc = locate_package_env(pkg) path_env_loc === nothing && return path, env_loc = path_env_loc if path === nothing || env_loc === nothing return end insert_extension_triggers(env_loc, pkg) end function insert_extension_triggers(env::String, pkg::PkgId)::Union{Nothing,Missing} project_file = env_project_file(env) if project_file isa String # Look in project for extensions to insert proj_pkg = project_file_name_uuid(project_file, pkg.name) if pkg == proj_pkg d_proj = parsed_toml(project_file) weakdeps = get(d_proj, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}} extensions = get(d_proj, "extensions", nothing)::Union{Nothing, Dict{String, Any}} extensions === nothing && return weakdeps === nothing && return if weakdeps isa Dict{String, Any} return _insert_extension_triggers(pkg, extensions, weakdeps) end end # Now look in manifest manifest_file = project_file_manifest_path(project_file) manifest_file === nothing && return d = get_deps(parsed_toml(manifest_file)) for (dep_name, entries) in d entries::Vector{Any} for entry in entries entry = entry::Dict{String, Any} uuid = get(entry, "uuid", nothing)::Union{String, Nothing} uuid === nothing && continue if UUID(uuid) == pkg.uuid weakdeps = get(entry, "weakdeps", nothing)::Union{Nothing, Vector{String}, Dict{String,Any}} extensions = get(entry, "extensions", nothing)::Union{Nothing, Dict{String, Any}} extensions === nothing && return weakdeps === nothing && return if weakdeps isa Dict{String, Any} return _insert_extension_triggers(pkg, extensions, weakdeps) end d_weakdeps = Dict{String, Any}() for (dep_name, entries) in d dep_name in weakdeps || continue entries::Vector{Any} if length(entries) != 1 error("expected a single entry for $(repr(dep_name)) in $(repr(project_file))") end entry = first(entries)::Dict{String, Any} uuid = entry["uuid"]::String d_weakdeps[dep_name] = uuid end @assert length(d_weakdeps) == length(weakdeps) return _insert_extension_triggers(pkg, extensions, d_weakdeps) end end end end return nothing end function _insert_extension_triggers(parent::PkgId, extensions::Dict{String, Any}, weakdeps::Dict{String, Any}) for (ext, triggers) in extensions triggers = triggers::Union{String, Vector{String}} triggers isa String && (triggers = [triggers]) id = PkgId(uuid5(parent.uuid, ext), ext) if id in keys(EXT_PRIMED) || haskey(Base.loaded_modules, id) continue # extension is already primed or loaded, don't add it again end EXT_PRIMED[id] = parent gid = ExtensionId(id, parent, 1 + length(triggers)) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, parent) push!(trigger1, gid) for trigger in triggers # TODO: Better error message if this lookup fails? uuid_trigger = UUID(weakdeps[trigger]::String) trigger_id = PkgId(uuid_trigger, trigger) if !haskey(Base.loaded_modules, trigger_id) || haskey(package_locks, trigger_id) trigger1 = get!(Vector{ExtensionId}, EXT_DORMITORY, trigger_id) push!(trigger1, gid) else gid.ntriggers -= 1 end end end end loading_extension::Bool = false precompiling_extension::Bool = false function run_extension_callbacks(extid::ExtensionId) assert_havelock(require_lock) succeeded = try # Used by Distributed to now load extensions in the package callback global loading_extension = true _require_prelocked(extid.id) @debug "Extension $(extid.id.name) of $(extid.parentid.name) loaded" true catch # Try to continue loading if loading an extension errors errs = current_exceptions() @error "Error during loading of extension $(extid.id.name) of $(extid.parentid.name), \ use `Base.retry_load_extensions()` to retry." exception=errs false finally global loading_extension = false end return succeeded end function run_extension_callbacks(pkgid::PkgId) assert_havelock(require_lock) # take ownership of extids that depend on this pkgid extids = pop!(EXT_DORMITORY, pkgid, nothing) extids === nothing && return for extid in extids if extid.ntriggers > 0 # indicate pkgid is loaded extid.ntriggers -= 1 end if extid.ntriggers < 0 # indicate pkgid is loaded extid.ntriggers += 1 succeeded = false else succeeded = true end if extid.ntriggers == 0 # actually load extid, now that all dependencies are met, # and record the result succeeded = succeeded && run_extension_callbacks(extid) succeeded || push!(EXT_DORMITORY_FAILED, extid) end end return end """ retry_load_extensions() Loads all the (not yet loaded) extensions that have their extension-dependencies loaded. This is used in cases where the automatic loading of an extension failed due to some problem with the extension. Instead of restarting the Julia session, the extension can be fixed, and this function run. """ function retry_load_extensions() @lock require_lock begin # this copy is desired since run_extension_callbacks will release this lock # so this can still mutate the list to drop successful ones failed = copy(EXT_DORMITORY_FAILED) empty!(EXT_DORMITORY_FAILED) filter!(failed) do extid return !run_extension_callbacks(extid) end prepend!(EXT_DORMITORY_FAILED, failed) end return end """ get_extension(parent::Module, extension::Symbol) Return the module for `extension` of `parent` or return `nothing` if the extension is not loaded. """ get_extension(parent::Module, ext::Symbol) = get_extension(PkgId(parent), ext) function get_extension(parentid::PkgId, ext::Symbol) parentid.uuid === nothing && return nothing extid = PkgId(uuid5(parentid.uuid, string(ext)), string(ext)) return get(loaded_modules, extid, nothing) end # End extensions # should sync with the types of arguments of `stale_cachefile` const StaleCacheKey = Tuple{Base.PkgId, UInt128, String, String} """ Base.isprecompiled(pkg::PkgId; ignore_loaded::Bool=false) Returns whether a given PkgId within the active project is precompiled. By default this check observes the same approach that code loading takes with respect to when different versions of dependencies are currently loaded to that which is expected. To ignore loaded modules and answer as if in a fresh julia session specify `ignore_loaded=true`. !!! compat "Julia 1.10" This function requires at least Julia 1.10. """ function isprecompiled(pkg::PkgId; ignore_loaded::Bool=false, stale_cache::Dict{StaleCacheKey,Bool}=Dict{StaleCacheKey, Bool}(), cachepaths::Vector{String}=Base.find_all_in_cache_path(pkg), sourcepath::Union{String,Nothing}=Base.locate_package(pkg) ) isnothing(sourcepath) && error("Cannot locate source for $(repr(pkg))") for path_to_try in cachepaths staledeps = stale_cachefile(sourcepath, path_to_try, ignore_loaded = true) if staledeps === true continue end staledeps, _ = staledeps::Tuple{Vector{Any}, Union{Nothing, String}} # finish checking staledeps module graph for i in 1:length(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} modpaths = find_all_in_cache_path(modkey) for modpath_to_try in modpaths::Vector{String} stale_cache_key = (modkey, modbuild_id, modpath, modpath_to_try)::StaleCacheKey if get!(() -> stale_cachefile(stale_cache_key...; ignore_loaded) === true, stale_cache, stale_cache_key) continue end @goto check_next_dep end @goto check_next_path @label check_next_dep end try # update timestamp of precompilation file so that it is the first to be tried by code loading touch(path_to_try) catch ex # file might be read-only and then we fail to update timestamp, which is fine ex isa IOError || rethrow() end return true @label check_next_path end return false end # loads a precompile cache file, after checking stale_cachefile tests function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt128) assert_havelock(require_lock) loaded = nothing if root_module_exists(modkey) loaded = root_module(modkey) else loaded = start_loading(modkey) if loaded === nothing try modpath = locate_package(modkey) modpath === nothing && return nothing set_pkgorigin_version_path(modkey, String(modpath)) loaded = _require_search_from_serialized(modkey, String(modpath), build_id) finally end_loading(modkey, loaded) end if loaded isa Module insert_extension_triggers(modkey) run_package_callbacks(modkey) end end end if !(loaded isa Module) || PkgId(loaded) != modkey return ErrorException("Required dependency $modkey failed to load from a cache file.") end return loaded end # loads a precompile cache file, ignoring stale_cachefile tests # assuming all depmods are already loaded and everything is valid function _tryrequire_from_serialized(modkey::PkgId, path::String, ocachepath::Union{Nothing, String}, sourcepath::String, depmods::Vector{Any}) assert_havelock(require_lock) loaded = nothing if root_module_exists(modkey) loaded = root_module(modkey) else loaded = start_loading(modkey) if loaded === nothing try for i in 1:length(depmods) dep = depmods[i] dep isa Module && continue _, depkey, depbuild_id = dep::Tuple{String, PkgId, UInt128} @assert root_module_exists(depkey) dep = root_module(depkey) depmods[i] = dep end set_pkgorigin_version_path(modkey, sourcepath) loaded = _include_from_serialized(modkey, path, ocachepath, depmods) finally end_loading(modkey, loaded) end if loaded isa Module insert_extension_triggers(modkey) run_package_callbacks(modkey) end end end if !(loaded isa Module) || PkgId(loaded) != modkey return ErrorException("Required dependency $modkey failed to load from a cache file.") end return loaded end # loads a precompile cache file, ignoring stale_cachefile tests # load the best available (non-stale) version of all dependent modules first function _tryrequire_from_serialized(pkg::PkgId, path::String, ocachepath::Union{Nothing, String}) assert_havelock(require_lock) local depmodnames io = open(path, "r") try iszero(isvalid_cache_header(io)) && return ArgumentError("Invalid header in cache file $path.") _, _, depmodnames, _, _, _, clone_targets, _ = parse_cache_header(io) pkgimage = !isempty(clone_targets) if pkgimage ocachepath !== nothing || return ArgumentError("Expected ocachepath to be provided") isfile(ocachepath) || return ArgumentError("Ocachepath $ocachepath is not a file.") ocachepath == ocachefile_from_cachefile(path) || return ArgumentError("$ocachepath is not the expected ocachefile") # TODO: Check for valid clone_targets? isvalid_pkgimage_crc(io, ocachepath) || return ArgumentError("Invalid checksum in cache file $ocachepath.") else @assert ocachepath === nothing end isvalid_file_crc(io) || return ArgumentError("Invalid checksum in cache file $path.") finally close(io) end ndeps = length(depmodnames) depmods = Vector{Any}(undef, ndeps) for i in 1:ndeps modkey, build_id = depmodnames[i] dep = _tryrequire_from_serialized(modkey, build_id) if !isa(dep, Module) return dep end depmods[i] = dep end # then load the file return _include_from_serialized(pkg, path, ocachepath, depmods) end # returns `nothing` if require found a precompile cache for this sourcepath, but couldn't load it # returns the set of modules restored if the cache load succeeded @constprop :none function _require_search_from_serialized(pkg::PkgId, sourcepath::String, build_id::UInt128) assert_havelock(require_lock) paths = find_all_in_cache_path(pkg) for path_to_try in paths::Vector{String} staledeps = stale_cachefile(pkg, build_id, sourcepath, path_to_try) if staledeps === true continue end staledeps, ocachefile = staledeps::Tuple{Vector{Any}, Union{Nothing, String}} # finish checking staledeps module graph for i in 1:length(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modbuild_id = dep::Tuple{String, PkgId, UInt128} modpaths = find_all_in_cache_path(modkey) for modpath_to_try in modpaths modstaledeps = stale_cachefile(modkey, modbuild_id, modpath, modpath_to_try) if modstaledeps === true continue end modstaledeps, modocachepath = modstaledeps::Tuple{Vector{Any}, Union{Nothing, String}} staledeps[i] = (modpath, modkey, modpath_to_try, modstaledeps, modocachepath) @goto check_next_dep end @debug "Rejecting cache file $path_to_try because required dependency $modkey with build ID $(UUID(modbuild_id)) is missing from the cache." @goto check_next_path @label check_next_dep end try touch(path_to_try) # update timestamp of precompilation file catch ex # file might be read-only and then we fail to update timestamp, which is fine ex isa IOError || rethrow() end # finish loading module graph into staledeps for i in 1:length(staledeps) dep = staledeps[i] dep isa Module && continue modpath, modkey, modcachepath, modstaledeps, modocachepath = dep::Tuple{String, PkgId, String, Vector{Any}, Union{Nothing, String}} dep = _tryrequire_from_serialized(modkey, modcachepath, modocachepath, modpath, modstaledeps) if !isa(dep, Module) @debug "Rejecting cache file $path_to_try because required dependency $modkey failed to load from cache file for $modcachepath." exception=dep @goto check_next_path end staledeps[i] = dep end restored = _include_from_serialized(pkg, path_to_try, ocachefile, staledeps) isa(restored, Module) && return restored @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored continue @label check_next_path end return nothing end # to synchronize multiple tasks trying to import/using something const package_locks = Dict{PkgId,Pair{Task,Threads.Condition}}() debug_loading_deadlocks::Bool = true # Enable a slightly more expensive, but more complete algorithm that can handle simultaneous tasks. # This only triggers if you have multiple tasks trying to load the same package at the same time, # so it is unlikely to make a difference normally. function start_loading(modkey::PkgId) # handle recursive calls to require assert_havelock(require_lock) loading = get(package_locks, modkey, nothing) if loading !== nothing # load already in progress for this module on the task task, cond = loading deps = String[modkey.name] pkgid = modkey assert_havelock(cond.lock) if debug_loading_deadlocks && current_task() !== task waiters = Dict{Task,Pair{Task,PkgId}}() # invert to track waiting tasks => loading tasks for each in package_locks cond2 = each[2][2] assert_havelock(cond2.lock) for waiting in cond2.waitq push!(waiters, waiting => (each[2][1] => each[1])) end end while true running = get(waiters, task, nothing) running === nothing && break task, pkgid = running push!(deps, pkgid.name) task === current_task() && break end end if current_task() === task others = String[modkey.name] # repeat this to emphasize the cycle here for each in package_locks # list the rest of the packages being loaded too if each[2][1] === task other = each[1].name other == modkey.name || other == pkgid.name || push!(others, other) end end msg = sprint(deps, others) do io, deps, others print(io, "deadlock detected in loading ") join(io, deps, " -> ") print(io, " -> ") join(io, others, " && ") end throw(ConcurrencyViolationError(msg)) end return wait(cond) end package_locks[modkey] = current_task() => Threads.Condition(require_lock) return end function end_loading(modkey::PkgId, @nospecialize loaded) loading = pop!(package_locks, modkey) notify(loading[2], loaded, all=true) nothing end # to notify downstream consumers that a module was successfully loaded # Callbacks take the form (mod::Base.PkgId) -> nothing. # WARNING: This is an experimental feature and might change later, without deprecation. const package_callbacks = Any[] # to notify downstream consumers that a file has been included into a particular module # Callbacks take the form (mod::Module, filename::String) -> nothing # WARNING: This is an experimental feature and might change later, without deprecation. const include_callbacks = Any[] # used to optionally track dependencies when requiring a module: const _concrete_dependencies = Pair{PkgId,UInt128}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them const _require_dependencies = Any[] # a list of (mod, path, mtime) tuples that are the file dependencies of the module currently being precompiled const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies function _include_dependency(mod::Module, _path::AbstractString) prev = source_path(nothing) if prev === nothing path = abspath(_path) else path = normpath(joinpath(dirname(prev), _path)) end if _track_dependencies[] @lock require_lock begin push!(_require_dependencies, (mod, path, mtime(path))) end end return path, prev end """ include_dependency(path::AbstractString) In a module, declare that the file, directory, or symbolic link specified by `path` (relative or absolute) is a dependency for precompilation; that is, the module will need to be recompiled if the modification time of `path` changes. This is only needed if your module depends on a path that is not used via [`include`](@ref). It has no effect outside of compilation. """ function include_dependency(path::AbstractString) _include_dependency(Main, path) return nothing end # we throw PrecompilableError when a module doesn't want to be precompiled import Core: PrecompilableError function show(io::IO, ex::PrecompilableError) print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.") end precompilableerror(ex::PrecompilableError) = true precompilableerror(ex::WrappedException) = precompilableerror(ex.error) precompilableerror(@nospecialize ex) = false # Call __precompile__(false) at the top of a tile prevent it from being precompiled (false) """ __precompile__(isprecompilable::Bool) Specify whether the file calling this function is precompilable, defaulting to `true`. If a module or file is *not* safely precompilable, it should call `__precompile__(false)` in order to throw an error if Julia attempts to precompile it. """ @noinline function __precompile__(isprecompilable::Bool=true) if !isprecompilable && ccall(:jl_generating_output, Cint, ()) != 0 throw(PrecompilableError()) end nothing end # require always works in Main scope and loads files from node 1 const toplevel_load = Ref(true) const _require_world_age = Ref{UInt}(typemax(UInt)) """ require(into::Module, module::Symbol) This function is part of the implementation of [`using`](@ref) / [`import`](@ref), if a module is not already defined in `Main`. It can also be called directly to force reloading a module, regardless of whether it has been loaded before (for example, when interactively developing libraries). Loads a source file, in the context of the `Main` module, on every active node, searching standard locations for files. `require` is considered a top-level operation, so it sets the current `include` path but does not use it to search for files (see help for [`include`](@ref)). This function is typically used to load library code, and is implicitly called by `using` to load packages. When searching for files, `require` first looks for package code in the global array [`LOAD_PATH`](@ref). `require` is case-sensitive on all platforms, including those with case-insensitive filesystems like macOS and Windows. For more details regarding code loading, see the manual sections on [modules](@ref modules) and [parallel computing](@ref code-availability). """ function require(into::Module, mod::Symbol) if _require_world_age[] != typemax(UInt) Base.invoke_in_world(_require_world_age[], __require, into, mod) else @invokelatest __require(into, mod) end end function __require(into::Module, mod::Symbol) @lock require_lock begin LOADING_CACHE[] = LoadingCache() try uuidkey_env = identify_package_env(into, String(mod)) # Core.println("require($(PkgId(into)), $mod) -> $uuidkey_env") if uuidkey_env === nothing where = PkgId(into) if where.uuid === nothing hint, dots = begin if isdefined(into, mod) && getfield(into, mod) isa Module true, "." elseif isdefined(parentmodule(into), mod) && getfield(parentmodule(into), mod) isa Module true, ".." else false, "" end end hint_message = hint ? ", maybe you meant `import/using $(dots)$(mod)`" : "" start_sentence = hint ? "Otherwise, run" : "Run" throw(ArgumentError(""" Package $mod not found in current path$hint_message. - $start_sentence `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package.""")) else throw(ArgumentError(""" Package $(where.name) does not have $mod in its dependencies: - You may have a partially installed environment. Try `Pkg.instantiate()` to ensure all packages in the environment are installed. - Or, if you have $(where.name) checked out for development and have added $mod as a dependency but haven't updated your primary environment's manifest file, try `Pkg.resolve()`. - Otherwise you may need to report an issue with $(where.name)""")) end end uuidkey, env = uuidkey_env if _track_dependencies[] push!(_require_dependencies, (into, binpack(uuidkey), 0.0)) end return _require_prelocked(uuidkey, env) finally LOADING_CACHE[] = nothing end end end require(uuidkey::PkgId) = @lock require_lock _require_prelocked(uuidkey) const REPL_PKGID = PkgId(UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL") function _require_prelocked(uuidkey::PkgId, env=nothing) if _require_world_age[] != typemax(UInt) Base.invoke_in_world(_require_world_age[], __require_prelocked, uuidkey, env) else @invokelatest __require_prelocked(uuidkey, env) end end function __require_prelocked(uuidkey::PkgId, env=nothing) assert_havelock(require_lock) if !root_module_exists(uuidkey) newm = _require(uuidkey, env) if newm === nothing error("package `$(uuidkey.name)` did not define the expected \ module `$(uuidkey.name)`, check for typos in package module name") end insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) if uuidkey == REPL_PKGID REPL_MODULE_REF[] = newm end else newm = root_module(uuidkey) end return newm end mutable struct PkgOrigin path::Union{String,Nothing} cachepath::Union{String,Nothing} version::Union{VersionNumber,Nothing} end PkgOrigin() = PkgOrigin(nothing, nothing, nothing) const pkgorigins = Dict{PkgId,PkgOrigin}() const loaded_modules = Dict{PkgId,Module}() const loaded_modules_order = Vector{Module}() const module_keys = IdDict{Module,PkgId}() # the reverse is_root_module(m::Module) = @lock require_lock haskey(module_keys, m) root_module_key(m::Module) = @lock require_lock module_keys[m] @constprop :none function register_root_module(m::Module) # n.b. This is called from C after creating a new module in `Base.__toplevel__`, # instead of adding them to the binding table there. @lock require_lock begin key = PkgId(m, String(nameof(m))) if haskey(loaded_modules, key) oldm = loaded_modules[key] if oldm !== m if (0 != ccall(:jl_generating_output, Cint, ())) && (JLOptions().incremental != 0) error("Replacing module `$(key.name)`") else @warn "Replacing module `$(key.name)`" end end end push!(loaded_modules_order, m) loaded_modules[key] = m module_keys[m] = key end nothing end register_root_module(Core) register_root_module(Base) register_root_module(Main) # This is used as the current module when loading top-level modules. # It has the special behavior that modules evaluated in it get added # to the loaded_modules table instead of getting bindings. baremodule __toplevel__ using Base end # get a top-level Module from the given key root_module(key::PkgId) = @lock require_lock loaded_modules[key] function root_module(where::Module, name::Symbol) key = identify_package(where, String(name)) key isa PkgId || throw(KeyError(name)) return root_module(key) end maybe_root_module(key::PkgId) = @lock require_lock get(loaded_modules, key, nothing) root_module_exists(key::PkgId) = @lock require_lock haskey(loaded_modules, key) loaded_modules_array() = @lock require_lock copy(loaded_modules_order) function unreference_module(key::PkgId) if haskey(loaded_modules, key) m = pop!(loaded_modules, key) # need to ensure all modules are GC rooted; will still be referenced # in module_keys end end # whoever takes the package_locks[pkg] must call this function immediately function set_pkgorigin_version_path(pkg::PkgId, path::Union{String,Nothing}) assert_havelock(require_lock) pkgorigin = get!(PkgOrigin, pkgorigins, pkg) if path !== nothing # Pkg needs access to the version of packages in the sysimage. if Core.Compiler.generating_sysimg() pkgorigin.version = get_pkgversion_from_path(joinpath(dirname(path), "..")) end end pkgorigin.path = path nothing end # A hook to allow code load to use Pkg.precompile const PKG_PRECOMPILE_HOOK = Ref{Function}() # Returns `nothing` or the new(ish) module function _require(pkg::PkgId, env=nothing) assert_havelock(require_lock) loaded = start_loading(pkg) loaded === nothing || return loaded last = toplevel_load[] try toplevel_load[] = false # perform the search operation to select the module file require intends to load path = locate_package(pkg, env) if path === nothing throw(ArgumentError(""" Package $pkg is required but does not seem to be installed: - Run `Pkg.instantiate()` to install all recorded dependencies. """)) end set_pkgorigin_version_path(pkg, path) pkg_precompile_attempted = false # being safe to avoid getting stuck in a Pkg.precompile loop # attempt to load the module file via the precompile cache locations if JLOptions().use_compiled_modules != 0 @label load_from_cache m = _require_search_from_serialized(pkg, path, UInt128(0)) if m isa Module return m end end # if the module being required was supposed to have a particular version # but it was not handled by the precompile loader, complain for (concrete_pkg, concrete_build_id) in _concrete_dependencies if pkg == concrete_pkg @warn """Module $(pkg.name) with build ID $((UUID(concrete_build_id))) is missing from the cache. This may mean $pkg does not support precompilation but is imported by a module that does.""" if JLOptions().incremental != 0 # during incremental precompilation, this should be fail-fast throw(PrecompilableError()) end end end if JLOptions().use_compiled_modules != 0 if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0) if !pkg_precompile_attempted && isinteractive() && isassigned(PKG_PRECOMPILE_HOOK) pkg_precompile_attempted = true unlock(require_lock) try @invokelatest PKG_PRECOMPILE_HOOK[](pkg.name, _from_loading = true) finally lock(require_lock) end @goto load_from_cache end # spawn off a new incremental pre-compile task for recursive `require` calls cachefile_or_module = maybe_cachefile_lock(pkg, path) do # double-check now that we have lock m = _require_search_from_serialized(pkg, path, UInt128(0)) m isa Module && return m compilecache(pkg, path) end cachefile_or_module isa Module && return cachefile_or_module::Module cachefile = cachefile_or_module if isnothing(cachefile) # maybe_cachefile_lock returns nothing if it had to wait for another process @goto load_from_cache # the new cachefile will have the newest mtime so will come first in the search elseif isa(cachefile, Exception) if precompilableerror(cachefile) verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug @logmsg verbosity "Skipping precompilation since __precompile__(false). Importing $pkg." else @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m end # fall-through to loading the file locally if not incremental else cachefile, ocachefile = cachefile::Tuple{String, Union{Nothing, String}} m = _tryrequire_from_serialized(pkg, cachefile, ocachefile) if !isa(m, Module) @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m else return m end end if JLOptions().incremental != 0 # during incremental precompilation, this should be fail-fast throw(PrecompilableError()) end end end # just load the file normally via include # for unknown dependencies uuid = pkg.uuid uuid = (uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, uuid)) old_uuid = ccall(:jl_module_uuid, NTuple{2, UInt64}, (Any,), __toplevel__) if uuid !== old_uuid ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, uuid) end unlock(require_lock) try include(__toplevel__, path) loaded = get(loaded_modules, pkg, nothing) finally lock(require_lock) if uuid !== old_uuid ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, old_uuid) end end finally toplevel_load[] = last end_loading(pkg, loaded) end return loaded end # Only used from test/precompile.jl function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Union{String, Nothing}) @lock require_lock begin set_pkgorigin_version_path(uuidkey, nothing) newm = _tryrequire_from_serialized(uuidkey, path, ocachepath) newm isa Module || throw(newm) insert_extension_triggers(uuidkey) # After successfully loading, notify downstream consumers run_package_callbacks(uuidkey) return newm end end # relative-path load """ include_string([mapexpr::Function,] m::Module, code::AbstractString, filename::AbstractString="string") Like [`include`](@ref), except reads code from the given string rather than from a file. The optional first argument `mapexpr` can be used to transform the included code before it is evaluated: for each parsed expression `expr` in `code`, the `include_string` function actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). !!! compat "Julia 1.5" Julia 1.5 is required for passing the `mapexpr` argument. """ function include_string(mapexpr::Function, mod::Module, code::AbstractString, filename::AbstractString="string") loc = LineNumberNode(1, Symbol(filename)) try ast = Meta.parseall(code, filename=filename) @assert Meta.isexpr(ast, :toplevel) result = nothing line_and_ex = Expr(:toplevel, loc, nothing) for ex in ast.args if ex isa LineNumberNode loc = ex line_and_ex.args[1] = ex continue end ex = mapexpr(ex) # Wrap things to be eval'd in a :toplevel expr to carry line # information as part of the expr. line_and_ex.args[2] = ex result = Core.eval(mod, line_and_ex) end return result catch exc # TODO: Now that stacktraces are more reliable we should remove # LoadError and expose the real error type directly. rethrow(LoadError(filename, loc.line, exc)) end end include_string(m::Module, txt::AbstractString, fname::AbstractString="string") = include_string(identity, m, txt, fname) function source_path(default::Union{AbstractString,Nothing}="") s = current_task().storage if s !== nothing s = s::IdDict{Any,Any} if haskey(s, :SOURCE_PATH) return s[:SOURCE_PATH]::Union{Nothing,String} end end return default end function source_dir() p = source_path(nothing) return p === nothing ? pwd() : dirname(p) end """ Base.include([mapexpr::Function,] m::Module, path::AbstractString) Evaluate the contents of the input source file in the global scope of module `m`. Every module (except those defined with [`baremodule`](@ref)) has its own definition of `include` omitting the `m` argument, which evaluates the file in that module. Returns the result of the last evaluated expression of the input file. During including, a task-local include path is set to the directory containing the file. Nested calls to `include` will search relative to that path. This function is typically used to load source interactively, or to combine files in packages that are broken into multiple source files. The optional first argument `mapexpr` can be used to transform the included code before it is evaluated: for each parsed expression `expr` in `path`, the `include` function actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). !!! compat "Julia 1.5" Julia 1.5 is required for passing the `mapexpr` argument. """ Base.include # defined in Base.jl # Full include() implementation which is used after bootstrap function _include(mapexpr::Function, mod::Module, _path::AbstractString) @noinline # Workaround for module availability in _simplify_include_frames path, prev = _include_dependency(mod, _path) for callback in include_callbacks # to preserve order, must come before eval in include_string invokelatest(callback, mod, path) end code = read(path, String) tls = task_local_storage() tls[:SOURCE_PATH] = path try return include_string(mapexpr, mod, code, path) finally if prev === nothing delete!(tls, :SOURCE_PATH) else tls[:SOURCE_PATH] = prev end end end """ evalfile(path::AbstractString, args::Vector{String}=String[]) Load the file into an anonymous module using [`include`](@ref), evaluate all expressions, and return the value of the last expression. The optional `args` argument can be used to set the input arguments of the script (i.e. the global `ARGS` variable). Note that definitions (e.g. methods, globals) are evaluated in the anonymous module and do not affect the current module. # Example ```jldoctest julia> write("testfile.jl", \"\"\" @show ARGS 1 + 1 \"\"\"); julia> x = evalfile("testfile.jl", ["ARG1", "ARG2"]); ARGS = ["ARG1", "ARG2"] julia> x 2 julia> rm("testfile.jl") ``` """ function evalfile(path::AbstractString, args::Vector{String}=String[]) return Core.eval(Module(:__anon__), Expr(:toplevel, :(const ARGS = $args), :(eval(x) = $(Expr(:core, :eval))(__anon__, x)), :(include(x) = $(Expr(:top, :include))(__anon__, x)), :(include(mapexpr::Function, x) = $(Expr(:top, :include))(mapexpr, __anon__, x)), :(include($path)))) end evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...]) function load_path_setup_code(load_path::Bool=true) code = """ append!(empty!(Base.DEPOT_PATH), $(repr(map(abspath, DEPOT_PATH)))) append!(empty!(Base.DL_LOAD_PATH), $(repr(map(abspath, DL_LOAD_PATH)))) """ if load_path load_path = map(abspath, Base.load_path()) path_sep = Sys.iswindows() ? ';' : ':' any(path -> path_sep in path, load_path) && error("LOAD_PATH entries cannot contain $(repr(path_sep))") code *= """ append!(empty!(Base.LOAD_PATH), $(repr(load_path))) ENV["JULIA_LOAD_PATH"] = $(repr(join(load_path, Sys.iswindows() ? ';' : ':'))) Base.set_active_project(nothing) """ end return code end # this is called in the external process that generates precompiled package files function include_package_for_output(pkg::PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::typeof(_concrete_dependencies), source::Union{Nothing,String}) append!(empty!(Base.DEPOT_PATH), depot_path) append!(empty!(Base.DL_LOAD_PATH), dl_load_path) append!(empty!(Base.LOAD_PATH), load_path) ENV["JULIA_LOAD_PATH"] = join(load_path, Sys.iswindows() ? ';' : ':') set_active_project(nothing) Base._track_dependencies[] = true get!(Base.PkgOrigin, Base.pkgorigins, pkg).path = input append!(empty!(Base._concrete_dependencies), concrete_deps) uuid_tuple = pkg.uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, pkg.uuid) ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, uuid_tuple) if source !== nothing task_local_storage()[:SOURCE_PATH] = source end ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred) Core.Compiler.track_newly_inferred.x = true try Base.include(Base.__toplevel__, input) catch ex precompilableerror(ex) || rethrow() @debug "Aborting `create_expr_cache'" exception=(ErrorException("Declaration of __precompile__(false) not allowed"), catch_backtrace()) exit(125) # we define status = 125 means PrecompileableError finally Core.Compiler.track_newly_inferred.x = false end end const PRECOMPILE_TRACE_COMPILE = Ref{String}() function create_expr_cache(pkg::PkgId, input::String, output::String, output_o::Union{Nothing, String}, concrete_deps::typeof(_concrete_dependencies), internal_stderr::IO = stderr, internal_stdout::IO = stdout) @nospecialize internal_stderr internal_stdout rm(output, force=true) # Remove file if it exists output_o === nothing || rm(output_o, force=true) depot_path = map(abspath, DEPOT_PATH) dl_load_path = map(abspath, DL_LOAD_PATH) load_path = map(abspath, Base.load_path()) path_sep = Sys.iswindows() ? ';' : ':' any(path -> path_sep in path, load_path) && error("LOAD_PATH entries cannot contain $(repr(path_sep))") deps_strs = String[] function pkg_str(_pkg::PkgId) if _pkg.uuid === nothing "Base.PkgId($(repr(_pkg.name)))" else "Base.PkgId(Base.UUID(\"$(_pkg.uuid)\"), $(repr(_pkg.name)))" end end for (pkg, build_id) in concrete_deps push!(deps_strs, "$(pkg_str(pkg)) => $(repr(build_id))") end if output_o !== nothing cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing) opt_level = Base.JLOptions().opt_level opts = `-O$(opt_level) --output-o $(output_o) --output-ji $(output) --output-incremental=yes` else cpu_target = nothing opts = `-O0 --output-ji $(output) --output-incremental=yes` end deps_eltype = sprint(show, eltype(concrete_deps); context = :module=>nothing) deps = deps_eltype * "[" * join(deps_strs, ",") * "]" trace = isassigned(PRECOMPILE_TRACE_COMPILE) ? `--trace-compile=$(PRECOMPILE_TRACE_COMPILE[])` : `` io = open(pipeline(addenv(`$(julia_cmd(;cpu_target)::Cmd) $(opts) --startup-file=no --history-file=no --warn-overwrite=yes --color=$(have_color === nothing ? "auto" : have_color ? "yes" : "no") $trace -`, "OPENBLAS_NUM_THREADS" => 1, "JULIA_NUM_THREADS" => 1), stderr = internal_stderr, stdout = internal_stdout), "w", stdout) # write data over stdin to avoid the (unlikely) case of exceeding max command line size write(io.in, """ empty!(Base.EXT_DORMITORY) # If we have a custom sysimage with `EXT_DORMITORY` prepopulated Base.precompiling_extension = $(loading_extension) Base.include_package_for_output($(pkg_str(pkg)), $(repr(abspath(input))), $(repr(depot_path)), $(repr(dl_load_path)), $(repr(load_path)), $deps, $(repr(source_path(nothing)))) """) close(io.in) return io end function compilecache_dir(pkg::PkgId) entrypath, entryfile = cache_file_entry(pkg) return joinpath(DEPOT_PATH[1], entrypath) end function compilecache_path(pkg::PkgId, prefs_hash::UInt64; project::String=something(Base.active_project(), ""))::String entrypath, entryfile = cache_file_entry(pkg) cachepath = joinpath(DEPOT_PATH[1], entrypath) isdir(cachepath) || mkpath(cachepath) if pkg.uuid === nothing abspath(cachepath, entryfile) * ".ji" else crc = _crc32c(project) crc = _crc32c(unsafe_string(JLOptions().image_file), crc) crc = _crc32c(unsafe_string(JLOptions().julia_bin), crc) crc = _crc32c(ccall(:jl_cache_flags, UInt8, ()), crc) cpu_target = get(ENV, "JULIA_CPU_TARGET", nothing) if cpu_target === nothing cpu_target = unsafe_string(JLOptions().cpu_target) end crc = _crc32c(cpu_target, crc) crc = _crc32c(prefs_hash, crc) project_precompile_slug = slug(crc, 5) abspath(cachepath, string(entryfile, "_", project_precompile_slug, ".ji")) end end """ Base.compilecache(module::PkgId) Creates a precompiled cache file for a module and all of its dependencies. This can be used to reduce package load times. Cache files are stored in `DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) for important notes. """ function compilecache(pkg::PkgId, internal_stderr::IO = stderr, internal_stdout::IO = stdout) @nospecialize internal_stderr internal_stdout path = locate_package(pkg) path === nothing && throw(ArgumentError("$pkg not found during precompilation")) return compilecache(pkg, path, internal_stderr, internal_stdout) end const MAX_NUM_PRECOMPILE_FILES = Ref(10) function compilecache(pkg::PkgId, path::String, internal_stderr::IO = stderr, internal_stdout::IO = stdout, keep_loaded_modules::Bool = true) @nospecialize internal_stderr internal_stdout # decide where to put the resulting cache file cachepath = compilecache_dir(pkg) # build up the list of modules that we want the precompile process to preserve concrete_deps = copy(_concrete_dependencies) if keep_loaded_modules for mod in loaded_modules_array() if !(mod === Main || mod === Core || mod === Base) push!(concrete_deps, PkgId(mod) => module_build_id(mod)) end end end # run the expression and cache the result verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug @logmsg verbosity "Precompiling $pkg" # create a temporary file in `cachepath` directory, write the cache in it, # write the checksum, _and then_ atomically move the file to `cachefile`. mkpath(cachepath) cache_objects = JLOptions().use_pkgimages != 0 tmppath, tmpio = mktemp(cachepath) if cache_objects tmppath_o, tmpio_o = mktemp(cachepath) tmppath_so, tmpio_so = mktemp(cachepath) else tmppath_o = nothing end local p try close(tmpio) if cache_objects close(tmpio_o) close(tmpio_so) end p = create_expr_cache(pkg, path, tmppath, tmppath_o, concrete_deps, internal_stderr, internal_stdout) if success(p) if cache_objects # Run linker over tmppath_o Linking.link_image(tmppath_o, tmppath_so) end # Read preferences hash back from .ji file (we can't precompute because # we don't actually know what the list of compile-time preferences are without compiling) prefs_hash = preferences_hash(tmppath) cachefile = compilecache_path(pkg, prefs_hash) ocachefile = cache_objects ? ocachefile_from_cachefile(cachefile) : nothing # append checksum for so to the end of the .ji file: crc_so = UInt32(0) if cache_objects crc_so = open(_crc32c, tmppath_so, "r") end # append extra crc to the end of the .ji file: open(tmppath, "r+") do f if iszero(isvalid_cache_header(f)) error("Invalid header for $pkg in new cache file $(repr(tmppath)).") end seekend(f) write(f, crc_so) seekstart(f) write(f, _crc32c(f)) end # inherit permission from the source file (and make them writable) chmod(tmppath, filemode(path) & 0o777 | 0o200) # prune the directory with cache files if pkg.uuid !== nothing entrypath, entryfile = cache_file_entry(pkg) cachefiles = filter!(x -> startswith(x, entryfile * "_") && endswith(x, ".ji"), readdir(cachepath)) if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES[] idx = findmin(mtime.(joinpath.(cachepath, cachefiles)))[2] evicted_cachefile = joinpath(cachepath, cachefiles[idx]) @debug "Evicting file from cache" evicted_cachefile rm(evicted_cachefile; force=true) try rm(ocachefile_from_cachefile(evicted_cachefile); force=true) @static if Sys.isapple() rm(ocachefile_from_cachefile(evicted_cachefile) * ".dSYM"; force=true, recursive=true) end catch e e isa IOError || rethrow() end end end if cache_objects try rename(tmppath_so, ocachefile::String; force=true) catch e e isa IOError || rethrow() isfile(ocachefile::String) || rethrow() # Windows prevents renaming a file that is in use so if there is a Julia session started # with a package image loaded, we cannot rename that file. # The code belows append a `_i` to the name of the cache file where `i` is the smallest number such that # that cache file does not exist. ocachename, ocacheext = splitext(ocachefile::String) old_cachefiles = Set(readdir(cachepath)) num = 1 while true ocachefile = ocachename * "_$num" * ocacheext in(basename(ocachefile), old_cachefiles) || break num += 1 end # TODO: Risk for a race here if some other process grabs this name before us cachefile = cachefile_from_ocachefile(ocachefile) rename(tmppath_so, ocachefile::String; force=true) end @static if Sys.isapple() run(`$(Linking.dsymutil()) $ocachefile`, Base.DevNull(), Base.DevNull(), Base.DevNull()) end end # this is atomic according to POSIX (not Win32): rename(tmppath, cachefile; force=true) return cachefile, ocachefile end finally rm(tmppath, force=true) if cache_objects rm(tmppath_o::String, force=true) rm(tmppath_so, force=true) end end if p.exitcode == 125 return PrecompilableError() else error("Failed to precompile $pkg to $(repr(tmppath)).") end end function module_build_id(m::Module) hi, lo = ccall(:jl_module_build_id, NTuple{2,UInt64}, (Any,), m) return (UInt128(hi) << 64) | lo end function isvalid_cache_header(f::IOStream) pkgimage = Ref{UInt8}() checksum = ccall(:jl_read_verify_header, UInt64, (Ptr{Cvoid}, Ptr{UInt8}, Ptr{Int64}, Ptr{Int64}), f.ios, pkgimage, Ref{Int64}(), Ref{Int64}()) # returns checksum id or zero if !iszero(checksum) && pkgimage[] != 0 @debug "Cache header was for pkgimage" return UInt64(0) # We somehow read the header for a pkgimage and not a ji end return checksum end isvalid_file_crc(f::IOStream) = (_crc32c(seekstart(f), filesize(f) - 4) == read(f, UInt32)) function isvalid_pkgimage_crc(f::IOStream, ocachefile::String) seekstart(f) # TODO necessary seek(f, filesize(f) - 8) expected_crc_so = read(f, UInt32) crc_so = open(_crc32c, ocachefile, "r") expected_crc_so == crc_so end struct CacheHeaderIncludes id::PkgId filename::String mtime::Float64 modpath::Vector{String} # seemingly not needed in Base, but used by Revise end function parse_cache_header(f::IO) flags = read(f, UInt8) modules = Vector{Pair{PkgId, UInt64}}() while true n = read(f, Int32) n == 0 && break sym = String(read(f, n)) # module name uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID build_id = read(f, UInt64) # build UUID (mostly just a timestamp) push!(modules, PkgId(uuid, sym) => build_id) end totbytes = read(f, Int64) # total bytes for file dependencies + preferences # read the list of requirements # and split the list into include and requires statements includes = CacheHeaderIncludes[] requires = Pair{PkgId, PkgId}[] while true n2 = read(f, Int32) totbytes -= 4 if n2 == 0 break end depname = String(read(f, n2)) totbytes -= n2 mtime = read(f, Float64) totbytes -= 8 n1 = read(f, Int32) totbytes -= 4 # map ids to keys modkey = (n1 == 0) ? PkgId("") : modules[n1].first modpath = String[] if n1 != 0 # determine the complete module path while true n1 = read(f, Int32) totbytes -= 4 if n1 == 0 break end push!(modpath, String(read(f, n1))) totbytes -= n1 end end if depname[1] == '\0' push!(requires, modkey => binunpack(depname)) else push!(includes, CacheHeaderIncludes(modkey, depname, mtime, modpath)) end end prefs = String[] while true n2 = read(f, Int32) totbytes -= 4 if n2 == 0 break end push!(prefs, String(read(f, n2))) totbytes -= n2 end prefs_hash = read(f, UInt64) totbytes -= 8 srctextpos = read(f, Int64) totbytes -= 8 @assert totbytes == 0 "header of cache file appears to be corrupt (totbytes == $(totbytes))" # read the list of modules that are required to be present during loading required_modules = Vector{Pair{PkgId, UInt128}}() while true n = read(f, Int32) n == 0 && break sym = String(read(f, n)) # module name uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID build_id = UInt128(read(f, UInt64)) << 64 build_id |= read(f, UInt64) push!(required_modules, PkgId(uuid, sym) => build_id) end l = read(f, Int32) clone_targets = read(f, l) return modules, (includes, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags end function parse_cache_header(cachefile::String; srcfiles_only::Bool=false) io = open(cachefile, "r") try iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) ret = parse_cache_header(io) srcfiles_only || return ret _, (includes, _), _, srctextpos, _... = ret srcfiles = srctext_files(io, srctextpos) delidx = Int[] for (i, chi) in enumerate(includes) chi.filename โˆˆ srcfiles || push!(delidx, i) end deleteat!(includes, delidx) return ret finally close(io) end end preferences_hash(f::IO) = parse_cache_header(f)[6] function preferences_hash(cachefile::String) io = open(cachefile, "r") try if iszero(isvalid_cache_header(io)) throw(ArgumentError("Invalid header in cache file $cachefile.")) end return preferences_hash(io) finally close(io) end end function cache_dependencies(f::IO) _, (includes, _), modules, _... = parse_cache_header(f) return modules, map(chi -> (chi.filename, chi.mtime), includes) # return just filename and mtime end function cache_dependencies(cachefile::String) io = open(cachefile, "r") try iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) return cache_dependencies(io) finally close(io) end end function read_dependency_src(io::IO, filename::AbstractString) srctextpos = parse_cache_header(io)[4] srctextpos == 0 && error("no source-text stored in cache file") seek(io, srctextpos) return _read_dependency_src(io, filename) end function _read_dependency_src(io::IO, filename::AbstractString) while !eof(io) filenamelen = read(io, Int32) filenamelen == 0 && break fn = String(read(io, filenamelen)) len = read(io, UInt64) if fn == filename return String(read(io, len)) end seek(io, position(io) + len) end error(filename, " is not stored in the source-text cache") end function read_dependency_src(cachefile::String, filename::AbstractString) io = open(cachefile, "r") try iszero(isvalid_cache_header(io)) && throw(ArgumentError("Invalid header in cache file $cachefile.")) return read_dependency_src(io, filename) finally close(io) end end function srctext_files(f::IO, srctextpos::Int64) files = Set{String}() srctextpos == 0 && return files seek(f, srctextpos) while !eof(f) filenamelen = read(f, Int32) filenamelen == 0 && break fn = String(read(f, filenamelen)) len = read(f, UInt64) push!(files, fn) seek(f, position(f) + len) end return files end # Test to see if this UUID is mentioned in this `Project.toml`; either as # the top-level UUID (e.g. that of the project itself), as a dependency, # or as an extra/weakdep for Preferences. function get_uuid_name(project::Dict{String, Any}, uuid::UUID) uuid_p = get(project, "uuid", nothing)::Union{Nothing, String} name = get(project, "name", nothing)::Union{Nothing, String} if name !== nothing && uuid_p !== nothing && UUID(uuid_p) == uuid return name end deps = get(project, "deps", nothing)::Union{Nothing, Dict{String, Any}} if deps !== nothing for (k, v) in deps if uuid == UUID(v::String) return k end end end for subkey in ("deps", "extras", "weakdeps") subsection = get(project, subkey, nothing)::Union{Nothing, Dict{String, Any}} if subsection !== nothing for (k, v) in subsection if uuid == UUID(v::String) return k end end end end return nothing end function get_uuid_name(project_toml::String, uuid::UUID) project = parsed_toml(project_toml) return get_uuid_name(project, uuid) end # If we've asked for a specific UUID, this function will extract the prefs # for that particular UUID. Otherwise, it returns all preferences. function filter_preferences(prefs::Dict{String, Any}, pkg_name) if pkg_name === nothing return prefs else return get(Dict{String, Any}, prefs, pkg_name)::Dict{String, Any} end end function collect_preferences(project_toml::String, uuid::Union{UUID,Nothing}) # We'll return a list of dicts to be merged dicts = Dict{String, Any}[] project = parsed_toml(project_toml) pkg_name = nothing if uuid !== nothing # If we've been given a UUID, map that to the name of the package as # recorded in the preferences section. If we can't find that mapping, # exit out, as it means there's no way preferences can be set for that # UUID, as we only allow actual dependencies to have preferences set. pkg_name = get_uuid_name(project, uuid) if pkg_name === nothing return dicts end end # Look first inside of `Project.toml` to see we have preferences embedded within there proj_preferences = get(Dict{String, Any}, project, "preferences")::Dict{String, Any} push!(dicts, filter_preferences(proj_preferences, pkg_name)) # Next, look for `(Julia)LocalPreferences.toml` files next to this `Project.toml` project_dir = dirname(project_toml) for name in preferences_names toml_path = joinpath(project_dir, name) if isfile(toml_path) prefs = parsed_toml(toml_path) push!(dicts, filter_preferences(prefs, pkg_name)) # If we find `JuliaLocalPreferences.toml`, don't look for `LocalPreferences.toml` break end end return dicts end """ recursive_prefs_merge(base::Dict, overrides::Dict...) Helper function to merge preference dicts recursively, honoring overrides in nested dictionaries properly. """ function recursive_prefs_merge(base::Dict{String, Any}, overrides::Dict{String, Any}...) new_base = Base._typeddict(base, overrides...) for override in overrides # Clear entries are keys that should be deleted from any previous setting. override_clear = get(override, "__clear__", nothing) if override_clear isa Vector{String} for k in override_clear delete!(new_base, k) end end for (k, override_k) in override # Note that if `base` has a mapping that is _not_ a `Dict`, and `override` new_base_k = get(new_base, k, nothing) if new_base_k isa Dict{String, Any} && override_k isa Dict{String, Any} new_base[k] = recursive_prefs_merge(new_base_k, override_k) else new_base[k] = override_k end end end return new_base end function get_preferences(uuid::Union{UUID,Nothing} = nothing) merged_prefs = Dict{String,Any}() for env in reverse(load_path()) project_toml = env_project_file(env) if !isa(project_toml, String) continue end # Collect all dictionaries from the current point in the load path, then merge them in dicts = collect_preferences(project_toml, uuid) merged_prefs = recursive_prefs_merge(merged_prefs, dicts...) end return merged_prefs end function get_preferences_hash(uuid::Union{UUID, Nothing}, prefs_list::Vector{String}) # Start from a predictable hash point to ensure that the same preferences always # hash to the same value, modulo changes in how Dictionaries are hashed. h = UInt(0) uuid === nothing && return UInt64(h) # Load the preferences prefs = get_preferences(uuid) # Walk through each name that's called out as a compile-time preference for name in prefs_list prefs_value = get(prefs, name, nothing) if prefs_value !== nothing h = hash(prefs_value, h)::UInt end end # We always return a `UInt64` so that our serialization format is stable return UInt64(h) end get_preferences_hash(m::Module, prefs_list::Vector{String}) = get_preferences_hash(PkgId(m).uuid, prefs_list) # This is how we keep track of who is using what preferences at compile-time const COMPILETIME_PREFERENCES = Dict{UUID,Set{String}}() # In `Preferences.jl`, if someone calls `load_preference(@__MODULE__, key)` while we're precompiling, # we mark that usage as a usage at compile-time and call this method, so that at the end of `.ji` generation, # we can record the list of compile-time preferences and embed that into the `.ji` header function record_compiletime_preference(uuid::UUID, key::String) pref = get!(Set{String}, COMPILETIME_PREFERENCES, uuid) push!(pref, key) return nothing end get_compiletime_preferences(uuid::UUID) = collect(get(Vector{String}, COMPILETIME_PREFERENCES, uuid)) get_compiletime_preferences(m::Module) = get_compiletime_preferences(PkgId(m).uuid) get_compiletime_preferences(::Nothing) = String[] function check_clone_targets(clone_targets) rejection_reason = ccall(:jl_check_pkgimage_clones, Any, (Ptr{Cchar},), clone_targets) if rejection_reason !== nothing return rejection_reason end end struct CacheFlags # OOICCDDP - see jl_cache_flags use_pkgimages::Bool debug_level::Int check_bounds::Int inline::Bool opt_level::Int function CacheFlags(f::UInt8) use_pkgimages = Bool(f & 1) debug_level = Int((f >> 1) & 3) check_bounds = Int((f >> 3) & 3) inline = Bool((f >> 5) & 1) opt_level = Int((f >> 6) & 3) # define OPT_LEVEL in statiddata_utils new(use_pkgimages, debug_level, check_bounds, inline, opt_level) end end CacheFlags(f::Int) = CacheFlags(UInt8(f)) CacheFlags() = CacheFlags(ccall(:jl_cache_flags, UInt8, ())) function show(io::IO, cf::CacheFlags) print(io, "use_pkgimages = ", cf.use_pkgimages) print(io, ", debug_level = ", cf.debug_level) print(io, ", check_bounds = ", cf.check_bounds) print(io, ", inline = ", cf.inline) print(io, ", opt_level = ", cf.opt_level) end struct ImageTarget name::String flags::Int32 ext_features::String features_en::Vector{UInt8} features_dis::Vector{UInt8} end function parse_image_target(io::IO) flags = read(io, Int32) nfeature = read(io, Int32) feature_en = read(io, 4*nfeature) feature_dis = read(io, 4*nfeature) name_len = read(io, Int32) name = String(read(io, name_len)) ext_features_len = read(io, Int32) ext_features = String(read(io, ext_features_len)) ImageTarget(name, flags, ext_features, feature_en, feature_dis) end function parse_image_targets(targets::Vector{UInt8}) io = IOBuffer(targets) ntargets = read(io, Int32) targets = Vector{ImageTarget}(undef, ntargets) for i in 1:ntargets targets[i] = parse_image_target(io) end return targets end function current_image_targets() targets = @ccall jl_reflect_clone_targets()::Vector{UInt8} return parse_image_targets(targets) end struct FeatureName name::Cstring bit::UInt32 # bit index into a `uint32_t` array; llvmver::UInt32 # 0 if it is available on the oldest LLVM version we support end function feature_names() fnames = Ref{Ptr{FeatureName}}() nf = Ref{Csize_t}() @ccall jl_reflect_feature_names(fnames::Ptr{Ptr{FeatureName}}, nf::Ptr{Csize_t})::Cvoid if fnames[] == C_NULL @assert nf[] == 0 return Vector{FeatureName}(undef, 0) end Base.unsafe_wrap(Array, fnames[], nf[], own=false) end function test_feature(features::Vector{UInt8}, feat::FeatureName) bitidx = feat.bit u8idx = div(bitidx, 8) + 1 bit = bitidx % 8 return (features[u8idx] & (1 << bit)) != 0 end function show(io::IO, it::ImageTarget) print(io, it.name) if !isempty(it.ext_features) print(io, ",", it.ext_features) end print(io, "; flags=", it.flags) print(io, "; features_en=(") first = true for feat in feature_names() if test_feature(it.features_en, feat) name = Base.unsafe_string(feat.name) if first first = false print(io, name) else print(io, ", ", name) end end end print(io, ")") # Is feature_dis useful? end # Set by FileWatching.__init__() global mkpidlock_hook global trymkpidlock_hook global parse_pidfile_hook # The preferences hash is only known after precompilation so just assume no preferences. # Also ignore the active project, which means that if all other conditions are equal, # the same package cannot be precompiled from different projects and/or different preferences at the same time. compilecache_pidfile_path(pkg::PkgId) = compilecache_path(pkg, UInt64(0); project="") * ".pidfile" const compilecache_pidlock_stale_age = 10 # Allows processes to wait if another process is precompiling a given source already. # The lock file mtime will be updated when held at most every `stale_age/2` seconds, with expected # variance of 10 seconds or more being infrequent but not unusual. # After `stale_age` seconds beyond the mtime of the lock file, the lock file is deleted and # precompilation will proceed if the locking process no longer exists or after `stale_age * 5` # seconds if the process does still exist. # If the lock is held by another host, it will conservatively wait `stale_age * 5` # seconds since processes cannot be checked remotely function maybe_cachefile_lock(f, pkg::PkgId, srcpath::String; stale_age=compilecache_pidlock_stale_age) if @isdefined(mkpidlock_hook) && @isdefined(trymkpidlock_hook) && @isdefined(parse_pidfile_hook) pidfile = compilecache_pidfile_path(pkg) cachefile = invokelatest(trymkpidlock_hook, f, pidfile; stale_age) if cachefile === false pid, hostname, age = invokelatest(parse_pidfile_hook, pidfile) verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug if isempty(hostname) || hostname == gethostname() @logmsg verbosity "Waiting for another process (pid: $pid) to finish precompiling $pkg. Pidfile: $pidfile" else @logmsg verbosity "Waiting for another machine (hostname: $hostname, pid: $pid) to finish precompiling $pkg. Pidfile: $pidfile" end # wait until the lock is available, but don't actually acquire it # returning nothing indicates a process waited for another return invokelatest(mkpidlock_hook, Returns(nothing), pidfile; stale_age) end return cachefile else # for packages loaded before FileWatching.__init__() f() end end # returns true if it "cachefile.ji" is stale relative to "modpath.jl" and build_id for modkey # otherwise returns the list of dependencies to also check @constprop :none function stale_cachefile(modpath::String, cachefile::String; ignore_loaded::Bool = false) return stale_cachefile(PkgId(""), UInt128(0), modpath, cachefile; ignore_loaded) end @constprop :none function stale_cachefile(modkey::PkgId, build_id::UInt128, modpath::String, cachefile::String; ignore_loaded::Bool = false) io = open(cachefile, "r") try checksum = isvalid_cache_header(io) if iszero(checksum) @debug "Rejecting cache file $cachefile due to it containing an invalid cache header" return true # invalid cache file end modules, (includes, requires), required_modules, srctextpos, prefs, prefs_hash, clone_targets, flags = parse_cache_header(io) if isempty(modules) return true # ignore empty file end if ccall(:jl_match_cache_flags, UInt8, (UInt8,), flags) == 0 @debug """ Rejecting cache file $cachefile for $modkey since the flags are mismatched current session: $(CacheFlags()) cache file: $(CacheFlags(flags)) """ return true end pkgimage = !isempty(clone_targets) if pkgimage ocachefile = ocachefile_from_cachefile(cachefile) if JLOptions().use_pkgimages == 0 # presence of clone_targets means native code cache @debug "Rejecting cache file $cachefile for $modkey since it would require usage of pkgimage" return true end rejection_reasons = check_clone_targets(clone_targets) if !isnothing(rejection_reasons) @debug("Rejecting cache file $cachefile for $modkey:", Reasons=rejection_reasons, var"Image Targets"=parse_image_targets(clone_targets), var"Current Targets"=current_image_targets()) return true end if !isfile(ocachefile) @debug "Rejecting cache file $cachefile for $modkey since pkgimage $ocachefile was not found" return true end else ocachefile = nothing end id = first(modules) if id.first != modkey && modkey != PkgId("") @debug "Rejecting cache file $cachefile for $modkey since it is for $id instead" return true end if build_id != UInt128(0) id_build = (UInt128(checksum) << 64) | id.second if id_build != build_id @debug "Ignoring cache file $cachefile for $modkey ($((UUID(id_build)))) since it is does not provide desired build_id ($((UUID(build_id))))" return true end end id = id.first modules = Dict{PkgId, UInt64}(modules) # Check if transitive dependencies can be fulfilled ndeps = length(required_modules) depmods = Vector{Any}(undef, ndeps) for i in 1:ndeps req_key, req_build_id = required_modules[i] # Module is already loaded if root_module_exists(req_key) M = root_module(req_key) if PkgId(M) == req_key && module_build_id(M) === req_build_id depmods[i] = M elseif ignore_loaded # Used by Pkg.precompile given that there it's ok to precompile different versions of loaded packages @goto locate_branch else @debug "Rejecting cache file $cachefile because module $req_key is already loaded and incompatible." return true # Won't be able to fulfill dependency end else @label locate_branch path = locate_package(req_key) if path === nothing @debug "Rejecting cache file $cachefile because dependency $req_key not found." return true # Won't be able to fulfill dependency end depmods[i] = (path, req_key, req_build_id) end end # check if this file is going to provide one of our concrete dependencies # or if it provides a version that conflicts with our concrete dependencies # or neither skip_timecheck = false for (req_key, req_build_id) in _concrete_dependencies build_id = get(modules, req_key, UInt64(0)) if build_id !== UInt64(0) build_id |= UInt128(checksum) << 64 if build_id === req_build_id skip_timecheck = true break end @debug "Rejecting cache file $cachefile because it provides the wrong build_id (got $((UUID(build_id)))) for $req_key (want $(UUID(req_build_id)))" return true # cachefile doesn't provide the required version of the dependency end end # now check if this file is fresh relative to its source files if !skip_timecheck if !samefile(includes[1].filename, modpath) && !samefile(fixup_stdlib_path(includes[1].filename), modpath) @debug "Rejecting cache file $cachefile because it is for file $(includes[1].filename) not file $modpath" return true # cache file was compiled from a different path end for (modkey, req_modkey) in requires # verify that `require(modkey, name(req_modkey))` ==> `req_modkey` if identify_package(modkey, req_modkey.name) != req_modkey @debug "Rejecting cache file $cachefile because uuid mapping for $modkey => $req_modkey has changed" return true end end for chi in includes f, ftime_req = chi.filename, chi.mtime if !ispath(f) _f = fixup_stdlib_path(f) if isfile(_f) && startswith(_f, Sys.STDLIB) # mtime is changed by extraction @debug "Skipping mtime check for file $f used by $cachefile, since it is a stdlib" continue end @debug "Rejecting stale cache file $cachefile because file $f does not exist" return true end ftime = mtime(f) is_stale = ( ftime != ftime_req ) && ( ftime != floor(ftime_req) ) && # Issue #13606, PR #13613: compensate for Docker images rounding mtimes ( ftime != ceil(ftime_req) ) && # PR: #47433 Compensate for CirceCI's truncating of timestamps in its caching ( ftime != trunc(ftime_req, digits=6) ) && # Issue #20837, PR #20840: compensate for GlusterFS truncating mtimes to microseconds ( ftime != 1.0 ) && # PR #43090: provide compatibility with Nix mtime. !( 0 < (ftime_req - ftime) < 1e-6 ) # PR #45552: Compensate for Windows tar giving mtimes that may be incorrect by up to one microsecond if is_stale @debug "Rejecting stale cache file $cachefile (mtime $ftime_req) because file $f (mtime $ftime) has changed" return true end end end if !isvalid_file_crc(io) @debug "Rejecting cache file $cachefile because it has an invalid checksum" return true end if pkgimage if !isvalid_pkgimage_crc(io, ocachefile::String) @debug "Rejecting cache file $cachefile because $ocachefile has an invalid checksum" return true end end curr_prefs_hash = get_preferences_hash(id.uuid, prefs) if prefs_hash != curr_prefs_hash @debug "Rejecting cache file $cachefile because preferences hash does not match 0x$(string(prefs_hash, base=16)) != 0x$(string(curr_prefs_hash, base=16))" return true end return depmods, ocachefile # fresh cachefile finally close(io) end end """ @__FILE__ -> String Expand to a string with the path to the file containing the macrocall, or an empty string if evaluated by `julia -e `. Return `nothing` if the macro was missing parser source information. Alternatively see [`PROGRAM_FILE`](@ref). """ macro __FILE__() __source__.file === nothing && return nothing return String(__source__.file::Symbol) end """ @__DIR__ -> String Expand to a string with the absolute path to the directory of the file containing the macrocall. Return the current working directory if run from a REPL or if evaluated by `julia -e `. """ macro __DIR__() __source__.file === nothing && return nothing _dirname = dirname(String(__source__.file::Symbol)) return isempty(_dirname) ? pwd() : abspath(_dirname) end """ precompile(f, argtypes::Tuple{Vararg{Any}}) Compile the given function `f` for the argument tuple (of types) `argtypes`, but do not execute it. """ function precompile(@nospecialize(f), @nospecialize(argtypes::Tuple)) precompile(Tuple{Core.Typeof(f), argtypes...}) end const ENABLE_PRECOMPILE_WARNINGS = Ref(false) function precompile(@nospecialize(argt::Type)) ret = ccall(:jl_compile_hint, Int32, (Any,), argt) != 0 if !ret && ENABLE_PRECOMPILE_WARNINGS[] @warn "Inactive precompile statement" maxlog=100 form=argt _module=nothing _file=nothing _line=0 end return ret end # Variants that work for `invoke`d calls for which the signature may not be sufficient precompile(mi::Core.MethodInstance, world::UInt=get_world_counter()) = (ccall(:jl_compile_method_instance, Cvoid, (Any, Any, UInt), mi, C_NULL, world); return true) """ precompile(f, argtypes::Tuple{Vararg{Any}}, m::Method) Precompile a specific method for the given argument types. This may be used to precompile a different method than the one that would ordinarily be chosen by dispatch, thus mimicking `invoke`. """ function precompile(@nospecialize(f), @nospecialize(argtypes::Tuple), m::Method) precompile(Tuple{Core.Typeof(f), argtypes...}, m) end function precompile(@nospecialize(argt::Type), m::Method) atype, sparams = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argt, m.sig)::SimpleVector mi = Core.Compiler.specialize_method(m, atype, sparams) return precompile(mi) end precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), Nothing)) precompile(include_package_for_output, (PkgId, String, Vector{String}, Vector{String}, Vector{String}, typeof(_concrete_dependencies), String)) precompile(create_expr_cache, (PkgId, String, String, String, typeof(_concrete_dependencies), IO, IO)) precompile(create_expr_cache, (PkgId, String, String, Nothing, typeof(_concrete_dependencies), IO, IO)) M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/timing.jlั?# This file is a part of Julia. License is MIT: https://julialang.org/license # This type must be kept in sync with the C struct in src/gc.h struct GC_Num allocd ::Int64 # GC internal deferred_alloc ::Int64 # GC internal freed ::Int64 # GC internal malloc ::Int64 realloc ::Int64 poolalloc ::Int64 bigalloc ::Int64 freecall ::Int64 total_time ::Int64 total_allocd ::Int64 # GC internal collect ::Csize_t # GC internal pause ::Cint full_sweep ::Cint max_pause ::Int64 max_memory ::Int64 time_to_safepoint ::Int64 max_time_to_safepoint ::Int64 total_time_to_safepoint ::Int64 sweep_time ::Int64 mark_time ::Int64 total_sweep_time ::Int64 total_mark_time ::Int64 last_full_sweep ::Int64 end gc_num() = ccall(:jl_gc_num, GC_Num, ()) reset_gc_stats() = ccall(:jl_gc_reset_stats, Cvoid, ()) # This type is to represent differences in the counters, so fields may be negative struct GC_Diff allocd ::Int64 # Bytes allocated malloc ::Int64 # Number of GC aware malloc() realloc ::Int64 # Number of GC aware realloc() poolalloc ::Int64 # Number of pool allocation bigalloc ::Int64 # Number of big (non-pool) allocation freecall ::Int64 # Number of GC aware free() total_time ::Int64 # Time spent in garbage collection pause ::Int64 # Number of GC pauses full_sweep ::Int64 # Number of GC full collection end gc_total_bytes(gc_num::GC_Num) = gc_num.allocd + gc_num.deferred_alloc + gc_num.total_allocd function GC_Diff(new::GC_Num, old::GC_Num) # logic from `src/gc.c:jl_gc_total_bytes` old_allocd = gc_total_bytes(old) new_allocd = gc_total_bytes(new) return GC_Diff(new_allocd - old_allocd, new.malloc - old.malloc, new.realloc - old.realloc, new.poolalloc - old.poolalloc, new.bigalloc - old.bigalloc, new.freecall - old.freecall, new.total_time - old.total_time, new.pause - old.pause, new.full_sweep - old.full_sweep) end function gc_alloc_count(diff::GC_Diff) diff.malloc + diff.realloc + diff.poolalloc + diff.bigalloc end # cumulative total time spent on compilation and recompilation, in nanoseconds function cumulative_compile_time_ns() comp = ccall(:jl_cumulative_compile_time_ns, UInt64, ()) recomp = ccall(:jl_cumulative_recompile_time_ns, UInt64, ()) return comp, recomp end function cumulative_compile_timing(b::Bool) if b ccall(:jl_cumulative_compile_timing_enable, Cvoid, ()) else ccall(:jl_cumulative_compile_timing_disable, Cvoid, ()) end return end # total time spend in garbage collection, in nanoseconds gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ()) """ Base.gc_live_bytes() Return the total size (in bytes) of objects currently in memory. This is computed as the total size of live objects after the last garbage collection, plus the number of bytes allocated since then. """ function gc_live_bytes() num = gc_num() Int(ccall(:jl_gc_live_bytes, Int64, ())) + num.allocd + num.deferred_alloc end """ Base.jit_total_bytes() Return the total amount (in bytes) allocated by the just-in-time compiler for e.g. native code and data. """ function jit_total_bytes() return ccall(:jl_jit_total_bytes, Csize_t, ()) end # print elapsed time, return expression value const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"] const _cnt_units = ["", " k", " M", " G", " T", " P"] function prettyprint_getunits(value, numunits, factor) if value == 0 || value == 1 return (value, 1) end unit = ceil(Int, log(value) / log(factor)) unit = min(numunits, unit) number = value/factor^(unit-1) return number, unit end function padded_nonzero_print(value, str, always_print = true) if always_print || value != 0 blanks = " "[1:(19 - length(str))] println(str, ":", blanks, value) end end function format_bytes(bytes) # also used by InteractiveUtils bytes, mb = prettyprint_getunits(bytes, length(_mem_units), Int64(1024)) if mb == 1 return string(Int(bytes), " ", _mem_units[mb], bytes==1 ? "" : "s") else return string(Ryu.writefixed(Float64(bytes), 3), " ", _mem_units[mb]) end end function time_print(io::IO, elapsedtime, bytes=0, gctime=0, allocs=0, compile_time=0, recompile_time=0, newline=false; msg::Union{String,Nothing}=nothing) timestr = Ryu.writefixed(Float64(elapsedtime/1e9), 6) str = sprint() do io if msg isa String print(io, msg, ": ") else print(io, length(timestr) < 10 ? (" "^(10 - length(timestr))) : "") end print(io, timestr, " seconds") parens = bytes != 0 || allocs != 0 || gctime > 0 || compile_time > 0 parens && print(io, " (") if bytes != 0 || allocs != 0 allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) if ma == 1 print(io, Int(allocs), _cnt_units[ma], allocs==1 ? " allocation: " : " allocations: ") else print(io, Ryu.writefixed(Float64(allocs), 2), _cnt_units[ma], " allocations: ") end print(io, format_bytes(bytes)) end if gctime > 0 if bytes != 0 || allocs != 0 print(io, ", ") end print(io, Ryu.writefixed(Float64(100*gctime/elapsedtime), 2), "% gc time") end if compile_time > 0 if bytes != 0 || allocs != 0 || gctime > 0 print(io, ", ") end print(io, Ryu.writefixed(Float64(100*compile_time/elapsedtime), 2), "% compilation time") end if recompile_time > 0 perc = Float64(100 * recompile_time / compile_time) # use "<1" to avoid the confusing UX of reporting 0% when it's >0% print(io, ": ", perc < 1 ? "<1" : Ryu.writefixed(perc, 0), "% of which was recompilation") end parens && print(io, ")") newline && print(io, "\n") end print(io, str) nothing end function timev_print(elapsedtime, diff::GC_Diff, compile_times; msg::Union{String,Nothing}=nothing) allocs = gc_alloc_count(diff) compile_time = first(compile_times) recompile_time = last(compile_times) time_print(stdout, elapsedtime, diff.allocd, diff.total_time, allocs, compile_time, recompile_time, true; msg) padded_nonzero_print(elapsedtime, "elapsed time (ns)") padded_nonzero_print(diff.total_time, "gc time (ns)") padded_nonzero_print(diff.allocd, "bytes allocated") padded_nonzero_print(diff.poolalloc, "pool allocs") padded_nonzero_print(diff.bigalloc, "non-pool GC allocs") padded_nonzero_print(diff.malloc, "malloc() calls", false) padded_nonzero_print(diff.realloc, "realloc() calls", false) # always print number of frees if there are mallocs padded_nonzero_print(diff.freecall, "free() calls", diff.malloc > 0) minor_collects = diff.pause - diff.full_sweep padded_nonzero_print(minor_collects, "minor collections") padded_nonzero_print(diff.full_sweep, "full collections") end # Like a try-finally block, except without introducing the try scope # NOTE: This is deprecated and should not be used from user logic. A proper solution to # this problem will be introduced in https://github.com/JuliaLang/julia/pull/39217 macro __tryfinally(ex, fin) Expr(:tryfinally, :($(esc(ex))), :($(esc(fin))) ) end """ @time expr @time "description" expr A macro to execute an expression, printing the time it took to execute, the number of allocations, and the total number of bytes its execution caused to be allocated, before returning the value of the expression. Any time spent garbage collecting (gc), compiling new code, or recompiling invalidated code is shown as a percentage. Optionally provide a description string to print before the time report. In some cases the system will look inside the `@time` expression and compile some of the called code before execution of the top-level expression begins. When that happens, some compilation time will not be counted. To include this time you can run `@time @eval ...`. See also [`@showtime`](@ref), [`@timev`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), [`@allocated`](@ref), and [`@allocations`](@ref). !!! note For more serious benchmarking, consider the `@btime` macro from the BenchmarkTools.jl package which among other things evaluates the function multiple times in order to reduce noise. !!! compat "Julia 1.8" The option to add a description was introduced in Julia 1.8. Recompilation time being shown separately from compilation time was introduced in Julia 1.8 ```julia-repl julia> x = rand(10,10); julia> @time x * x; 0.606588 seconds (2.19 M allocations: 116.555 MiB, 3.75% gc time, 99.94% compilation time) julia> @time x * x; 0.000009 seconds (1 allocation: 896 bytes) julia> @time begin sleep(0.3) 1+1 end 0.301395 seconds (8 allocations: 336 bytes) 2 julia> @time "A one second sleep" sleep(1) A one second sleep: 1.005750 seconds (5 allocations: 144 bytes) julia> for loop in 1:3 @time loop sleep(1) end 1: 1.006760 seconds (5 allocations: 144 bytes) 2: 1.001263 seconds (5 allocations: 144 bytes) 3: 1.003676 seconds (5 allocations: 144 bytes) ``` """ macro time(ex) quote @time nothing $(esc(ex)) end end macro time(msg, ex) quote Experimental.@force_compile local stats = gc_num() local elapsedtime = time_ns() cumulative_compile_timing(true) local compile_elapsedtimes = cumulative_compile_time_ns() local val = @__tryfinally($(esc(ex)), (elapsedtime = time_ns() - elapsedtime; cumulative_compile_timing(false); compile_elapsedtimes = cumulative_compile_time_ns() .- compile_elapsedtimes) ) local diff = GC_Diff(gc_num(), stats) local _msg = $(esc(msg)) time_print(stdout, elapsedtime, diff.allocd, diff.total_time, gc_alloc_count(diff), first(compile_elapsedtimes), last(compile_elapsedtimes), true; msg=_msg) val end end """ @showtime expr Like `@time` but also prints the expression being evaluated for reference. !!! compat "Julia 1.8" This macro was added in Julia 1.8. See also [`@time`](@ref). ```julia-repl julia> @showtime sleep(1) sleep(1): 1.002164 seconds (4 allocations: 128 bytes) ``` """ macro showtime(ex) quote @time $(sprint(show_unquoted,ex)) $(esc(ex)) end end """ @timev expr @timev "description" expr This is a verbose version of the `@time` macro. It first prints the same information as `@time`, then any non-zero memory allocation counters, and then returns the value of the expression. Optionally provide a description string to print before the time report. !!! compat "Julia 1.8" The option to add a description was introduced in Julia 1.8. See also [`@time`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), [`@allocated`](@ref), and [`@allocations`](@ref). ```julia-repl julia> x = rand(10,10); julia> @timev x * x; 0.546770 seconds (2.20 M allocations: 116.632 MiB, 4.23% gc time, 99.94% compilation time) elapsed time (ns): 546769547 gc time (ns): 23115606 bytes allocated: 122297811 pool allocs: 2197930 non-pool GC allocs:1327 malloc() calls: 36 realloc() calls: 5 GC pauses: 3 julia> @timev x * x; 0.000010 seconds (1 allocation: 896 bytes) elapsed time (ns): 9848 bytes allocated: 896 pool allocs: 1 ``` """ macro timev(ex) quote @timev nothing $(esc(ex)) end end macro timev(msg, ex) quote Experimental.@force_compile local stats = gc_num() local elapsedtime = time_ns() cumulative_compile_timing(true) local compile_elapsedtimes = cumulative_compile_time_ns() local val = @__tryfinally($(esc(ex)), (elapsedtime = time_ns() - elapsedtime; cumulative_compile_timing(false); compile_elapsedtimes = cumulative_compile_time_ns() .- compile_elapsedtimes) ) local diff = GC_Diff(gc_num(), stats) local _msg = $(esc(msg)) timev_print(elapsedtime, diff, compile_elapsedtimes; msg=_msg) val end end """ @elapsed A macro to evaluate an expression, discarding the resulting value, instead returning the number of seconds it took to execute as a floating-point number. In some cases the system will look inside the `@elapsed` expression and compile some of the called code before execution of the top-level expression begins. When that happens, some compilation time will not be counted. To include this time you can run `@elapsed @eval ...`. See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), [`@allocated`](@ref), and [`@allocations`](@ref). ```julia-repl julia> @elapsed sleep(0.3) 0.301391426 ``` """ macro elapsed(ex) quote Experimental.@force_compile local t0 = time_ns() $(esc(ex)) (time_ns() - t0) / 1e9 end end # total number of bytes allocated so far gc_bytes(b::Ref{Int64}) = ccall(:jl_gc_get_total_bytes, Cvoid, (Ptr{Int64},), b) # NOTE: gc_bytes() is deprecated function gc_bytes() b = Ref{Int64}() gc_bytes(b) b[] end """ @allocated A macro to evaluate an expression, discarding the resulting value, instead returning the total number of bytes allocated during evaluation of the expression. See also [`@allocations`](@ref), [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), and [`@elapsed`](@ref). ```julia-repl julia> @allocated rand(10^6) 8000080 ``` """ macro allocated(ex) quote Experimental.@force_compile local b0 = Ref{Int64}(0) local b1 = Ref{Int64}(0) gc_bytes(b0) $(esc(ex)) gc_bytes(b1) b1[] - b0[] end end """ @allocations A macro to evaluate an expression, discard the resulting value, and instead return the total number of allocations during evaluation of the expression. See also [`@allocated`](@ref), [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), and [`@elapsed`](@ref). ```julia-repl julia> @allocations rand(10^6) 2 ``` !!! compat "Julia 1.9" This macro was added in Julia 1.9. """ macro allocations(ex) quote Experimental.@force_compile local stats = Base.gc_num() $(esc(ex)) local diff = Base.GC_Diff(Base.gc_num(), stats) Base.gc_alloc_count(diff) end end """ @timed A macro to execute an expression, and return the value of the expression, elapsed time, total bytes allocated, garbage collection time, and an object with various memory allocation counters. In some cases the system will look inside the `@timed` expression and compile some of the called code before execution of the top-level expression begins. When that happens, some compilation time will not be counted. To include this time you can run `@timed @eval ...`. See also [`@time`](@ref), [`@timev`](@ref), [`@elapsed`](@ref), [`@allocated`](@ref), and [`@allocations`](@ref). ```julia-repl julia> stats = @timed rand(10^6); julia> stats.time 0.006634834 julia> stats.bytes 8000256 julia> stats.gctime 0.0055765 julia> propertynames(stats.gcstats) (:allocd, :malloc, :realloc, :poolalloc, :bigalloc, :freecall, :total_time, :pause, :full_sweep) julia> stats.gcstats.total_time 5576500 ``` !!! compat "Julia 1.5" The return type of this macro was changed from `Tuple` to `NamedTuple` in Julia 1.5. """ macro timed(ex) quote Experimental.@force_compile local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) elapsedtime = time_ns() - elapsedtime local diff = GC_Diff(gc_num(), stats) (value=val, time=elapsedtime/1e9, bytes=diff.allocd, gctime=diff.total_time/1e9, gcstats=diff) end end K/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/util.jl2q# This file is a part of Julia. License is MIT: https://julialang.org/license ## printing with color ## const text_colors = Dict{Union{Symbol,Int},String}( :black => "\033[30m", :red => "\033[31m", :green => "\033[32m", :yellow => "\033[33m", :blue => "\033[34m", :magenta => "\033[35m", :cyan => "\033[36m", :white => "\033[37m", :light_black => "\033[90m", # gray :light_red => "\033[91m", :light_green => "\033[92m", :light_yellow => "\033[93m", :light_blue => "\033[94m", :light_magenta => "\033[95m", :light_cyan => "\033[96m", :light_white => "\033[97m", :normal => "\033[0m", :default => "\033[39m", :bold => "\033[1m", :italic => "\033[3m", :underline => "\033[4m", :blink => "\033[5m", :reverse => "\033[7m", :hidden => "\033[8m", :nothing => "", ) for i in 0:255 text_colors[i] = "\033[38;5;$(i)m" end const disable_text_style = Dict{Symbol,String}( :bold => "\033[22m", :italic => "\033[23m", :underline => "\033[24m", :blink => "\033[25m", :reverse => "\033[27m", :hidden => "\033[28m", :normal => "", :default => "", :nothing => "", ) # Create a docstring with an automatically generated list # of colors. let color_syms = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors))), formatting_syms = [:normal, :bold, :italic, :default] global const available_text_colors = cat( sort!(intersect(color_syms, formatting_syms), rev=true), sort!(setdiff( color_syms, formatting_syms)); dims=1) end const available_text_colors_docstring = string(join([string("`:", key,"`") for key in available_text_colors], ",\n", ", or \n")) """Dictionary of color codes for the terminal. Available colors are: $available_text_colors_docstring as well as the integers 0 to 255 inclusive. The color `:default` will print text in the default color while the color `:normal` will print text with all text properties (like boldness) reset. Printing with the color `:nothing` will print the string without modifications. """ text_colors function with_output_color(@nospecialize(f::Function), color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false, italic::Bool = false, underline::Bool = false, blink::Bool = false, reverse::Bool = false, hidden::Bool = false) buf = IOBuffer() iscolor = get(io, :color, false)::Bool try f(IOContext(buf, io), args...) finally str = String(take!(buf)) if !iscolor print(io, str) else bold && color === :bold && (color = :nothing) italic && color === :italic && (color = :nothing) underline && color === :underline && (color = :nothing) blink && color === :blink && (color = :nothing) reverse && color === :reverse && (color = :nothing) hidden && color === :hidden && (color = :nothing) enable_ansi = get(text_colors, color, text_colors[:default]) * (bold ? text_colors[:bold] : "") * (italic ? text_colors[:italic] : "") * (underline ? text_colors[:underline] : "") * (blink ? text_colors[:blink] : "") * (reverse ? text_colors[:reverse] : "") * (hidden ? text_colors[:hidden] : "") disable_ansi = (hidden ? disable_text_style[:hidden] : "") * (reverse ? disable_text_style[:reverse] : "") * (blink ? disable_text_style[:blink] : "") * (underline ? disable_text_style[:underline] : "") * (bold ? disable_text_style[:bold] : "") * (italic ? disable_text_style[:italic] : "") * get(disable_text_style, color, text_colors[:default]) first = true for line in eachsplit(str, '\n') first || print(buf, '\n') first = false isempty(line) && continue print(buf, enable_ansi, line, disable_ansi) end print(io, String(take!(buf))) end end end """ printstyled([io], xs...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Symbol,Int}=:normal) Print `xs` in a color specified as a symbol or integer, optionally in bold. Keyword `color` may take any of the values $(Base.available_text_colors_docstring) or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. Keywords `bold=true`, `italic=true`, `underline=true`, `blink=true` are self-explanatory. Keyword `reverse=true` prints with foreground and background colors exchanged, and `hidden=true` should be invisible in the terminal but can still be copied. These properties can be used in any combination. See also [`print`](@ref), [`println`](@ref), [`show`](@ref). !!! note Not all terminals support italic output. Some terminals interpret italic as reverse or blink. !!! compat "Julia 1.7" Keywords except `color` and `bold` were added in Julia 1.7. !!! compat "Julia 1.10" Support for italic output was added in Julia 1.10. """ @constprop :none printstyled(io::IO, msg...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = with_output_color(print, color, io, msg...; bold=bold, italic=italic, underline=underline, blink=blink, reverse=reverse, hidden=hidden) @constprop :none printstyled(msg...; bold::Bool=false, italic::Bool=false, underline::Bool=false, blink::Bool=false, reverse::Bool=false, hidden::Bool=false, color::Union{Int,Symbol}=:normal) = printstyled(stdout, msg...; bold=bold, italic=italic, underline=underline, blink=blink, reverse=reverse, hidden=hidden, color=color) """ Base.julia_cmd(juliapath=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Union{Nothing,String}=nothing) Return a julia command similar to the one of the running process. Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, `--compiled-modules`, `--pkgimages`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `-g`, `--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn` command line arguments that are not at their default values. Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently. Unless set to `nothing`, the `cpu_target` keyword argument can be used to override the CPU target set for the running process. To get the julia command without propagated command line arguments, `julia_cmd()[1]` can be used. !!! compat "Julia 1.1" Only the `--cpu-target`, `--sysimage`, `--depwarn`, `--compile` and `--check-bounds` flags were propagated before Julia 1.1. !!! compat "Julia 1.5" The flags `--color` and `--startup-file` were added in Julia 1.5. !!! compat "Julia 1.9" The keyword argument `cpu_target` was added. The flag `--pkgimages` was added in Julia 1.9. """ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Union{Nothing,String} = nothing) opts = JLOptions() if cpu_target === nothing cpu_target = unsafe_string(opts.cpu_target) end image_file = unsafe_string(opts.image_file) addflags = String[] let compile = if opts.compile_enabled == 0 "no" elseif opts.compile_enabled == 2 "all" elseif opts.compile_enabled == 3 "min" else "" # default = "yes" end isempty(compile) || push!(addflags, "--compile=$compile") end let depwarn = if opts.depwarn == 1 "yes" elseif opts.depwarn == 2 "error" else "" # default = "no" end isempty(depwarn) || push!(addflags, "--depwarn=$depwarn") end let check_bounds = if opts.check_bounds == 1 "yes" # on elseif opts.check_bounds == 2 "no" # off else "" # default = "auto" end isempty(check_bounds) || push!(addflags, "--check-bounds=$check_bounds") end opts.can_inline == 0 && push!(addflags, "--inline=no") opts.use_compiled_modules == 0 && push!(addflags, "--compiled-modules=no") opts.opt_level == 2 || push!(addflags, "-O$(opts.opt_level)") opts.opt_level_min == 0 || push!(addflags, "--min-optlevel=$(opts.opt_level_min)") push!(addflags, "-g$(opts.debug_level)") if opts.code_coverage != 0 # Forward the code-coverage flag only if applicable (if the filename is pid-dependent) coverage_file = (opts.output_code_coverage != C_NULL) ? unsafe_string(opts.output_code_coverage) : "" if isempty(coverage_file) || occursin("%p", coverage_file) if opts.code_coverage == 1 push!(addflags, "--code-coverage=user") elseif opts.code_coverage == 2 push!(addflags, "--code-coverage=all") elseif opts.code_coverage == 3 push!(addflags, "--code-coverage=@$(unsafe_string(opts.tracked_path))") end isempty(coverage_file) || push!(addflags, "--code-coverage=$coverage_file") end end if opts.malloc_log == 1 push!(addflags, "--track-allocation=user") elseif opts.malloc_log == 2 push!(addflags, "--track-allocation=all") elseif opts.malloc_log == 3 push!(addflags, "--track-allocation=@$(unsafe_string(opts.tracked_path))") end if opts.color == 1 push!(addflags, "--color=yes") elseif opts.color == 2 push!(addflags, "--color=no") end if opts.startupfile == 2 push!(addflags, "--startup-file=no") end if opts.use_sysimage_native_code == 0 push!(addflags, "--sysimage-native-code=no") end if opts.use_pkgimages == 0 push!(addflags, "--pkgimages=no") else # If pkgimage is set, malloc_log and code_coverage should not @assert opts.malloc_log == 0 && opts.code_coverage == 0 end return `$julia -C $cpu_target -J$image_file $addflags` end function julia_exename() if !Base.isdebugbuild() return @static Sys.iswindows() ? "julia.exe" : "julia" else return @static Sys.iswindows() ? "julia-debug.exe" : "julia-debug" end end """ securezero!(o) `securezero!` fills the memory associated with an object `o` with zeros. Unlike `fill!(o,0)` and similar code, which might be optimized away by the compiler for objects about to be discarded, the `securezero!` function will always be called. """ function securezero! end @noinline securezero!(a::AbstractArray{<:Number}) = fill!(a, 0) @noinline unsafe_securezero!(p::Ptr{T}, len::Integer=1) where {T} = memset(p, 0, len*sizeof(T)) unsafe_securezero!(p::Ptr{Cvoid}, len::Integer=1) = Ptr{Cvoid}(unsafe_securezero!(Ptr{UInt8}(p), len)) """ Base.getpass(message::AbstractString) -> Base.SecretBuffer Display a message and wait for the user to input a secret, returning an `IO` object containing the secret. !!! info "Windows" Note that on Windows, the secret might be displayed as it is typed; see `Base.winprompt` for securely retrieving username/password pairs from a graphical interface. """ function getpass end # Note, this helper only works within `with_raw_tty()` on POSIX platforms! function _getch() @static if Sys.iswindows() return UInt8(ccall(:_getch, Cint, ())) else return read(stdin, UInt8) end end const termios_size = Int(ccall(:jl_termios_size, Cint, ())) make_termios() = zeros(UInt8, termios_size) # These values seem to hold on all OSes we care about: # glibc Linux, musl Linux, macOS, FreeBSD @enum TCSETATTR_FLAGS TCSANOW=0 TCSADRAIN=1 TCSAFLUSH=2 function tcgetattr(fd::RawFD, termios) ret = ccall(:tcgetattr, Cint, (Cint, Ptr{Cvoid}), fd, termios) if ret != 0 throw(IOError("tcgetattr failed", ret)) end end function tcsetattr(fd::RawFD, termios, mode::TCSETATTR_FLAGS = TCSADRAIN) ret = ccall(:tcsetattr, Cint, (Cint, Cint, Ptr{Cvoid}), fd, Cint(mode), termios) if ret != 0 throw(IOError("tcsetattr failed", ret)) end end cfmakeraw(termios) = ccall(:cfmakeraw, Cvoid, (Ptr{Cvoid},), termios) function with_raw_tty(f::Function, input::TTY) input === stdin || throw(ArgumentError("with_raw_tty only works for stdin")) fd = RawFD(0) # If we're on windows, we do nothing, as we have access to `_getch()` quite easily @static if Sys.iswindows() return f() end # Get the current terminal mode old_termios = make_termios() tcgetattr(fd, old_termios) try # Set a new, raw, terminal mode new_termios = copy(old_termios) cfmakeraw(new_termios) tcsetattr(fd, new_termios) # Call the user-supplied callback f() finally # Always restore the terminal mode tcsetattr(fd, old_termios) end end function getpass(input::TTY, output::IO, prompt::AbstractString) input === stdin || throw(ArgumentError("getpass only works for stdin")) with_raw_tty(stdin) do print(output, prompt, ": ") flush(output) s = SecretBuffer() plen = 0 while true c = _getch() if c == 0xff || c == UInt8('\n') || c == UInt8('\r') || c == 0x04 break # EOF or return elseif c == 0x00 || c == 0xe0 _getch() # ignore function/arrow keys elseif c == UInt8('\b') && plen > 0 plen -= 1 # delete last character on backspace elseif !iscntrl(Char(c)) && plen < 128 write(s, c) end end return seekstart(s) end end # allow new getpass methods to be defined if stdin has been # redirected to some custom stream, e.g. in IJulia. getpass(prompt::AbstractString) = getpass(stdin, stdout, prompt) """ prompt(message; default="") -> Union{String, Nothing} Displays the `message` then waits for user input. Input is terminated when a newline (\\n) is encountered or EOF (^D) character is entered on a blank line. If a `default` is provided then the user can enter just a newline character to select the `default`. See also `Base.winprompt` (for Windows) and `Base.getpass` for secure entry of passwords. # Example ```julia-repl julia> your_name = Base.prompt("Enter your name"); Enter your name: Logan julia> your_name "Logan" ``` """ function prompt(input::IO, output::IO, message::AbstractString; default::AbstractString="") msg = !isempty(default) ? "$message [$default]: " : "$message: " print(output, msg) uinput = readline(input, keep=true) isempty(uinput) && return nothing # Encountered an EOF uinput = chomp(uinput) isempty(uinput) ? default : uinput end # allow new prompt methods to be defined if stdin has been # redirected to some custom stream, e.g. in IJulia. prompt(message::AbstractString; default::AbstractString="") = prompt(stdin, stdout, message, default=default) # Windows authentication prompt if Sys.iswindows() struct CREDUI_INFO cbSize::UInt32 parent::Ptr{Cvoid} pszMessageText::Ptr{UInt16} pszCaptionText::Ptr{UInt16} banner::Ptr{Cvoid} end const CREDUIWIN_GENERIC = 0x0001 const CREDUIWIN_IN_CRED_ONLY = 0x0020 const CREDUIWIN_ENUMERATE_CURRENT_USER = 0x0200 const CRED_PACK_GENERIC_CREDENTIALS = 0x0004 const ERROR_SUCCESS = 0x0000 const ERROR_CANCELLED = 0x04c7 function winprompt(message, caption, default_username; prompt_username = true) # Step 1: Create an encrypted username/password bundle that will be used to set # the default username (in theory could also provide a default password) credbuf = Vector{UInt8}(undef, 1024) credbufsize = Ref{UInt32}(sizeof(credbuf)) succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool, (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}), CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize) if !succeeded credbuf = resize!(credbuf, credbufsize[]) succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool, (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}), CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize) @assert succeeded end # Step 2: Create the actual dialog # 2.1: Set up the window messageArr = Base.cwstring(message) captionArr = Base.cwstring(caption) pfSave = Ref{Bool}(false) cred = Ref{CREDUI_INFO}(CREDUI_INFO(sizeof(CREDUI_INFO), C_NULL, pointer(messageArr), pointer(captionArr), C_NULL)) dwflags = CREDUIWIN_GENERIC | CREDUIWIN_ENUMERATE_CURRENT_USER if !prompt_username # Disable setting anything other than default_username dwflags |= CREDUIWIN_IN_CRED_ONLY end authPackage = Ref{Culong}(0) outbuf_data = Ref{Ptr{Cvoid}}(C_NULL) outbuf_size = Ref{Culong}(0) # 2.2: Do the actual request code = ccall((:CredUIPromptForWindowsCredentialsW, "credui.dll"), stdcall, UInt32, (Ptr{CREDUI_INFO}, UInt32, Ptr{Culong}, Ptr{Cvoid}, Culong, Ptr{Ptr{Cvoid}}, Ptr{Culong}, Ptr{Bool}, UInt32), cred, 0, authPackage, credbuf, credbufsize[], outbuf_data, outbuf_size, pfSave, dwflags) # 2.3: If that failed for any reason other than the user canceling, error out. # If the user canceled, just return nothing code == ERROR_CANCELLED && return nothing windowserror(:winprompt, code != ERROR_SUCCESS) # Step 3: Convert encrypted credentials back to plain text passbuf = Vector{UInt16}(undef, 1024) passlen = Ref{UInt32}(length(passbuf)) usernamebuf = Vector{UInt16}(undef, 1024) usernamelen = Ref{UInt32}(length(usernamebuf)) # Need valid buffers for domain, even though we don't care dummybuf = Vector{UInt16}(undef, 1024) succeeded = ccall((:CredUnPackAuthenticationBufferW, "credui.dll"), Bool, (UInt32, Ptr{Cvoid}, UInt32, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}), 0, outbuf_data[], outbuf_size[], usernamebuf, usernamelen, dummybuf, Ref{UInt32}(1024), passbuf, passlen) windowserror(:winprompt, !succeeded) # Step 4: Free the encrypted buffer # ccall(:SecureZeroMemory, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), outbuf_data[], outbuf_size[]) - not an actual function unsafe_securezero!(outbuf_data[], outbuf_size[]) ccall((:CoTaskMemFree, "ole32.dll"), Cvoid, (Ptr{Cvoid},), outbuf_data[]) # Done. passbuf_ = passbuf[1:passlen[]-1] result = (String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])), SecretBuffer!(transcode(UInt8, passbuf_))) securezero!(passbuf_) securezero!(passbuf) return result end end unsafe_crc32c(a, n, crc) = ccall(:jl_crc32c, UInt32, (UInt32, Ptr{UInt8}, Csize_t), crc, a, n) _crc32c(a::NTuple{<:Any, UInt8}, crc::UInt32=0x00000000) = unsafe_crc32c(Ref(a), length(a) % Csize_t, crc) _crc32c(a::Union{Array{UInt8},FastContiguousSubArray{UInt8,N,<:Array{UInt8}} where N}, crc::UInt32=0x00000000) = unsafe_crc32c(a, length(a) % Csize_t, crc) function _crc32c(s::Union{String, SubString{String}}, crc::UInt32=0x00000000) unsafe_crc32c(s, sizeof(s) % Csize_t, crc) end function _crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) nb < 0 && throw(ArgumentError("number of bytes to checksum must be โ‰ฅ 0, got $nb")) # use block size 24576=8192*3, since that is the threshold for # 3-way parallel SIMD code in the underlying jl_crc32c C function. buf = Vector{UInt8}(undef, min(nb, 24576)) while !eof(io) && nb > 24576 n = readbytes!(io, buf) crc = unsafe_crc32c(buf, n, crc) nb -= n end return unsafe_crc32c(buf, readbytes!(io, buf, min(nb, length(buf))), crc) end _crc32c(io::IO, crc::UInt32=0x00000000) = _crc32c(io, typemax(Int64), crc) _crc32c(io::IOStream, crc::UInt32=0x00000000) = _crc32c(io, filesize(io)-position(io), crc) _crc32c(uuid::UUID, crc::UInt32=0x00000000) = _crc32c(uuid.value, crc) _crc32c(x::UInt128, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt128}, Csize_t), crc, x, 16) _crc32c(x::UInt64, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt64}, Csize_t), crc, x, 8) _crc32c(x::UInt32, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt32}, Csize_t), crc, x, 4) _crc32c(x::UInt16, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt16}, Csize_t), crc, x, 2) _crc32c(x::UInt8, crc::UInt32=0x00000000) = ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt8}, Csize_t), crc, x, 1) """ @kwdef typedef This is a helper macro that automatically defines a keyword-based constructor for the type declared in the expression `typedef`, which must be a `struct` or `mutable struct` expression. The default argument is supplied by declaring fields of the form `field::T = default` or `field = default`. If no default is provided then the keyword argument becomes a required keyword argument in the resulting type constructor. Inner constructors can still be defined, but at least one should accept arguments in the same form as the default inner constructor (i.e. one positional argument per field) in order to function correctly with the keyword outer constructor. !!! compat "Julia 1.1" `Base.@kwdef` for parametric structs, and structs with supertypes requires at least Julia 1.1. !!! compat "Julia 1.9" This macro is exported as of Julia 1.9. # Examples ```jldoctest julia> @kwdef struct Foo a::Int = 1 # specified default b::String # required keyword end Foo julia> Foo(b="hi") Foo(1, "hi") julia> Foo() ERROR: UndefKeywordError: keyword argument `b` not assigned Stacktrace: [...] ``` """ macro kwdef(expr) expr = macroexpand(__module__, expr) # to expand @static isexpr(expr, :struct) || error("Invalid usage of @kwdef") T = expr.args[2] if T isa Expr && T.head === :<: T = T.args[1] end params_ex = Expr(:parameters) call_args = Any[] _kwdef!(expr.args[3], params_ex.args, call_args) # Only define a constructor if the type has fields, otherwise we'll get a stack # overflow on construction if !isempty(params_ex.args) if T isa Symbol sig = :(($(esc(T)))($params_ex)) call = :(($(esc(T)))($(call_args...))) body = Expr(:block, __source__, call) kwdefs = Expr(:function, sig, body) elseif isexpr(T, :curly) # if T == S{A<:AA,B<:BB}, define two methods # S(...) = ... # S{A,B}(...) where {A<:AA,B<:BB} = ... S = T.args[1] P = T.args[2:end] Q = Any[isexpr(U, :<:) ? U.args[1] : U for U in P] SQ = :($S{$(Q...)}) body1 = Expr(:block, __source__, :(($(esc(S)))($(call_args...)))) sig1 = :(($(esc(S)))($params_ex)) def1 = Expr(:function, sig1, body1) body2 = Expr(:block, __source__, :(($(esc(SQ)))($(call_args...)))) sig2 = :(($(esc(SQ)))($params_ex) where {$(esc.(P)...)}) def2 = Expr(:function, sig2, body2) kwdefs = Expr(:block, def1, def2) else error("Invalid usage of @kwdef") end else kwdefs = nothing end return quote $(esc(:($Base.@__doc__ $expr))) $kwdefs end end # @kwdef helper function # mutates arguments inplace function _kwdef!(blk, params_args, call_args) for i in eachindex(blk.args) ei = blk.args[i] if ei isa Symbol # var push!(params_args, ei) push!(call_args, ei) elseif ei isa Expr is_atomic = ei.head === :atomic ei = is_atomic ? first(ei.args) : ei # strip "@atomic" and add it back later is_const = ei.head === :const ei = is_const ? first(ei.args) : ei # strip "const" and add it back later # Note: `@atomic const ..` isn't valid, but reconstruct it anyway to serve a nice error if ei isa Symbol # const var push!(params_args, ei) push!(call_args, ei) elseif ei.head === :(=) lhs = ei.args[1] if lhs isa Symbol # var = defexpr var = lhs elseif lhs isa Expr && lhs.head === :(::) && lhs.args[1] isa Symbol # var::T = defexpr var = lhs.args[1] else # something else, e.g. inline inner constructor # F(...) = ... continue end defexpr = ei.args[2] # defexpr push!(params_args, Expr(:kw, var, esc(defexpr))) push!(call_args, var) lhs = is_const ? Expr(:const, lhs) : lhs lhs = is_atomic ? Expr(:atomic, lhs) : lhs blk.args[i] = lhs # overrides arg elseif ei.head === :(::) && ei.args[1] isa Symbol # var::Typ var = ei.args[1] push!(params_args, var) push!(call_args, var) elseif ei.head === :block # can arise with use of @static inside type decl _kwdef!(ei, params_args, call_args) end end end blk end # testing """ Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_THREADS / 2), exit_on_error=false, revise=false, [seed]) Run the Julia unit tests listed in `tests`, which can be either a string or an array of strings, using `ncores` processors. If `exit_on_error` is `false`, when one test fails, all remaining tests in other files will still be run; they are otherwise discarded, when `exit_on_error == true`. If `revise` is `true`, the `Revise` package is used to load any modifications to `Base` or to the standard libraries before running the tests. If a seed is provided via the keyword argument, it is used to seed the global RNG in the context where the tests are run; otherwise the seed is chosen randomly. """ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS / 2), exit_on_error::Bool=false, revise::Bool=false, seed::Union{BitInteger,Nothing}=nothing) if isa(tests,AbstractString) tests = split(tests) end exit_on_error && push!(tests, "--exit-on-error") revise && push!(tests, "--revise") seed !== nothing && push!(tests, "--seed=0x$(string(seed % UInt128, base=16))") # cast to UInt128 to avoid a minus sign ENV2 = copy(ENV) ENV2["JULIA_CPU_THREADS"] = "$ncores" pathsep = Sys.iswindows() ? ";" : ":" ENV2["JULIA_DEPOT_PATH"] = string(mktempdir(; cleanup = true), pathsep) # make sure the default depots can be loaded delete!(ENV2, "JULIA_LOAD_PATH") delete!(ENV2, "JULIA_PROJECT") try run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) nothing catch buf = PipeBuffer() original_load_path = copy(Base.LOAD_PATH); empty!(Base.LOAD_PATH); pushfirst!(Base.LOAD_PATH, "@stdlib") Base.require(Base, :InteractiveUtils).versioninfo(buf) empty!(Base.LOAD_PATH); append!(Base.LOAD_PATH, original_load_path) error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" * "including error messages above and the output of versioninfo():\n$(read(buf, String))") end end """ isdebugbuild() Return `true` if julia is a debug version. """ function isdebugbuild() return ccall(:jl_is_debugbuild, Cint, ()) != 0 end M/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/client.jl๒M# This file is a part of Julia. License is MIT: https://julialang.org/license ## client.jl - frontend handling command line options, environment setup, ## and REPL have_color = nothing const default_color_warn = :yellow const default_color_error = :light_red const default_color_info = :cyan const default_color_debug = :blue const default_color_input = :normal const default_color_answer = :normal const color_normal = text_colors[:normal] function repl_color(key, default) env_str = get(ENV, key, "") c = tryparse(Int, env_str) c_conv = something(c, Symbol(env_str)) haskey(text_colors, c_conv) ? c_conv : default end error_color() = repl_color("JULIA_ERROR_COLOR", default_color_error) warn_color() = repl_color("JULIA_WARN_COLOR" , default_color_warn) info_color() = repl_color("JULIA_INFO_COLOR" , default_color_info) debug_color() = repl_color("JULIA_DEBUG_COLOR" , default_color_debug) input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)] answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)] stackframe_lineinfo_color() = repl_color("JULIA_STACKFRAME_LINEINFO_COLOR", :bold) stackframe_function_color() = repl_color("JULIA_STACKFRAME_FUNCTION_COLOR", :bold) function repl_cmd(cmd, out) shell = shell_split(get(ENV, "JULIA_SHELL", get(ENV, "SHELL", "/bin/sh"))) shell_name = Base.basename(shell[1]) # Immediately expand all arguments, so that typing e.g. ~/bin/foo works. cmd.exec .= expanduser.(cmd.exec) if isempty(cmd.exec) throw(ArgumentError("no cmd to execute")) elseif cmd.exec[1] == "cd" new_oldpwd = pwd() if length(cmd.exec) > 2 throw(ArgumentError("cd method only takes one argument")) elseif length(cmd.exec) == 2 dir = cmd.exec[2] if dir == "-" if !haskey(ENV, "OLDPWD") error("cd: OLDPWD not set") end dir = ENV["OLDPWD"] end cd(dir) else cd() end ENV["OLDPWD"] = new_oldpwd println(out, pwd()) else @static if !Sys.iswindows() if shell_name == "fish" shell_escape_cmd = "begin; $(shell_escape_posixly(cmd)); and true; end" else shell_escape_cmd = "($(shell_escape_posixly(cmd))) && true" end cmd = `$shell -c $shell_escape_cmd` end try run(ignorestatus(cmd)) catch # Windows doesn't shell out right now (complex issue), so Julia tries to run the program itself # Julia throws an exception if it can't find the program, but the stack trace isn't useful lasterr = current_exceptions() lasterr = ExceptionStack([(exception = e[1], backtrace = [] ) for e in lasterr]) invokelatest(display_error, lasterr) end end nothing end # deprecated function--preserved for DocTests.jl function ip_matches_func(ip, func::Symbol) for fr in StackTraces.lookup(ip) if fr === StackTraces.UNKNOWN || fr.from_c return false end fr.func === func && return true end return false end function scrub_repl_backtrace(bt) if bt !== nothing && !(bt isa Vector{Any}) # ignore our sentinel value types bt = bt isa Vector{StackFrame} ? copy(bt) : stacktrace(bt) # remove REPL-related frames from interactive printing eval_ind = findlast(frame -> !frame.from_c && frame.func === :eval, bt) eval_ind === nothing || deleteat!(bt, eval_ind:length(bt)) end return bt end scrub_repl_backtrace(stack::ExceptionStack) = ExceptionStack(Any[(;x.exception, backtrace = scrub_repl_backtrace(x.backtrace)) for x in stack]) istrivialerror(stack::ExceptionStack) = length(stack) == 1 && length(stack[1].backtrace) โ‰ค 1 && !isa(stack[1].exception, MethodError) # frame 1 = top level; assumes already went through scrub_repl_backtrace; MethodError see #50803 function display_error(io::IO, stack::ExceptionStack) printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) show_exception_stack(IOContext(io, :limit => true), stack) println(io) end display_error(stack::ExceptionStack) = display_error(stderr, stack) # these forms are depended on by packages outside Julia function display_error(io::IO, er, bt) printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) showerror(IOContext(io, :limit => true), er, bt, backtrace = bt!==nothing) println(io) end display_error(er, bt=nothing) = display_error(stderr, er, bt) function eval_user_input(errio, @nospecialize(ast), show_value::Bool) errcount = 0 lasterr = nothing have_color = get(stdout, :color, false)::Bool while true try if have_color print(color_normal) end if lasterr !== nothing lasterr = scrub_repl_backtrace(lasterr) istrivialerror(lasterr) || setglobal!(Base.MainInclude, :err, lasterr) invokelatest(display_error, errio, lasterr) errcount = 0 lasterr = nothing else ast = Meta.lower(Main, ast) value = Core.eval(Main, ast) setglobal!(Base.MainInclude, :ans, value) if !(value === nothing) && show_value if have_color print(answer_color()) end try invokelatest(display, value) catch @error "Evaluation succeeded, but an error occurred while displaying the value" typeof(value) rethrow() end end end break catch if errcount > 0 @error "SYSTEM: display_error(errio, lasterr) caused an error" end errcount += 1 lasterr = scrub_repl_backtrace(current_exceptions()) setglobal!(Base.MainInclude, :err, lasterr) if errcount > 2 @error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount break end end end isa(stdin, TTY) && println() nothing end function _parse_input_line_core(s::String, filename::String) ex = Meta.parseall(s, filename=filename) if ex isa Expr && ex.head === :toplevel if isempty(ex.args) return nothing end last = ex.args[end] if last isa Expr && (last.head === :error || last.head === :incomplete) # if a parse error happens in the middle of a multi-line input # return only the error, so that none of the input is evaluated. return last end end return ex end function parse_input_line(s::String; filename::String="none", depwarn=true) # For now, assume all parser warnings are depwarns ex = if depwarn _parse_input_line_core(s, filename) else with_logger(NullLogger()) do _parse_input_line_core(s, filename) end end return ex end parse_input_line(s::AbstractString) = parse_input_line(String(s)) # detect the reason which caused an :incomplete expression # from the error message # NOTE: the error messages are defined in src/julia-parser.scm function fl_incomplete_tag(msg::AbstractString) occursin("string", msg) && return :string occursin("comment", msg) && return :comment occursin("requires end", msg) && return :block occursin("\"`\"", msg) && return :cmd occursin("character", msg) && return :char return :other end incomplete_tag(ex) = :none function incomplete_tag(ex::Expr) if ex.head !== :incomplete return :none elseif isempty(ex.args) return :other elseif ex.args[1] isa String return fl_incomplete_tag(ex.args[1]) else return incomplete_tag(ex.args[1]) end end incomplete_tag(exc::Meta.ParseError) = incomplete_tag(exc.detail) function exec_options(opts) quiet = (opts.quiet != 0) startup = (opts.startupfile != 2) history_file = (opts.historyfile != 0) color_set = (opts.color != 0) # --color!=auto global have_color = color_set ? (opts.color == 1) : nothing # --color=on global is_interactive = (opts.isinteractive != 0) # pre-process command line argument list arg_is_program = !isempty(ARGS) repl = !arg_is_program cmds = unsafe_load_commands(opts.commands) for (cmd, arg) in cmds if cmd == 'e' arg_is_program = false repl = false elseif cmd == 'E' arg_is_program = false repl = false elseif cmd == 'L' # nothing elseif cmd == 'B' # --bug-report # If we're doing a bug report, don't load anything else. We will # spawn a child in which to execute these options. let InteractiveUtils = load_InteractiveUtils() InteractiveUtils.report_bug(arg) end return nothing else @warn "Unexpected command -$cmd'$arg'" end end # remove filename from ARGS global PROGRAM_FILE = arg_is_program ? popfirst!(ARGS) : "" # Load Distributed module only if any of the Distributed options have been specified. distributed_mode = (opts.worker == 1) || (opts.nprocs > 0) || (opts.machine_file != C_NULL) if distributed_mode let Distributed = require(PkgId(UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) Core.eval(Main, :(const Distributed = $Distributed)) Core.eval(Main, :(using .Distributed)) end invokelatest(Main.Distributed.process_opts, opts) end interactiveinput = (repl || is_interactive::Bool) && isa(stdin, TTY) is_interactive::Bool |= interactiveinput # load ~/.julia/config/startup.jl file if startup try load_julia_startup() catch invokelatest(display_error, scrub_repl_backtrace(current_exceptions())) !(repl || is_interactive::Bool) && exit(1) end end # process cmds list for (cmd, arg) in cmds if cmd == 'e' Core.eval(Main, parse_input_line(arg)) elseif cmd == 'E' invokelatest(show, Core.eval(Main, parse_input_line(arg))) println() elseif cmd == 'L' # load file immediately on all processors if !distributed_mode include(Main, arg) else # TODO: Move this logic to Distributed and use a callback @sync for p in invokelatest(Main.procs) @async invokelatest(Main.remotecall_wait, include, p, Main, arg) end end end end # load file if arg_is_program # program if !is_interactive::Bool exit_on_sigint(true) end try if PROGRAM_FILE == "-" include_string(Main, read(stdin, String), "stdin") else include(Main, PROGRAM_FILE) end catch invokelatest(display_error, scrub_repl_backtrace(current_exceptions())) if !is_interactive::Bool exit(1) end end end if repl || is_interactive::Bool if interactiveinput banner = (opts.banner != 0) # --banner!=no else banner = (opts.banner == 1) # --banner=yes end run_main_repl(interactiveinput, quiet, banner, history_file, color_set) end nothing end function _global_julia_startup_file() # If the user built us with a specific Base.SYSCONFDIR, check that location first for a startup.jl file # If it is not found, then continue on to the relative path based on Sys.BINDIR BINDIR = Sys.BINDIR SYSCONFDIR = Base.SYSCONFDIR if !isempty(SYSCONFDIR) p1 = abspath(BINDIR, SYSCONFDIR, "julia", "startup.jl") isfile(p1) && return p1 end p2 = abspath(BINDIR, "..", "etc", "julia", "startup.jl") isfile(p2) && return p2 return nothing end function _local_julia_startup_file() if !isempty(DEPOT_PATH) path = abspath(DEPOT_PATH[1], "config", "startup.jl") isfile(path) && return path end return nothing end function load_julia_startup() global_file = _global_julia_startup_file() (global_file !== nothing) && include(Main, global_file) local_file = _local_julia_startup_file() (local_file !== nothing) && include(Main, local_file) return nothing end const repl_hooks = [] """ atreplinit(f) Register a one-argument function to be called before the REPL interface is initialized in interactive sessions; this is useful to customize the interface. The argument of `f` is the REPL object. This function should be called from within the `.julia/config/startup.jl` initialization file. """ atreplinit(f::Function) = (pushfirst!(repl_hooks, f); nothing) function __atreplinit(repl) for f in repl_hooks try f(repl) catch err showerror(stderr, err) println(stderr) end end end _atreplinit(repl) = invokelatest(__atreplinit, repl) function load_InteractiveUtils(mod::Module=Main) # load interactive-only libraries if !isdefined(mod, :InteractiveUtils) try let InteractiveUtils = require(PkgId(UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils")) Core.eval(mod, :(const InteractiveUtils = $InteractiveUtils)) Core.eval(mod, :(using .InteractiveUtils)) return InteractiveUtils end catch ex @warn "Failed to import InteractiveUtils into module $mod" exception=(ex, catch_backtrace()) end return nothing end return getfield(mod, :InteractiveUtils) end global active_repl # run the requested sort of evaluation loop on stdio function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool) load_InteractiveUtils() if interactive && isassigned(REPL_MODULE_REF) invokelatest(REPL_MODULE_REF[]) do REPL term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr) banner && Base.banner(term) if term.term_type == "dumb" repl = REPL.BasicREPL(term) quiet || @warn "Terminal not fully functional" else repl = REPL.LineEditREPL(term, get(stdout, :color, false), true) repl.history_file = history_file end global active_repl = repl # Make sure any displays pushed in .julia/config/startup.jl ends up above the # REPLDisplay pushdisplay(REPL.REPLDisplay(repl)) _atreplinit(repl) REPL.run_repl(repl, backend->(global active_repl_backend = backend)) end else # otherwise provide a simple fallback if interactive && !quiet @warn "REPL provider not available: using basic fallback" end banner && Base.banner() let input = stdin if isa(input, File) || isa(input, IOStream) # for files, we can slurp in the whole thing at once ex = parse_input_line(read(input, String)) if Meta.isexpr(ex, :toplevel) # if we get back a list of statements, eval them sequentially # as if we had parsed them sequentially for stmt in ex.args eval_user_input(stderr, stmt, true) end body = ex.args else eval_user_input(stderr, ex, true) end else while isopen(input) || !eof(input) if interactive print("julia> ") flush(stdout) end try line = "" ex = nothing while !eof(input) line *= readline(input, keep=true) ex = parse_input_line(line) if !(isa(ex, Expr) && ex.head === :incomplete) break end end eval_user_input(stderr, ex, true) catch err isa(err, InterruptException) ? print("\n\n") : rethrow() end end end end end nothing end # MainInclude exists to hide Main.include and eval from `names(Main)`. baremodule MainInclude using ..Base # These definitions calls Base._include rather than Base.include to get # one-frame stacktraces for the common case of using include(fname) in Main. include(mapexpr::Function, fname::AbstractString) = Base._include(mapexpr, Main, fname) function include(fname::AbstractString) isa(fname, String) || (fname = Base.convert(String, fname)::String) Base._include(identity, Main, fname) end eval(x) = Core.eval(Main, x) """ ans A variable referring to the last computed value, automatically imported to the interactive prompt. """ global ans = nothing """ err A variable referring to the last thrown errors, automatically imported to the interactive prompt. The thrown errors are collected in a stack of exceptions. """ global err = nothing # weakly exposes ans and err variables to Main export ans, err end """ eval(expr) Evaluate an expression in the global scope of the containing module. Every `Module` (except those defined with `baremodule`) has its own 1-argument definition of `eval`, which evaluates expressions in that module. """ MainInclude.eval """ include([mapexpr::Function,] path::AbstractString) Evaluate the contents of the input source file in the global scope of the containing module. Every module (except those defined with `baremodule`) has its own definition of `include`, which evaluates the file in that module. Returns the result of the last evaluated expression of the input file. During including, a task-local include path is set to the directory containing the file. Nested calls to `include` will search relative to that path. This function is typically used to load source interactively, or to combine files in packages that are broken into multiple source files. The argument `path` is normalized using [`normpath`](@ref) which will resolve relative path tokens such as `..` and convert `/` to the appropriate path separator. The optional first argument `mapexpr` can be used to transform the included code before it is evaluated: for each parsed expression `expr` in `path`, the `include` function actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). Use [`Base.include`](@ref) to evaluate a file into another module. !!! compat "Julia 1.5" Julia 1.5 is required for passing the `mapexpr` argument. """ MainInclude.include function _start() empty!(ARGS) append!(ARGS, Core.ARGS) # clear any postoutput hooks that were saved in the sysimage empty!(Base.postoutput_hooks) try exec_options(JLOptions()) catch invokelatest(display_error, scrub_repl_backtrace(current_exceptions())) exit(1) end if is_interactive && get(stdout, :color, false) print(color_normal) end end O/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/asyncmap.jl4# This file is a part of Julia. License is MIT: https://julialang.org/license using Base.Iterators: Enumerate """ asyncmap(f, c...; ntasks=0, batch_size=nothing) Uses multiple concurrent tasks to map `f` over a collection (or multiple equal length collections). For multiple collection arguments, `f` is applied elementwise. `ntasks` specifies the number of tasks to run concurrently. Depending on the length of the collections, if `ntasks` is unspecified, up to 100 tasks will be used for concurrent mapping. `ntasks` can also be specified as a zero-arg function. In this case, the number of tasks to run in parallel is checked before processing every element and a new task started if the value of `ntasks_func` is greater than the current number of tasks. If `batch_size` is specified, the collection is processed in batch mode. `f` must then be a function that must accept a `Vector` of argument tuples and must return a vector of results. The input vector will have a length of `batch_size` or less. The following examples highlight execution in different tasks by returning the `objectid` of the tasks in which the mapping function is executed. First, with `ntasks` undefined, each element is processed in a different task. ``` julia> tskoid() = objectid(current_task()); julia> asyncmap(x->tskoid(), 1:5) 5-element Array{UInt64,1}: 0x6e15e66c75c75853 0x440f8819a1baa682 0x9fb3eeadd0c83985 0xebd3e35fe90d4050 0x29efc93edce2b961 julia> length(unique(asyncmap(x->tskoid(), 1:5))) 5 ``` With `ntasks=2` all elements are processed in 2 tasks. ``` julia> asyncmap(x->tskoid(), 1:5; ntasks=2) 5-element Array{UInt64,1}: 0x027ab1680df7ae94 0xa23d2f80cd7cf157 0x027ab1680df7ae94 0xa23d2f80cd7cf157 0x027ab1680df7ae94 julia> length(unique(asyncmap(x->tskoid(), 1:5; ntasks=2))) 2 ``` With `batch_size` defined, the mapping function needs to be changed to accept an array of argument tuples and return an array of results. `map` is used in the modified mapping function to achieve this. ``` julia> batch_func(input) = map(x->string("args_tuple: ", x, ", element_val: ", x[1], ", task: ", tskoid()), input) batch_func (generic function with 1 method) julia> asyncmap(batch_func, 1:5; ntasks=2, batch_size=2) 5-element Array{String,1}: "args_tuple: (1,), element_val: 1, task: 9118321258196414413" "args_tuple: (2,), element_val: 2, task: 4904288162898683522" "args_tuple: (3,), element_val: 3, task: 9118321258196414413" "args_tuple: (4,), element_val: 4, task: 4904288162898683522" "args_tuple: (5,), element_val: 5, task: 9118321258196414413" ``` """ function asyncmap(f, c...; ntasks=0, batch_size=nothing) return async_usemap(f, c...; ntasks=ntasks, batch_size=batch_size) end function async_usemap(f, c...; ntasks=0, batch_size=nothing) ntasks = verify_ntasks(c[1], ntasks) batch_size = verify_batch_size(batch_size) if batch_size !== nothing exec_func = batch -> begin # extract the Refs from the input tuple batch_refs = map(x->x[1], batch) # and the args tuple.... batched_args = map(x->x[2], batch) results = f(batched_args) foreach(x -> (batch_refs[x[1]].x = x[2]), enumerate(results)) end else exec_func = (r,args) -> (r.x = f(args...)) end chnl, worker_tasks = setup_chnl_and_tasks(exec_func, ntasks, batch_size) return wrap_n_exec_twice(chnl, worker_tasks, ntasks, exec_func, c...) end batch_size_err_str(batch_size) = string("batch_size must be specified as a positive integer. batch_size=", batch_size) function verify_batch_size(batch_size) if batch_size === nothing return batch_size elseif isa(batch_size, Number) batch_size = Int(batch_size) batch_size < 1 && throw(ArgumentError(batch_size_err_str(batch_size))) return batch_size else throw(ArgumentError(batch_size_err_str(batch_size))) end end function verify_ntasks(iterable, ntasks) if !((isa(ntasks, Number) && (ntasks >= 0)) || isa(ntasks, Function)) err = string("ntasks must be specified as a positive integer or a 0-arg function. ntasks=", ntasks) throw(ArgumentError(err)) end if ntasks == 0 if haslength(iterable) ntasks = max(1,min(100, length(iterable))) else ntasks = 100 end end return ntasks end function wrap_n_exec_twice(chnl, worker_tasks, ntasks, exec_func, c...) # The driver task, creates a Ref object and writes it and the args tuple to # the communication channel for processing by a free worker task. push_arg_to_channel = (x...) -> (r=Ref{Any}(nothing); put!(chnl,(r,x));r) if isa(ntasks, Function) map_f = (x...) -> begin # check number of tasks every time, and start one if required. # number_tasks > optimal_number is fine, the other way around is inefficient. if length(worker_tasks) < ntasks() start_worker_task!(worker_tasks, exec_func, chnl) end push_arg_to_channel(x...) end else map_f = push_arg_to_channel end maptwice(map_f, chnl, worker_tasks, c...) end function maptwice(wrapped_f, chnl, worker_tasks, c...) # first run, returns a collection of Refs asyncrun_excp = nothing local asyncrun try asyncrun = map(wrapped_f, c...) catch ex if isa(ex,InvalidStateException) # channel could be closed due to exceptions in the async tasks, # we propagate those errors, if any, over the `put!` failing # in asyncrun due to a closed channel. asyncrun_excp = ex else rethrow() end end # close channel and wait for all worker tasks to finish close(chnl) # check and throw any exceptions from the worker tasks foreach(x->(v=fetch(x); isa(v, Exception) && throw(v)), worker_tasks) # check if there was a genuine problem with asyncrun (asyncrun_excp !== nothing) && throw(asyncrun_excp) if isa(asyncrun, Ref) # scalar case return asyncrun.x else # second run, extract values from the Refs and return return map(ref->ref.x, asyncrun) end end function setup_chnl_and_tasks(exec_func, ntasks, batch_size=nothing) if isa(ntasks, Function) nt = ntasks()::Int # start at least one worker task. if nt == 0 nt = 1 end else nt = ntasks::Int end # Use an unbuffered channel for communicating with the worker tasks. In the event # of an error in any of the worker tasks, the channel is closed. This # results in the `put!` in the driver task failing immediately. chnl = Channel(0) worker_tasks = [] foreach(_ -> start_worker_task!(worker_tasks, exec_func, chnl, batch_size), 1:nt) yield() return (chnl, worker_tasks) end function start_worker_task!(worker_tasks, exec_func, chnl, batch_size=nothing) t = @async begin retval = nothing try if isa(batch_size, Number) while isopen(chnl) # The mapping function expects an array of input args, as it processes # elements in a batch. batch_collection=Any[] n = 0 for exec_data in chnl push!(batch_collection, exec_data) n += 1 (n == batch_size) && break end if n > 0 exec_func(batch_collection) end end else for exec_data in chnl exec_func(exec_data...) end end catch e close(chnl) retval = capture_exception(e, catch_backtrace()) end retval end push!(worker_tasks, t) end # Special handling for some types. function asyncmap(f, s::AbstractString; kwargs...) s2 = Vector{Char}(undef, length(s)) asyncmap!(f, s2, s; kwargs...) return String(s2) end # map on a single BitArray returns a BitArray if the mapping function is boolean. function asyncmap(f, b::BitArray; kwargs...) b2 = async_usemap(f, b; kwargs...) if eltype(b2) == Bool return BitArray(b2) end return b2 end mutable struct AsyncCollector f results enumerator::Enumerate ntasks batch_size nt_check::Bool # check number of tasks on every iteration AsyncCollector(f, r, en::Enumerate, ntasks, batch_size) = new(f, r, en, ntasks, batch_size, isa(ntasks, Function)) end """ AsyncCollector(f, results, c...; ntasks=0, batch_size=nothing) -> iterator Return an iterator which applies `f` to each element of `c` asynchronously and collects output into `results`. Keyword args `ntasks` and `batch_size` have the same behavior as in [`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note `iterate(::AsyncCollector, state) -> (nothing, state)`. A successful return from `iterate` indicates that the next element from the input collection is being processed asynchronously. It blocks until a free worker task becomes available. !!! note `for _ in AsyncCollector(f, results, c...; ntasks=1) end` is equivalent to `map!(f, results, c...)`. """ function AsyncCollector(f, results, c...; ntasks=0, batch_size=nothing) AsyncCollector(f, results, enumerate(zip(c...)), ntasks, batch_size) end mutable struct AsyncCollectorState chnl::Channel worker_tasks::Array{Task,1} enum_state # enumerator state AsyncCollectorState(chnl::Channel, worker_tasks::Vector) = new(chnl, convert(Vector{Task}, worker_tasks)) end function iterate(itr::AsyncCollector) itr.ntasks = verify_ntasks(itr.enumerator, itr.ntasks) itr.batch_size = verify_batch_size(itr.batch_size) chnl, worker_tasks = setup_chnl_and_tasks((i,args) -> (itr.results[i]=itr.f(args...)), itr.ntasks, itr.batch_size) return iterate(itr, AsyncCollectorState(chnl, worker_tasks)) end function wait_done(itr::AsyncCollector, state::AsyncCollectorState) close(state.chnl) # wait for all tasks to finish foreach(x->(v=fetch(x); isa(v, Exception) && throw(v)), state.worker_tasks) empty!(state.worker_tasks) end function iterate(itr::AsyncCollector, state::AsyncCollectorState) if itr.nt_check && (length(state.worker_tasks) < itr.ntasks()) start_worker_task!(state.worker_tasks, itr.f, state.chnl) end # Get index and mapped function arguments from enumeration iterator. y = isdefined(state, :enum_state) ? iterate(itr.enumerator, state.enum_state) : iterate(itr.enumerator) if y === nothing wait_done(itr, state) return nothing end (i, args), state.enum_state = y put!(state.chnl, (i, args)) return (nothing, state) end """ AsyncGenerator(f, c...; ntasks=0, batch_size=nothing) -> iterator Apply `f` to each element of `c` using at most `ntasks` asynchronous tasks. Keyword args `ntasks` and `batch_size` have the same behavior as in [`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note `collect(AsyncGenerator(f, c...; ntasks=1))` is equivalent to `map(f, c...)`. """ mutable struct AsyncGenerator collector::AsyncCollector end function AsyncGenerator(f, c...; ntasks=0) AsyncGenerator(AsyncCollector(f, Dict{Int,Any}(), c...; ntasks=ntasks)) end mutable struct AsyncGeneratorState i::Int collector_done::Bool collector_state::AsyncCollectorState AsyncGeneratorState(i::Int) = new(i, false) end function iterate(itr::AsyncGenerator, state::AsyncGeneratorState=AsyncGeneratorState(0)) state.i += 1 results_dict = itr.collector.results while !state.collector_done && !haskey(results_dict, state.i) y = isdefined(state, :collector_state) ? iterate(itr.collector, state.collector_state) : iterate(itr.collector) if y === nothing # `check_done` waits for async tasks to finish. if we do not have the index # we are looking for, it is an error. state.collector_done = true break; end _, state.collector_state = y end state.collector_done && isempty(results_dict) && return nothing r = results_dict[state.i] delete!(results_dict, state.i) return (r, state) end # pass-through iterator traits to the iterable # on which the mapping function is being applied IteratorSize(::Type{AsyncGenerator}) = SizeUnknown() IteratorEltype(::Type{AsyncGenerator}) = EltypeUnknown() size(itr::AsyncGenerator) = size(itr.collector.enumerator) length(itr::AsyncGenerator) = length(itr.collector.enumerator) """ asyncmap!(f, results, c...; ntasks=0, batch_size=nothing) Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. $(_DOCS_ALIASING_WARNING) """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) r end Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/deprecated.jl˜6# This file is a part of Julia. License is MIT: https://julialang.org/license # Deprecated functions and objects # # Please add new deprecations at the bottom of the file. # A function deprecated in a release will be removed in the next one. # Please also add a reference to the pull request which introduced the # deprecation. For simple cases where a direct replacement is available, # use @deprecate. @deprecate takes care of calling the replacement # and of exporting the function. # # For more complex cases, move the body of the deprecated method in this file, # and call depwarn() directly from inside it. The symbol depwarn() expects is # the name of the function, which is used to ensure that the deprecation warning # is only printed the first time for each call place. """ @deprecate old new [export_old=true] Deprecate method `old` and specify the replacement call `new`, defining a new method `old` with the specified signature in the process. To prevent `old` from being exported, set `export_old` to `false`. !!! compat "Julia 1.5" As of Julia 1.5, functions defined by `@deprecate` do not print warning when `julia` is run without the `--depwarn=yes` flag set, as the default value of `--depwarn` option is `no`. The warnings are printed from tests run by `Pkg.test()`. # Examples ```jldoctest julia> @deprecate old(x) new(x) old (generic function with 1 method) julia> @deprecate old(x) new(x) false old (generic function with 1 method) ``` Calls to `@deprecate` without explicit type-annotations will define deprecated methods accepting any number of positional and keyword arguments of type `Any`. !!! compat "Julia 1.9" Keyword arguments are forwarded when there is no explicit type annotation as of Julia 1.9. For older versions, you can manually forward positional and keyword arguments by doing `@deprecate old(args...; kwargs...) new(args...; kwargs...)`. To restrict deprecation to a specific signature, annotate the arguments of `old`. For example, ```jldoctest; filter = r"@ .*"a julia> new(x::Int) = x; julia> new(x::Float64) = 2x; julia> @deprecate old(x::Int) new(x); julia> methods(old) # 1 method for generic function "old" from Main: [1] old(x::Int64) @ deprecated.jl:94 ``` will define and deprecate a method `old(x::Int)` that mirrors `new(x::Int)` but will not define nor deprecate the method `old(x::Float64)`. """ macro deprecate(old, new, export_old=true) function cannot_export_nonsymbol() error( "if the third `export_old` argument is not specified or `true`, the first", " argument must be of form", " (1) `f(...)` where `f` is a symbol,", " (2) `T{...}(...)` where `T` is a symbol, or", " (3) a symbol.", ) end meta = Expr(:meta, :noinline) if isa(old, Expr) && (old.head === :call || old.head === :where) remove_linenums!(new) oldcall = sprint(show_unquoted, old) newcall = sprint(show_unquoted, new) # if old.head is a :where, step down one level to the :call to avoid code duplication below callexpr = old.head === :call ? old : old.args[1] if callexpr.head === :call fnexpr = callexpr.args[1] if fnexpr isa Expr && fnexpr.head === :curly fnexpr = fnexpr.args[1] end if export_old if fnexpr isa Symbol maybe_export = Expr(:export, esc(fnexpr)) else cannot_export_nonsymbol() end else maybe_export = nothing end else error("invalid usage of @deprecate") end Expr(:toplevel, maybe_export, :($(esc(old)) = begin $meta depwarn($"`$oldcall` is deprecated, use `$newcall` instead.", Core.Typeof($(esc(fnexpr))).name.mt.name) $(esc(new)) end)) else if export_old && !(old isa Symbol) cannot_export_nonsymbol() end Expr(:toplevel, export_old ? Expr(:export, esc(old)) : nothing, :(function $(esc(old))(args...; kwargs...) $meta depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name) $(esc(new))(args...; kwargs...) end)) end end function depwarn(msg, funcsym; force::Bool=false) opts = JLOptions() if opts.depwarn == 2 throw(ErrorException(msg)) end deplevel = force || opts.depwarn == 1 ? CoreLogging.Warn : CoreLogging.BelowMinLevel @logmsg( deplevel, msg, _module=begin bt = backtrace() frame, caller = firstcaller(bt, funcsym) linfo = caller.linfo if linfo isa Core.MethodInstance def = linfo.def def isa Module ? def : def.module else Core # TODO: Is it reasonable to attribute callers without linfo to Core? end end, _file=String(caller.file), _line=caller.line, _id=(frame,funcsym), _group=:depwarn, caller=caller, maxlog=funcsym === nothing ? nothing : 1 ) nothing end firstcaller(bt::Vector, ::Nothing) = Ptr{Cvoid}(0), StackTraces.UNKNOWN firstcaller(bt::Vector, funcsym::Symbol) = firstcaller(bt, (funcsym,)) function firstcaller(bt::Vector, funcsyms) # Identify the calling line found = false for ip in bt lkups = StackTraces.lookup(ip) for lkup in lkups if lkup == StackTraces.UNKNOWN || lkup.from_c continue end if found return ip, lkup end found = lkup.func in funcsyms # look for constructor type name if !found li = lkup.linfo if li isa Core.MethodInstance def = li.def found = def isa Method && def.name in funcsyms end end end end return C_NULL, StackTraces.UNKNOWN end deprecate(m::Module, s::Symbol, flag=1) = ccall(:jl_deprecate_binding, Cvoid, (Any, Any, Cint), m, s, flag) macro deprecate_binding(old, new, export_old=true, dep_message=:nothing, constant=true) dep_message === :nothing && (dep_message = ", use $new instead.") return Expr(:toplevel, export_old ? Expr(:export, esc(old)) : nothing, Expr(:const, Expr(:(=), esc(Symbol(string("_dep_message_",old))), esc(dep_message))), constant ? Expr(:const, Expr(:(=), esc(old), esc(new))) : Expr(:(=), esc(old), esc(new)), Expr(:call, :deprecate, __module__, Expr(:quote, old))) end macro deprecate_stdlib(old, mod, export_old=true, newname=old) rename = old === newname ? "" : " as `$newname`" dep_message = """: it has been moved to the standard library package `$mod`$rename. Add `using $mod` to your imports.""" new = GlobalRef(Base.root_module(Base, mod), newname) return Expr(:toplevel, export_old ? Expr(:export, esc(old)) : nothing, Expr(:const, Expr(:(=), esc(Symbol(string("_dep_message_",old))), esc(dep_message))), Expr(:const, Expr(:(=), esc(old), esc(new))), Expr(:call, :deprecate, __module__, Expr(:quote, old))) end macro deprecate_moved(old, new, export_old=true) eold = esc(old) emsg = string(old, " has been moved to the package ", new, ".jl.\n", "Run `Pkg.add(\"", new, "\")` to install it, restart Julia,\n", "and then run `using ", new, "` to load it.") return Expr(:toplevel, :($eold(args...; kwargs...) = error($emsg)), export_old ? Expr(:export, eold) : nothing, Expr(:call, :deprecate, __module__, Expr(:quote, old), 2)) end # BEGIN 1.0 deprecations @deprecate one(i::CartesianIndex) oneunit(i) @deprecate one(I::Type{CartesianIndex{N}}) where {N} oneunit(I) @deprecate BigFloat(x, prec::Int) BigFloat(x; precision=prec) @deprecate BigFloat(x, prec::Int, rounding::RoundingMode) BigFloat(x, rounding; precision=prec) @deprecate BigFloat(x::Real, prec::Int) BigFloat(x; precision=prec) @deprecate BigFloat(x::Real, prec::Int, rounding::RoundingMode) BigFloat(x, rounding; precision=prec) # END 1.0 deprecations # BEGIN 1.5 deprecations """ isimmutable(v) -> Bool !!! warning Consider using `!ismutable(v)` instead, as `isimmutable(v)` will be replaced by `!ismutable(v)` in a future release. (Since Julia 1.5) Return `true` iff value `v` is immutable. See [Mutable Composite Types](@ref) for a discussion of immutability. Note that this function works on values, so if you give it a type, it will tell you that a value of `DataType` is mutable. # Examples ```jldoctest julia> isimmutable(1) true julia> isimmutable([1,2]) false ``` """ isimmutable(@nospecialize(x)) = !ismutable(x) export isimmutable # Note isimmutable is not @deprecated out of performance concerns macro get!(h, key0, default) f, l = __source__.file, __source__.line @warn "`@get!(dict, key, default)` at $f:$l is deprecated, use `get!(()->default, dict, key)` instead." return quote get!(()->$(esc(default)), $(esc(h)), $(esc(key0))) end end pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::Tuple) = pointer(V, CartesianIndex(is)) # END 1.5 deprecations # BEGIN 1.6 deprecations # These changed from SimpleVector to `MethodMatch`. These definitions emulate # being a SimpleVector to ease transition for packages that make explicit # use of (internal) APIs that return raw method matches. iterate(match::Core.MethodMatch, field::Int=1) = field > nfields(match) ? nothing : (getfield(match, field), field+1) getindex(match::Core.MethodMatch, field::Int) = getfield(match, field) # these were internal functions, but some packages seem to be relying on them tuple_type_head(T::Type) = fieldtype(T, 1) tuple_type_cons(::Type, ::Type{Union{}}) = Union{} function tuple_type_cons(::Type{S}, ::Type{T}) where T<:Tuple where S @_foldable_meta Tuple{S, T.parameters...} end function parameter_upper_bound(t::UnionAll, idx) @_foldable_meta return rewrap_unionall((unwrap_unionall(t)::DataType).parameters[idx], t) end # these were internal functions, but some packages seem to be relying on them @deprecate cat_shape(dims, shape::Tuple{}, shapes::Tuple...) cat_shape(dims, shapes) false cat_shape(dims, shape::Tuple{}) = () # make sure `cat_shape(dims, ())` do not recursively calls itself @deprecate unsafe_indices(A) axes(A) false @deprecate unsafe_length(r) length(r) false # these were internal type aliases, but some packages seem to be relying on them const Any16{N} = Tuple{Any,Any,Any,Any,Any,Any,Any,Any, Any,Any,Any,Any,Any,Any,Any,Any,Vararg{Any,N}} const All16{T,N} = Tuple{T,T,T,T,T,T,T,T, T,T,T,T,T,T,T,T,Vararg{T,N}} # END 1.6 deprecations # BEGIN 1.7 deprecations # the plan is to eventually overload getproperty to access entries of the dict @noinline function getproperty(x::Pairs, s::Symbol) depwarn("use values(kwargs) and keys(kwargs) instead of kwargs.data and kwargs.itr", :getproperty, force=true) return getfield(x, s) end # This function was marked as experimental and not exported. @deprecate catch_stack(task=current_task(); include_bt=true) current_exceptions(task; backtrace=include_bt) false # END 1.7 deprecations # BEGIN 1.8 deprecations const var"@_inline_meta" = var"@inline" const var"@_noinline_meta" = var"@noinline" @deprecate getindex(t::Tuple, i::Real) t[convert(Int, i)] # END 1.8 deprecations # BEGIN 1.9 deprecations # We'd generally like to avoid direct external access to internal fields # Core.Compiler.is_inlineable and Core.Compiler.set_inlineable! move towards this direction, # but we need to keep these around for compat function getproperty(ci::CodeInfo, s::Symbol) s === :inlineable && return Core.Compiler.is_inlineable(ci) return getfield(ci, s) end function setproperty!(ci::CodeInfo, s::Symbol, v) s === :inlineable && return Core.Compiler.set_inlineable!(ci, v) return setfield!(ci, s, convert(fieldtype(CodeInfo, s), v)) end @eval Threads nthreads() = threadpoolsize() @eval Threads begin """ resize_nthreads!(A, copyvalue=A[1]) Resize the array `A` to length [`nthreads()`](@ref). Any new elements that are allocated are initialized to `deepcopy(copyvalue)`, where `copyvalue` defaults to `A[1]`. This is typically used to allocate per-thread variables, and should be called in `__init__` if `A` is a global constant. !!! warning This function is deprecated, since as of Julia v1.9 the number of threads can change at run time. Instead, per-thread state should be created as needed based on the thread id of the caller. """ function resize_nthreads!(A::AbstractVector, copyvalue=A[1]) nthr = nthreads() nold = length(A) resize!(A, nthr) for i = nold+1:nthr A[i] = deepcopy(copyvalue) end return A end end # END 1.9 deprecations # BEGIN 1.10 deprecations """ @pure ex `@pure` gives the compiler a hint for the definition of a pure function, helping for type inference. !!! warning This macro is intended for internal compiler use and may be subject to changes. !!! warning In Julia 1.8 and higher, it is favorable to use [`@assume_effects`](@ref) instead of `@pure`. This is because `@assume_effects` allows a finer grained control over Julia's purity modeling and the effect system enables a wider range of optimizations. """ macro pure(ex) return esc(:(Base.@assume_effects :foldable $ex)) end # END 1.10 deprecations T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/docs/basedocs.jl:# This file is a part of Julia. License is MIT: https://julialang.org/license module BaseDocs @nospecialize # don't specialize on any arguments of the methods declared herein struct Keyword name::Symbol end macro kw_str(text) return Keyword(Symbol(text)) end """ **Welcome to Julia $(string(VERSION)).** The full manual is available at https://docs.julialang.org as well as many great tutorials and learning resources: https://julialang.org/learning/ For help on a specific function or macro, type `?` followed by its name, e.g. `?cos`, or `?@time`, and press enter. Type `;` to enter shell mode, `]` to enter package mode. To exit the interactive session, type `CTRL-D` (press the control key together with the `d` key), or type `exit()`. """ kw"help", kw"Julia", kw"julia", kw"" """ using `using Foo` will load the module or package `Foo` and make its [`export`](@ref)ed names available for direct use. Names can also be used via dot syntax (e.g. `Foo.foo` to access the name `foo`), whether they are `export`ed or not. See the [manual section about modules](@ref modules) for details. """ kw"using" """ import `import Foo` will load the module or package `Foo`. Names from the imported `Foo` module can be accessed with dot syntax (e.g. `Foo.foo` to access the name `foo`). See the [manual section about modules](@ref modules) for details. """ kw"import" """ export `export` is used within modules to tell Julia which functions should be made available to the user. For example: `export foo` makes the name `foo` available when [`using`](@ref) the module. See the [manual section about modules](@ref modules) for details. """ kw"export" """ as `as` is used as a keyword to rename an identifier brought into scope by `import` or `using`, for the purpose of working around name conflicts as well as for shortening names. (Outside of `import` or `using` statements, `as` is not a keyword and can be used as an ordinary identifier.) `import LinearAlgebra as LA` brings the imported `LinearAlgebra` standard library into scope as `LA`. `import LinearAlgebra: eigen as eig, cholesky as chol` brings the `eigen` and `cholesky` methods from `LinearAlgebra` into scope as `eig` and `chol` respectively. `as` works with `using` only when individual identifiers are brought into scope. For example, `using LinearAlgebra: eigen as eig` or `using LinearAlgebra: eigen as eig, cholesky as chol` works, but `using LinearAlgebra as LA` is invalid syntax, since it is nonsensical to rename *all* exported names from `LinearAlgebra` to `LA`. """ kw"as" """ abstract type `abstract type` declares a type that cannot be instantiated, and serves only as a node in the type graph, thereby describing sets of related concrete types: those concrete types which are their descendants. Abstract types form the conceptual hierarchy which makes Juliaโ€™s type system more than just a collection of object implementations. For example: ```julia abstract type Number end abstract type Real <: Number end ``` [`Number`](@ref) has no supertype, whereas [`Real`](@ref) is an abstract subtype of `Number`. """ kw"abstract type", kw"abstract" """ module `module` declares a [`Module`](@ref), which is a separate global variable workspace. Within a module, you can control which names from other modules are visible (via importing), and specify which of your names are intended to be public (via exporting). Modules allow you to create top-level definitions without worrying about name conflicts when your code is used together with somebody elseโ€™s. See the [manual section about modules](@ref modules) for more details. # Examples ```julia module Foo import Base.show export MyType, foo struct MyType x end bar(x) = 2x foo(a::MyType) = bar(a.x) + 1 show(io::IO, a::MyType) = print(io, "MyType \$(a.x)") end ``` """ kw"module" """ __init__ The `__init__()` function in a module executes immediately *after* the module is loaded at runtime for the first time. It is called once, after all other statements in the module have been executed. Because it is called after fully importing the module, `__init__` functions of submodules will be executed first. Two typical uses of `__init__` are calling runtime initialization functions of external C libraries and initializing global constants that involve pointers returned by external libraries. See the [manual section about modules](@ref modules) for more details. # Examples ```julia const foo_data_ptr = Ref{Ptr{Cvoid}}(0) function __init__() ccall((:foo_init, :libfoo), Cvoid, ()) foo_data_ptr[] = ccall((:foo_data, :libfoo), Ptr{Cvoid}, ()) nothing end ``` """ kw"__init__" """ baremodule `baremodule` declares a module that does not contain `using Base` or local definitions of [`eval`](@ref Base.MainInclude.eval) and [`include`](@ref Base.include). It does still import `Core`. In other words, ```julia module Mod ... end ``` is equivalent to ```julia baremodule Mod using Base eval(x) = Core.eval(Mod, x) include(p) = Base.include(Mod, p) ... end ``` """ kw"baremodule" """ primitive type `primitive type` declares a concrete type whose data consists only of a series of bits. Classic examples of primitive types are integers and floating-point values. Some example built-in primitive type declarations: ```julia primitive type Char 32 end primitive type Bool <: Integer 8 end ``` The number after the name indicates how many bits of storage the type requires. Currently, only sizes that are multiples of 8 bits are supported. The [`Bool`](@ref) declaration shows how a primitive type can be optionally declared to be a subtype of some supertype. """ kw"primitive type" """ macro `macro` defines a method for inserting generated code into a program. A macro maps a sequence of argument expressions to a returned expression, and the resulting expression is substituted directly into the program at the point where the macro is invoked. Macros are a way to run generated code without calling [`eval`](@ref Base.MainInclude.eval), since the generated code instead simply becomes part of the surrounding program. Macro arguments may include expressions, literal values, and symbols. Macros can be defined for variable number of arguments (varargs), but do not accept keyword arguments. Every macro also implicitly gets passed the arguments `__source__`, which contains the line number and file name the macro is called from, and `__module__`, which is the module the macro is expanded in. See the manual section on [Metaprogramming](@ref) for more information about how to write a macro. # Examples ```jldoctest julia> macro sayhello(name) return :( println("Hello, ", \$name, "!") ) end @sayhello (macro with 1 method) julia> @sayhello "Charlie" Hello, Charlie! julia> macro saylots(x...) return :( println("Say: ", \$(x...)) ) end @saylots (macro with 1 method) julia> @saylots "hey " "there " "friend" Say: hey there friend ``` """ kw"macro" """ __module__ The argument `__module__` is only visible inside the macro, and it provides information (in the form of a `Module` object) about the expansion context of the macro invocation. See the manual section on [Macro invocation](@ref) for more information. """ kw"__module__" """ __source__ The argument `__source__` is only visible inside the macro, and it provides information (in the form of a `LineNumberNode` object) about the parser location of the `@` sign from the macro invocation. See the manual section on [Macro invocation](@ref) for more information. """ kw"__source__" """ local `local` introduces a new local variable. See the [manual section on variable scoping](@ref scope-of-variables) for more information. # Examples ```jldoctest julia> function foo(n) x = 0 for i = 1:n local x # introduce a loop-local x x = i end x end foo (generic function with 1 method) julia> foo(10) 0 ``` """ kw"local" """ global `global x` makes `x` in the current scope and its inner scopes refer to the global variable of that name. See the [manual section on variable scoping](@ref scope-of-variables) for more information. # Examples ```jldoctest julia> z = 3 3 julia> function foo() global z = 6 # use the z variable defined outside foo end foo (generic function with 1 method) julia> foo() 6 julia> z 6 ``` """ kw"global" """ for outer Reuse an existing local variable for iteration in a `for` loop. See the [manual section on variable scoping](@ref scope-of-variables) for more information. See also [`for`](@ref). # Examples ```jldoctest julia> function f() i = 0 for i = 1:3 # empty end return i end; julia> f() 0 ``` ```jldoctest julia> function f() i = 0 for outer i = 1:3 # empty end return i end; julia> f() 3 ``` ```jldoctest julia> i = 0 # global variable for outer i = 1:3 end ERROR: syntax: no outer local variable declaration exists for "for outer" [...] ``` """ kw"outer" """ ' ' A pair of single-quote characters delimit a [`Char`](@ref) (that is, character) literal. # Examples ```jldoctest julia> 'j' 'j': ASCII/Unicode U+006A (category Ll: Letter, lowercase) ``` """ kw"''" """ = `=` is the assignment operator. * For variable `a` and expression `b`, `a = b` makes `a` refer to the value of `b`. * For functions `f(x)`, `f(x) = x` defines a new function constant `f`, or adds a new method to `f` if `f` is already defined; this usage is equivalent to `function f(x); x; end`. * `a[i] = v` calls [`setindex!`](@ref)`(a,v,i)`. * `a.b = c` calls [`setproperty!`](@ref)`(a,:b,c)`. * Inside a function call, `f(a=b)` passes `b` as the value of keyword argument `a`. * Inside parentheses with commas, `(a=1,)` constructs a [`NamedTuple`](@ref). # Examples Assigning `a` to `b` does not create a copy of `b`; instead use [`copy`](@ref) or [`deepcopy`](@ref). ```jldoctest julia> b = [1]; a = b; b[1] = 2; a 1-element Array{Int64, 1}: 2 julia> b = [1]; a = copy(b); b[1] = 2; a 1-element Array{Int64, 1}: 1 ``` Collections passed to functions are also not copied. Functions can modify (mutate) the contents of the objects their arguments refer to. (The names of functions which do this are conventionally suffixed with '!'.) ```jldoctest julia> function f!(x); x[:] .+= 1; end f! (generic function with 1 method) julia> a = [1]; f!(a); a 1-element Array{Int64, 1}: 2 ``` Assignment can operate on multiple variables in parallel, taking values from an iterable: ```jldoctest julia> a, b = 4, 5 (4, 5) julia> a, b = 1:3 1:3 julia> a, b (1, 2) ``` Assignment can operate on multiple variables in series, and will return the value of the right-hand-most expression: ```jldoctest julia> a = [1]; b = [2]; c = [3]; a = b = c 1-element Array{Int64, 1}: 3 julia> b[1] = 2; a, b, c ([2], [2], [2]) ``` Assignment at out-of-bounds indices does not grow a collection. If the collection is a [`Vector`](@ref) it can instead be grown with [`push!`](@ref) or [`append!`](@ref). ```jldoctest julia> a = [1, 1]; a[3] = 2 ERROR: BoundsError: attempt to access 2-element Array{Int64, 1} at index [3] [...] julia> push!(a, 2, 3) 4-element Array{Int64, 1}: 1 1 2 3 ``` Assigning `[]` does not eliminate elements from a collection; instead use [`filter!`](@ref). ```jldoctest julia> a = collect(1:3); a[a .<= 1] = [] ERROR: DimensionMismatch: tried to assign 0 elements to 1 destinations [...] julia> filter!(x -> x > 1, a) # in-place & thus more efficient than a = a[a .> 1] 2-element Array{Int64, 1}: 2 3 ``` """ kw"=" """ .= Perform broadcasted assignment. The right-side argument is expanded as in [`broadcast`](@ref) and then assigned into the left-side argument in-place. Fuses with other dotted operators in the same expression; i.e. the whole assignment expression is converted into a single loop. `A .= B` is similar to `broadcast!(identity, A, B)`. # Examples ```jldoctest julia> A = zeros(4, 4); B = [1, 2, 3, 4]; julia> A .= B 4ร—4 Array{Float64, 2}: 1.0 1.0 1.0 1.0 2.0 2.0 2.0 2.0 3.0 3.0 3.0 3.0 4.0 4.0 4.0 4.0 julia> A 4ร—4 Array{Float64, 2}: 1.0 1.0 1.0 1.0 2.0 2.0 2.0 2.0 3.0 3.0 3.0 3.0 4.0 4.0 4.0 4.0 ``` """ kw".=" """ . The dot operator is used to access fields or properties of objects and access variables defined inside modules. In general, `a.b` calls `getproperty(a, :b)` (see [`getproperty`](@ref Base.getproperty)). # Examples ```jldoctest julia> z = 1 + 2im; z.im 2 julia> Iterators.product product (generic function with 1 method) ``` """ kw"." """ let `let` blocks create a new hard scope and optionally introduce new local bindings. Just like the [other scope constructs](@ref man-scope-table), `let` blocks define the block of code where newly introduced local variables are accessible. Additionally, the syntax has a special meaning for comma-separated assignments and variable names that may optionally appear on the same line as the `let`: ```julia let var1 = value1, var2, var3 = value3 code end ``` The variables introduced on this line are local to the `let` block and the assignments are evaluated in order, with each right-hand side evaluated in the scope without considering the name on the left-hand side. Therefore it makes sense to write something like `let x = x`, since the two `x` variables are distinct with the left-hand side locally shadowing the `x` from the outer scope. This can even be a useful idiom as new local variables are freshly created each time local scopes are entered, but this is only observable in the case of variables that outlive their scope via closures. A `let` variable without an assignment, such as `var2` in the example above, declares a new local variable that is not yet bound to a value. By contrast, [`begin`](@ref) blocks also group multiple expressions together but do not introduce scope or have the special assignment syntax. ### Examples In the function below, there is a single `x` that is iteratively updated three times by the `map`. The closures returned all reference that one `x` at its final value: ```jldoctest julia> function test_outer_x() x = 0 map(1:3) do _ x += 1 return ()->x end end test_outer_x (generic function with 1 method) julia> [f() for f in test_outer_x()] 3-element Vector{Int64}: 3 3 3 ``` If, however, we add a `let` block that introduces a _new_ local variable we will end up with three distinct variables being captured (one at each iteration) even though we chose to use (shadow) the same name. ```jldoctest julia> function test_let_x() x = 0 map(1:3) do _ x += 1 let x = x return ()->x end end end test_let_x (generic function with 1 method) julia> [f() for f in test_let_x()] 3-element Vector{Int64}: 1 2 3 ``` All scope constructs that introduce new local variables behave this way when repeatedly run; the distinctive feature of `let` is its ability to succinctly declare new `local`s that may shadow outer variables of the same name. For example, directly using the argument of the `do` function similarly captures three distinct variables: ```jldoctest julia> function test_do_x() map(1:3) do x return ()->x end end test_do_x (generic function with 1 method) julia> [f() for f in test_do_x()] 3-element Vector{Int64}: 1 2 3 ``` """ kw"let" """ quote `quote` creates multiple expression objects in a block without using the explicit [`Expr`](@ref) constructor. For example: ```julia ex = quote x = 1 y = 2 x + y end ``` Unlike the other means of quoting, `:( ... )`, this form introduces `QuoteNode` elements to the expression tree, which must be considered when directly manipulating the tree. For other purposes, `:( ... )` and `quote .. end` blocks are treated identically. """ kw"quote" """ @ The at sign followed by a macro name marks a macro call. Macros provide the ability to include generated code in the final body of a program. A macro maps a tuple of arguments, expressed as space-separated expressions or a function-call-like argument list, to a returned *expression*. The resulting expression is compiled directly into the surrounding code. See [Metaprogramming](@ref man-macros) for more details and examples. """ kw"@" """ {} Curly braces are used to specify [type parameters](@ref man-parametric-types). Type parameters allow a single type declaration to introduce a whole family of new types โ€” one for each possible combination of parameter values. For example, the [`Set`](@ref) type describes many possible types of sets; it uses one type parameter to describe the type of the elements it contains. The specific _parameterized_ types `Set{Float64}` and `Set{Int64}` describe two _concrete_ types: both are subtypes ([`<:`](@ref)) of `Set`, but the former has `Float64` elements and the latter has `Int64` elements. """ kw"{", kw"{}", kw"}" """ [] Square braces are used for [indexing](@ref man-array-indexing), [indexed assignment](@ref man-indexed-assignment), [array literals](@ref man-array-literals), and [array comprehensions](@ref man-comprehensions). """ kw"[", kw"[]", kw"]" """ () Parentheses are used to group expressions, call functions, and construct [tuples](@ref Tuple) and [named tuples](@ref NamedTuple). """ kw"(", kw"()", kw")" """ # The number sign (or hash) character is used to begin a single-line comment. """ kw"#" """ #= =# A multi-line comment begins with `#=` and ends with `=#`, and may be nested. """ kw"#=", kw"=#" """ ; Semicolons are used as statement separators and mark the beginning of keyword arguments in function declarations or calls. """ kw";" """ Expr(head::Symbol, args...) A type representing compound expressions in parsed julia code (ASTs). Each expression consists of a `head` `Symbol` identifying which kind of expression it is (e.g. a call, for loop, conditional statement, etc.), and subexpressions (e.g. the arguments of a call). The subexpressions are stored in a `Vector{Any}` field called `args`. See the manual chapter on [Metaprogramming](@ref) and the developer documentation [Julia ASTs](@ref). # Examples ```jldoctest julia> Expr(:call, :+, 1, 2) :(1 + 2) julia> dump(:(a ? b : c)) Expr head: Symbol if args: Array{Any}((3,)) 1: Symbol a 2: Symbol b 3: Symbol c ``` """ Expr """ :expr Quote an expression `expr`, returning the abstract syntax tree (AST) of `expr`. The AST may be of type `Expr`, `Symbol`, or a literal value. The syntax `:identifier` evaluates to a `Symbol`. See also: [`Expr`](@ref), [`Symbol`](@ref), [`Meta.parse`](@ref) # Examples ```jldoctest julia> expr = :(a = b + 2*x) :(a = b + 2x) julia> sym = :some_identifier :some_identifier julia> value = :0xff 0xff julia> typeof((expr, sym, value)) Tuple{Expr, Symbol, UInt8} ``` """ (:) """ \$ Interpolation operator for interpolating into e.g. [strings](@ref string-interpolation) and [expressions](@ref man-expression-interpolation). # Examples ```jldoctest julia> name = "Joe" "Joe" julia> "My name is \$name." "My name is Joe." ``` """ kw"$" """ const `const` is used to declare global variables whose values will not change. In almost all code (and particularly performance sensitive code) global variables should be declared constant in this way. ```julia const x = 5 ``` Multiple variables can be declared within a single `const`: ```julia const y, z = 7, 11 ``` Note that `const` only applies to one `=` operation, therefore `const x = y = 1` declares `x` to be constant but not `y`. On the other hand, `const x = const y = 1` declares both `x` and `y` constant. Note that "constant-ness" does not extend into mutable containers; only the association between a variable and its value is constant. If `x` is an array or dictionary (for example) you can still modify, add, or remove elements. In some cases changing the value of a `const` variable gives a warning instead of an error. However, this can produce unpredictable behavior or corrupt the state of your program, and so should be avoided. This feature is intended only for convenience during interactive use. """ kw"const" """ function Functions are defined with the `function` keyword: ```julia function add(a, b) return a + b end ``` Or the short form notation: ```julia add(a, b) = a + b ``` The use of the [`return`](@ref) keyword is exactly the same as in other languages, but is often optional. A function without an explicit `return` statement will return the last expression in the function body. """ kw"function" """ x -> y Create an anonymous function mapping argument(s) `x` to the function body `y`. ```jldoctest julia> f = x -> x^2 + 2x - 1 #1 (generic function with 1 method) julia> f(2) 7 ``` Anonymous functions can also be defined for multiple arguments. ```jldoctest julia> g = (x,y) -> x^2 + y^2 #2 (generic function with 1 method) julia> g(2,3) 13 ``` See the manual section on [anonymous functions](@ref man-anonymous-functions) for more details. """ kw"->" """ return `return x` causes the enclosing function to exit early, passing the given value `x` back to its caller. `return` by itself with no value is equivalent to `return nothing` (see [`nothing`](@ref)). ```julia function compare(a, b) a == b && return "equal to" a < b ? "less than" : "greater than" end ``` In general you can place a `return` statement anywhere within a function body, including within deeply nested loops or conditionals, but be careful with `do` blocks. For example: ```julia function test1(xs) for x in xs iseven(x) && return 2x end end function test2(xs) map(xs) do x iseven(x) && return 2x x end end ``` In the first example, the return breaks out of `test1` as soon as it hits an even number, so `test1([5,6,7])` returns `12`. You might expect the second example to behave the same way, but in fact the `return` there only breaks out of the *inner* function (inside the `do` block) and gives a value back to `map`. `test2([5,6,7])` then returns `[5,12,7]`. When used in a top-level expression (i.e. outside any function), `return` causes the entire current top-level expression to terminate early. """ kw"return" """ if/elseif/else `if`/`elseif`/`else` performs conditional evaluation, which allows portions of code to be evaluated or not evaluated depending on the value of a boolean expression. Here is the anatomy of the `if`/`elseif`/`else` conditional syntax: ```julia if x < y println("x is less than y") elseif x > y println("x is greater than y") else println("x is equal to y") end ``` If the condition expression `x < y` is true, then the corresponding block is evaluated; otherwise the condition expression `x > y` is evaluated, and if it is true, the corresponding block is evaluated; if neither expression is true, the `else` block is evaluated. The `elseif` and `else` blocks are optional, and as many `elseif` blocks as desired can be used. In contrast to some other languages conditions must be of type `Bool`. It does not suffice for conditions to be convertible to `Bool`. ```jldoctest julia> if 1 end ERROR: TypeError: non-boolean (Int64) used in boolean context ``` """ kw"if", kw"elseif", kw"else" """ a ? b : c Short form for conditionals; read "if `a`, evaluate `b` otherwise evaluate `c`". Also known as the [ternary operator](https://en.wikipedia.org/wiki/%3F:). This syntax is equivalent to `if a; b else c end`, but is often used to emphasize the value `b`-or-`c` which is being used as part of a larger expression, rather than the side effects that evaluating `b` or `c` may have. See the manual section on [control flow](@ref man-conditional-evaluation) for more details. # Examples ``` julia> x = 1; y = 2; julia> x > y ? println("x is larger") : println("y is larger") y is larger ``` """ kw"?", kw"?:" """ for `for` loops repeatedly evaluate a block of statements while iterating over a sequence of values. The iteration variable is always a new variable, even if a variable of the same name exists in the enclosing scope. Use [`outer`](@ref) to reuse an existing local variable for iteration. # Examples ```jldoctest julia> for i in [1, 4, 0] println(i) end 1 4 0 ``` """ kw"for" """ while `while` loops repeatedly evaluate a conditional expression, and continue evaluating the body of the while loop as long as the expression remains true. If the condition expression is false when the while loop is first reached, the body is never evaluated. # Examples ```jldoctest julia> i = 1 1 julia> while i < 5 println(i) global i += 1 end 1 2 3 4 ``` """ kw"while" """ end `end` marks the conclusion of a block of expressions, for example [`module`](@ref), [`struct`](@ref), [`mutable struct`](@ref), [`begin`](@ref), [`let`](@ref), [`for`](@ref) etc. `end` may also be used when indexing to represent the last index of a collection or the last index of a dimension of an array. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Array{Int64, 2}: 1 2 3 4 julia> A[end, :] 2-element Array{Int64, 1}: 3 4 ``` """ kw"end" """ try/catch A `try`/`catch` statement allows intercepting errors (exceptions) thrown by [`throw`](@ref) so that program execution can continue. For example, the following code attempts to write a file, but warns the user and proceeds instead of terminating execution if the file cannot be written: ```julia try open("/danger", "w") do f println(f, "Hello") end catch @warn "Could not write file." end ``` or, when the file cannot be read into a variable: ```julia lines = try open("/danger", "r") do f readlines(f) end catch @warn "File not found." end ``` The syntax `catch e` (where `e` is any variable) assigns the thrown exception object to the given variable within the `catch` block. The power of the `try`/`catch` construct lies in the ability to unwind a deeply nested computation immediately to a much higher level in the stack of calling functions. """ kw"try", kw"catch" """ finally Run some code when a given block of code exits, regardless of how it exits. For example, here is how we can guarantee that an opened file is closed: ```julia f = open("file") try operate_on_file(f) finally close(f) end ``` When control leaves the [`try`](@ref) block (for example, due to a [`return`](@ref), or just finishing normally), [`close(f)`](@ref) will be executed. If the `try` block exits due to an exception, the exception will continue propagating. A `catch` block may be combined with `try` and `finally` as well. In this case the `finally` block will run after `catch` has handled the error. """ kw"finally" """ break Break out of a loop immediately. # Examples ```jldoctest julia> i = 0 0 julia> while true global i += 1 i > 5 && break println(i) end 1 2 3 4 5 ``` """ kw"break" """ continue Skip the rest of the current loop iteration. # Examples ```jldoctest julia> for i = 1:6 iseven(i) && continue println(i) end 1 3 5 ``` """ kw"continue" """ do Create an anonymous function and pass it as the first argument to a function call. For example: ```julia map(1:10) do x 2x end ``` is equivalent to `map(x->2x, 1:10)`. Use multiple arguments like so: ```julia map(1:10, 11:20) do x, y x + y end ``` """ kw"do" """ ... The "splat" operator, `...`, represents a sequence of arguments. `...` can be used in function definitions, to indicate that the function accepts an arbitrary number of arguments. `...` can also be used to apply a function to a sequence of arguments. # Examples ```jldoctest julia> add(xs...) = reduce(+, xs) add (generic function with 1 method) julia> add(1, 2, 3, 4, 5) 15 julia> add([1, 2, 3]...) 6 julia> add(7, 1:100..., 1000:1100...) 111107 ``` """ kw"..." """ ; `;` has a similar role in Julia as in many C-like languages, and is used to delimit the end of the previous statement. `;` is not necessary at the end of a line, but can be used to separate statements on a single line or to join statements into a single expression. Adding `;` at the end of a line in the REPL will suppress printing the result of that expression. In function declarations, and optionally in calls, `;` separates regular arguments from keywords. In array literals, arguments separated by semicolons have their contents concatenated together. A separator made of a single `;` concatenates vertically (i.e. along the first dimension), `;;` concatenates horizontally (second dimension), `;;;` concatenates along the third dimension, etc. Such a separator can also be used in last position in the square brackets to add trailing dimensions of length 1. A `;` in first position inside of parentheses can be used to construct a named tuple. The same `(; ...)` syntax on the left side of an assignment allows for property destructuring. In the standard REPL, typing `;` on an empty line will switch to shell mode. # Examples ```jldoctest julia> function foo() x = "Hello, "; x *= "World!" return x end foo (generic function with 1 method) julia> bar() = (x = "Hello, Mars!"; return x) bar (generic function with 1 method) julia> foo(); julia> bar() "Hello, Mars!" julia> function plot(x, y; style="solid", width=1, color="black") ### end julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> [1; 3;; 2; 4;;; 10*A] 2ร—2ร—2 Array{Int64, 3}: [:, :, 1] = 1 2 3 4 [:, :, 2] = 10 20 30 40 julia> [2; 3;;;] 2ร—1ร—1 Array{Int64, 3}: [:, :, 1] = 2 3 julia> nt = (; x=1) # without the ; or a trailing comma this would assign to x (x = 1,) julia> key = :a; c = 3; julia> nt2 = (; key => 1, b=2, c, nt.x) (a = 1, b = 2, c = 3, x = 1) julia> (; b, x) = nt2; # set variables b and x using property destructuring julia> b, x (2, 1) julia> ; # upon typing ;, the prompt changes (in place) to: shell> shell> echo hello hello ``` """ kw";" """ x && y Short-circuiting boolean AND. See also [`&`](@ref), the ternary operator `? :`, and the manual section on [control flow](@ref man-conditional-evaluation). # Examples ```jldoctest julia> x = 3; julia> x > 1 && x < 10 && x isa Int true julia> x < 0 && error("expected positive x") false ``` """ kw"&&" """ x || y Short-circuiting boolean OR. See also: [`|`](@ref), [`xor`](@ref), [`&&`](@ref). # Examples ```jldoctest julia> pi < 3 || โ„ฏ < 3 true julia> false || true || println("neither is true!") true ``` """ kw"||" """ ccall((function_name, library), returntype, (argtype1, ...), argvalue1, ...) ccall(function_name, returntype, (argtype1, ...), argvalue1, ...) ccall(function_pointer, returntype, (argtype1, ...), argvalue1, ...) Call a function in a C-exported shared library, specified by the tuple `(function_name, library)`, where each component is either a string or symbol. Instead of specifying a library, one can also use a `function_name` symbol or string, which is resolved in the current process. Alternatively, `ccall` may also be used to call a function pointer `function_pointer`, such as one returned by `dlsym`. Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression. Each `argvalue` to the `ccall` will be converted to the corresponding `argtype`, by automatic insertion of calls to `unsafe_convert(argtype, cconvert(argtype, argvalue))`. (See also the documentation for [`unsafe_convert`](@ref Base.unsafe_convert) and [`cconvert`](@ref Base.cconvert) for further details.) In most cases, this simply results in a call to `convert(argtype, argvalue)`. """ kw"ccall" """ llvmcall(fun_ir::String, returntype, Tuple{argtype1, ...}, argvalue1, ...) llvmcall((mod_ir::String, entry_fn::String), returntype, Tuple{argtype1, ...}, argvalue1, ...) llvmcall((mod_bc::Vector{UInt8}, entry_fn::String), returntype, Tuple{argtype1, ...}, argvalue1, ...) Call the LLVM code provided in the first argument. There are several ways to specify this first argument: - as a literal string, representing function-level IR (similar to an LLVM `define` block), with arguments are available as consecutive unnamed SSA variables (%0, %1, etc.); - as a 2-element tuple, containing a string of module IR and a string representing the name of the entry-point function to call; - as a 2-element tuple, but with the module provided as an `Vector{UInt8}` with bitcode. Note that contrary to `ccall`, the argument types must be specified as a tuple type, and not a tuple of types. All types, as well as the LLVM code, should be specified as literals, and not as variables or expressions (it may be necessary to use `@eval` to generate these literals). See `test/llvmcall.jl` for usage examples. """ Core.Intrinsics.llvmcall """ begin `begin...end` denotes a block of code. ```julia begin println("Hello, ") println("World!") end ``` Usually `begin` will not be necessary, since keywords such as [`function`](@ref) and [`let`](@ref) implicitly begin blocks of code. See also [`;`](@ref). `begin` may also be used when indexing to represent the first index of a collection or the first index of a dimension of an array. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Array{Int64,2}: 1 2 3 4 julia> A[begin, :] 2-element Array{Int64,1}: 1 2 ``` """ kw"begin" """ struct The most commonly used kind of type in Julia is a struct, specified as a name and a set of fields. ```julia struct Point x y end ``` Fields can have type restrictions, which may be parameterized: ```julia struct Point{X} x::X y::Float64 end ``` A struct can also declare an abstract super type via `<:` syntax: ```julia struct Point <: AbstractPoint x y end ``` `struct`s are immutable by default; an instance of one of these types cannot be modified after construction. Use [`mutable struct`](@ref) instead to declare a type whose instances can be modified. See the manual section on [Composite Types](@ref) for more details, such as how to define constructors. """ kw"struct" """ mutable struct `mutable struct` is similar to [`struct`](@ref), but additionally allows the fields of the type to be set after construction. See the manual section on [Composite Types](@ref) for more information. """ kw"mutable struct" """ new, or new{A,B,...} Special function available to inner constructors which creates a new object of the type. The form new{A,B,...} explicitly specifies values of parameters for parametric types. See the manual section on [Inner Constructor Methods](@ref man-inner-constructor-methods) for more information. """ kw"new" """ where The `where` keyword creates a type that is an iterated union of other types, over all values of some variable. For example `Vector{T} where T<:Real` includes all [`Vector`](@ref)s where the element type is some kind of `Real` number. The variable bound defaults to [`Any`](@ref) if it is omitted: ```julia Vector{T} where T # short for `where T<:Any` ``` Variables can also have lower bounds: ```julia Vector{T} where T>:Int Vector{T} where Int<:T<:Real ``` There is also a concise syntax for nested `where` expressions. For example, this: ```julia Pair{T, S} where S<:Array{T} where T<:Number ``` can be shortened to: ```julia Pair{T, S} where {T<:Number, S<:Array{T}} ``` This form is often found on method signatures. Note that in this form, the variables are listed outermost-first. This matches the order in which variables are substituted when a type is "applied" to parameter values using the syntax `T{p1, p2, ...}`. """ kw"where" """ var The syntax `var"#example#"` refers to a variable named `Symbol("#example#")`, even though `#example#` is not a valid Julia identifier name. This can be useful for interoperability with programming languages which have different rules for the construction of valid identifiers. For example, to refer to the `R` variable `draw.segments`, you can use `var"draw.segments"` in your Julia code. It is also used to `show` julia source code which has gone through macro hygiene or otherwise contains variable names which can't be parsed normally. Note that this syntax requires parser support so it is expanded directly by the parser rather than being implemented as a normal string macro `@var_str`. !!! compat "Julia 1.3" This syntax requires at least Julia 1.3. """ kw"var\"name\"", kw"@var_str" """ devnull Used in a stream redirect to discard all data written to it. Essentially equivalent to `/dev/null` on Unix or `NUL` on Windows. Usage: ```julia run(pipeline(`cat test.txt`, devnull)) ``` """ devnull # doc strings for code in boot.jl and built-ins """ Nothing A type with no fields that is the type of [`nothing`](@ref). See also: [`isnothing`](@ref), [`Some`](@ref), [`Missing`](@ref). """ Nothing """ nothing The singleton instance of type [`Nothing`](@ref), used by convention when there is no value to return (as in a C `void` function) or when a variable or field holds no value. See also: [`isnothing`](@ref), [`something`](@ref), [`missing`](@ref). """ nothing """ Core.TypeofBottom The singleton type containing only the value `Union{}` (which represents the empty type). """ Core.TypeofBottom """ Core.Type{T} `Core.Type` is an abstract type which has all type objects as its instances. The only instance of the singleton type `Core.Type{T}` is the object `T`. # Examples ```jldoctest julia> isa(Type{Float64}, Type) true julia> isa(Float64, Type) true julia> isa(Real, Type{Float64}) false julia> isa(Real, Type{Real}) true ``` """ Core.Type """ DataType <: Type{T} `DataType` represents explicitly declared types that have names, explicitly declared supertypes, and, optionally, parameters. Every concrete value in the system is an instance of some `DataType`. # Examples ```jldoctest julia> typeof(Real) DataType julia> typeof(Int) DataType julia> struct Point x::Int y end julia> typeof(Point) DataType ``` """ Core.DataType """ Function Abstract type of all functions. # Examples ```jldoctest julia> isa(+, Function) true julia> typeof(sin) typeof(sin) (singleton type of function sin, subtype of Function) julia> ans <: Function true ``` """ Function """ ReadOnlyMemoryError() An operation tried to write to memory that is read-only. """ ReadOnlyMemoryError """ ErrorException(msg) Generic error type. The error message, in the `.msg` field, may provide more specific details. # Examples ```jldoctest julia> ex = ErrorException("I've done a bad thing"); julia> ex.msg "I've done a bad thing" ``` """ ErrorException """ WrappedException(msg) Generic type for `Exception`s wrapping another `Exception`, such as `LoadError` and `InitError`. Those exceptions contain information about the root cause of an exception. Subtypes define a field `error` containing the causing `Exception`. """ Core.WrappedException """ UndefRefError() The item or field is not defined for the given object. # Examples ```jldoctest julia> struct MyType a::Vector{Int} MyType() = new() end julia> A = MyType() MyType(#undef) julia> A.a ERROR: UndefRefError: access to undefined reference Stacktrace: [...] ``` """ UndefRefError """ Float32(x [, mode::RoundingMode]) Create a `Float32` from `x`. If `x` is not exactly representable then `mode` determines how `x` is rounded. # Examples ```jldoctest julia> Float32(1/3, RoundDown) 0.3333333f0 julia> Float32(1/3, RoundUp) 0.33333334f0 ``` See [`RoundingMode`](@ref) for available rounding modes. """ Float32(x) """ Float64(x [, mode::RoundingMode]) Create a `Float64` from `x`. If `x` is not exactly representable then `mode` determines how `x` is rounded. # Examples ```jldoctest julia> Float64(pi, RoundDown) 3.141592653589793 julia> Float64(pi, RoundUp) 3.1415926535897936 ``` See [`RoundingMode`](@ref) for available rounding modes. """ Float64(x) """ OutOfMemoryError() An operation allocated too much memory for either the system or the garbage collector to handle properly. """ OutOfMemoryError """ BoundsError([a],[i]) An indexing operation into an array, `a`, tried to access an out-of-bounds element at index `i`. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> A = fill(1.0, 7); julia> A[8] ERROR: BoundsError: attempt to access 7-element Vector{Float64} at index [8] julia> B = fill(1.0, (2,3)); julia> B[2, 4] ERROR: BoundsError: attempt to access 2ร—3 Matrix{Float64} at index [2, 4] julia> B[9] ERROR: BoundsError: attempt to access 2ร—3 Matrix{Float64} at index [9] ``` """ BoundsError """ InexactError(name::Symbol, T, val) Cannot exactly convert `val` to type `T` in a method of function `name`. # Examples ```jldoctest julia> convert(Float64, 1+2im) ERROR: InexactError: Float64(1 + 2im) Stacktrace: [...] ``` """ InexactError """ DomainError(val) DomainError(val, msg) The argument `val` to a function or constructor is outside the valid domain. # Examples ```jldoctest julia> sqrt(-1) ERROR: DomainError with -1.0: sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). Stacktrace: [...] ``` """ DomainError """ Task(func) Create a `Task` (i.e. coroutine) to execute the given function `func` (which must be callable with no arguments). The task exits when this function returns. The task will run in the "world age" from the parent at construction when [`schedule`](@ref)d. !!! warning By default tasks will have the sticky bit set to true `t.sticky`. This models the historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread they are first scheduled on. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky bit manually to `false`. # Examples ```jldoctest julia> a() = sum(i for i in 1:1000); julia> b = Task(a); ``` In this example, `b` is a runnable `Task` that hasn't started yet. """ Task """ StackOverflowError() The function call grew beyond the size of the call stack. This usually happens when a call recurses infinitely. """ StackOverflowError """ nfields(x) -> Int Get the number of fields in the given object. # Examples ```jldoctest julia> a = 1//2; julia> nfields(a) 2 julia> b = 1 1 julia> nfields(b) 0 julia> ex = ErrorException("I've done a bad thing"); julia> nfields(ex) 1 ``` In these examples, `a` is a [`Rational`](@ref), which has two fields. `b` is an `Int`, which is a primitive bitstype with no fields at all. `ex` is an [`ErrorException`](@ref), which has one field. """ nfields """ UndefVarError(var::Symbol) A symbol in the current scope is not defined. # Examples ```jldoctest julia> a ERROR: UndefVarError: `a` not defined julia> a = 1; julia> a 1 ``` """ UndefVarError """ UndefKeywordError(var::Symbol) The required keyword argument `var` was not assigned in a function call. # Examples ```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" julia> function my_func(;my_arg) return my_arg + 1 end my_func (generic function with 1 method) julia> my_func() ERROR: UndefKeywordError: keyword argument `my_arg` not assigned Stacktrace: [1] my_func() at ./REPL[1]:2 [2] top-level scope at REPL[2]:1 ``` """ UndefKeywordError """ OverflowError(msg) The result of an expression is too large for the specified type and will cause a wraparound. """ OverflowError """ TypeError(func::Symbol, context::AbstractString, expected::Type, got) A type assertion failure, or calling an intrinsic function with an incorrect argument type. """ TypeError """ InterruptException() The process was stopped by a terminal interrupt (CTRL+C). Note that, in Julia script started without `-i` (interactive) option, `InterruptException` is not thrown by default. Calling [`Base.exit_on_sigint(false)`](@ref Base.exit_on_sigint) in the script can recover the behavior of the REPL. Alternatively, a Julia script can be started with ```sh julia -e "include(popfirst!(ARGS))" script.jl ``` to let `InterruptException` be thrown by CTRL+C during the execution. """ InterruptException """ applicable(f, args...) -> Bool Determine whether the given generic function has a method applicable to the given arguments. See also [`hasmethod`](@ref). # Examples ```jldoctest julia> function f(x, y) x + y end; julia> applicable(f, 1) false julia> applicable(f, 1, 2) true ``` """ applicable """ invoke(f, argtypes::Type, args...; kwargs...) Invoke a method for the given generic function `f` matching the specified types `argtypes` on the specified arguments `args` and passing the keyword arguments `kwargs`. The arguments `args` must conform with the specified types in `argtypes`, i.e. conversion is not automatically performed. This method allows invoking a method other than the most specific matching method, which is useful when the behavior of a more general definition is explicitly needed (often as part of the implementation of a more specific method of the same function). Be careful when using `invoke` for functions that you don't write. What definition is used for given `argtypes` is an implementation detail unless the function is explicitly states that calling with certain `argtypes` is a part of public API. For example, the change between `f1` and `f2` in the example below is usually considered compatible because the change is invisible by the caller with a normal (non-`invoke`) call. However, the change is visible if you use `invoke`. # Examples ```jldoctest julia> f(x::Real) = x^2; julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); julia> f(2) 5 julia> f1(::Integer) = Integer f1(::Real) = Real; julia> f2(x::Real) = _f2(x) _f2(::Integer) = Integer _f2(_) = Real; julia> f1(1) Integer julia> f2(1) Integer julia> invoke(f1, Tuple{Real}, 1) Real julia> invoke(f2, Tuple{Real}, 1) Integer ``` """ invoke """ isa(x, type) -> Bool Determine whether `x` is of the given `type`. Can also be used as an infix operator, e.g. `x isa type`. # Examples ```jldoctest julia> isa(1, Int) true julia> isa(1, Matrix) false julia> isa(1, Char) false julia> isa(1, Number) true julia> 1 isa Number true ``` """ isa """ DivideError() Integer division was attempted with a denominator value of 0. # Examples ```jldoctest julia> 2/0 Inf julia> div(2, 0) ERROR: DivideError: integer division error Stacktrace: [...] ``` """ DivideError """ Number Abstract supertype for all number types. """ Number """ Real <: Number Abstract supertype for all real numbers. """ Real """ AbstractFloat <: Real Abstract supertype for all floating point numbers. """ AbstractFloat """ Integer <: Real Abstract supertype for all integers. """ Integer """ Signed <: Integer Abstract supertype for all signed integers. """ Signed """ Unsigned <: Integer Abstract supertype for all unsigned integers. """ Unsigned """ Bool <: Integer Boolean type, containing the values `true` and `false`. `Bool` is a kind of number: `false` is numerically equal to `0` and `true` is numerically equal to `1`. Moreover, `false` acts as a multiplicative "strong zero": ```jldoctest julia> false == 0 true julia> true == 1 true julia> 0 * NaN NaN julia> false * NaN 0.0 ``` See also: [`digits`](@ref), [`iszero`](@ref), [`NaN`](@ref). """ Bool for (bit, sign, exp, frac) in ((16, 1, 5, 10), (32, 1, 8, 23), (64, 1, 11, 52)) @eval begin """ Float$($bit) <: AbstractFloat $($bit)-bit floating point number type (IEEE 754 standard). Binary format: $($sign) sign, $($exp) exponent, $($frac) fraction bits. """ $(Symbol("Float", bit)) end end for bit in (8, 16, 32, 64, 128) @eval begin """ Int$($bit) <: Signed $($bit)-bit signed integer type. """ $(Symbol("Int", bit)) """ UInt$($bit) <: Unsigned $($bit)-bit unsigned integer type. """ $(Symbol("UInt", bit)) end end """ Symbol The type of object used to represent identifiers in parsed julia code (ASTs). Also often used as a name or label to identify an entity (e.g. as a dictionary key). `Symbol`s can be entered using the `:` quote operator: ```jldoctest julia> :name :name julia> typeof(:name) Symbol julia> x = 42 42 julia> eval(:x) 42 ``` `Symbol`s can also be constructed from strings or other values by calling the constructor `Symbol(x...)`. `Symbol`s are immutable and their implementation re-uses the same object for all `Symbol`s with the same name. Unlike strings, `Symbol`s are "atomic" or "scalar" entities that do not support iteration over characters. """ Symbol """ Symbol(x...) -> Symbol Create a [`Symbol`](@ref) by concatenating the string representations of the arguments together. # Examples ```jldoctest julia> Symbol("my", "name") :myname julia> Symbol("day", 4) :day4 ``` """ Symbol(x...) """ tuple(xs...) Construct a tuple of the given objects. See also [`Tuple`](@ref), [`ntuple`](@ref), [`NamedTuple`](@ref). # Examples ```jldoctest julia> tuple(1, 'b', pi) (1, 'b', ฯ€) julia> ans === (1, 'b', ฯ€) true julia> Tuple(Real[1, 2, pi]) # takes a collection (1, 2, ฯ€) ``` """ tuple """ getfield(value, name::Symbol, [order::Symbol]) getfield(value, i::Int, [order::Symbol]) Extract a field from a composite `value` by name or position. Optionally, an ordering can be defined for the operation. If the field was declared `@atomic`, the specification is strongly recommended to be compatible with the stores to that location. Otherwise, if not declared as `@atomic`, this parameter must be `:not_atomic` if specified. See also [`getproperty`](@ref Base.getproperty) and [`fieldnames`](@ref). # Examples ```jldoctest julia> a = 1//2 1//2 julia> getfield(a, :num) 1 julia> a.num 1 julia> getfield(a, 1) 1 ``` """ getfield """ setfield!(value, name::Symbol, x, [order::Symbol]) setfield!(value, i::Int, x, [order::Symbol]) Assign `x` to a named field in `value` of composite type. The `value` must be mutable and `x` must be a subtype of `fieldtype(typeof(value), name)`. Additionally, an ordering can be specified for this operation. If the field was declared `@atomic`, this specification is mandatory. Otherwise, if not declared as `@atomic`, it must be `:not_atomic` if specified. See also [`setproperty!`](@ref Base.setproperty!). # Examples ```jldoctest julia> mutable struct MyMutableStruct field::Int end julia> a = MyMutableStruct(1); julia> setfield!(a, :field, 2); julia> getfield(a, :field) 2 julia> a = 1//2 1//2 julia> setfield!(a, :num, 3); ERROR: setfield!: immutable struct of type Rational cannot be changed ``` """ setfield! """ swapfield!(value, name::Symbol, x, [order::Symbol]) swapfield!(value, i::Int, x, [order::Symbol]) These atomically perform the operations to simultaneously get and set a field: y = getfield(value, name) setfield!(value, name, x) return y """ swapfield! """ modifyfield!(value, name::Symbol, op, x, [order::Symbol]) -> Pair modifyfield!(value, i::Int, op, x, [order::Symbol]) -> Pair These atomically perform the operations to get and set a field after applying the function `op`. y = getfield(value, name) z = op(y, x) setfield!(value, name, z) return y => z If supported by the hardware (for example, atomic increment), this may be optimized to the appropriate hardware instruction, otherwise it'll use a loop. """ modifyfield! """ replacefield!(value, name::Symbol, expected, desired, [success_order::Symbol, [fail_order::Symbol=success_order]) -> (; old, success::Bool) replacefield!(value, i::Int, expected, desired, [success_order::Symbol, [fail_order::Symbol=success_order]) -> (; old, success::Bool) These atomically perform the operations to get and conditionally set a field to a given value. y = getfield(value, name, fail_order) ok = y === expected if ok setfield!(value, name, desired, success_order) end return (; old = y, success = ok) If supported by the hardware, this may be optimized to the appropriate hardware instruction, otherwise it'll use a loop. """ replacefield! """ getglobal(module::Module, name::Symbol, [order::Symbol=:monotonic]) Retrieve the value of the binding `name` from the module `module`. Optionally, an atomic ordering can be defined for the operation, otherwise it defaults to monotonic. While accessing module bindings using [`getfield`](@ref) is still supported to maintain compatibility, using `getglobal` should always be preferred since `getglobal` allows for control over atomic ordering (`getfield` is always monotonic) and better signifies the code's intent both to the user as well as the compiler. Most users should not have to call this function directly -- The [`getproperty`](@ref Base.getproperty) function or corresponding syntax (i.e. `module.name`) should be preferred in all but few very specific use cases. !!! compat "Julia 1.9" This function requires Julia 1.9 or later. See also [`getproperty`](@ref Base.getproperty) and [`setglobal!`](@ref). # Examples ```jldoctest julia> a = 1 1 julia> module M a = 2 end; julia> getglobal(@__MODULE__, :a) 1 julia> getglobal(M, :a) 2 ``` """ getglobal """ setglobal!(module::Module, name::Symbol, x, [order::Symbol=:monotonic]) Set or change the value of the binding `name` in the module `module` to `x`. No type conversion is performed, so if a type has already been declared for the binding, `x` must be of appropriate type or an error is thrown. Additionally, an atomic ordering can be specified for this operation, otherwise it defaults to monotonic. Users will typically access this functionality through the [`setproperty!`](@ref Base.setproperty!) function or corresponding syntax (i.e. `module.name = x`) instead, so this is intended only for very specific use cases. !!! compat "Julia 1.9" This function requires Julia 1.9 or later. See also [`setproperty!`](@ref Base.setproperty!) and [`getglobal`](@ref) # Examples ```jldoctest julia> module M end; julia> M.a # same as `getglobal(M, :a)` ERROR: UndefVarError: `a` not defined julia> setglobal!(M, :a, 1) 1 julia> M.a 1 ``` """ setglobal! """ typeof(x) Get the concrete type of `x`. See also [`eltype`](@ref). # Examples ```jldoctest julia> a = 1//2; julia> typeof(a) Rational{Int64} julia> M = [1 2; 3.5 4]; julia> typeof(M) Matrix{Float64} (alias for Array{Float64, 2}) ``` """ typeof """ isdefined(m::Module, s::Symbol, [order::Symbol]) isdefined(object, s::Symbol, [order::Symbol]) isdefined(object, index::Int, [order::Symbol]) Tests whether a global variable or object field is defined. The arguments can be a module and a symbol or a composite object and field name (as a symbol) or index. Optionally, an ordering can be defined for the operation. If the field was declared `@atomic`, the specification is strongly recommended to be compatible with the stores to that location. Otherwise, if not declared as `@atomic`, this parameter must be `:not_atomic` if specified. To test whether an array element is defined, use [`isassigned`](@ref) instead. See also [`@isdefined`](@ref). # Examples ```jldoctest julia> isdefined(Base, :sum) true julia> isdefined(Base, :NonExistentMethod) false julia> a = 1//2; julia> isdefined(a, 2) true julia> isdefined(a, 3) false julia> isdefined(a, :num) true julia> isdefined(a, :numerator) false ``` """ isdefined """ Vector{T}(undef, n) Construct an uninitialized [`Vector{T}`](@ref) of length `n`. # Examples ```julia-repl julia> Vector{Float64}(undef, 3) 3-element Array{Float64, 1}: 6.90966e-310 6.90966e-310 6.90966e-310 ``` """ Vector{T}(::UndefInitializer, n) """ Vector{T}(nothing, m) Construct a [`Vector{T}`](@ref) of length `m`, initialized with [`nothing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Nothing <: T`. # Examples ```jldoctest julia> Vector{Union{Nothing, String}}(nothing, 2) 2-element Vector{Union{Nothing, String}}: nothing nothing ``` """ Vector{T}(::Nothing, n) """ Vector{T}(missing, m) Construct a [`Vector{T}`](@ref) of length `m`, initialized with [`missing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Missing <: T`. # Examples ```jldoctest julia> Vector{Union{Missing, String}}(missing, 2) 2-element Vector{Union{Missing, String}}: missing missing ``` """ Vector{T}(::Missing, n) """ Matrix{T}(undef, m, n) Construct an uninitialized [`Matrix{T}`](@ref) of size `m`ร—`n`. # Examples ```julia-repl julia> Matrix{Float64}(undef, 2, 3) 2ร—3 Array{Float64, 2}: 2.36365e-314 2.28473e-314 5.0e-324 2.26704e-314 2.26711e-314 NaN julia> similar(ans, Int32, 2, 2) 2ร—2 Matrix{Int32}: 490537216 1277177453 1 1936748399 ``` """ Matrix{T}(::UndefInitializer, m, n) """ Matrix{T}(nothing, m, n) Construct a [`Matrix{T}`](@ref) of size `m`ร—`n`, initialized with [`nothing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Nothing <: T`. # Examples ```jldoctest julia> Matrix{Union{Nothing, String}}(nothing, 2, 3) 2ร—3 Matrix{Union{Nothing, String}}: nothing nothing nothing nothing nothing nothing ``` """ Matrix{T}(::Nothing, m, n) """ Matrix{T}(missing, m, n) Construct a [`Matrix{T}`](@ref) of size `m`ร—`n`, initialized with [`missing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Missing <: T`. # Examples ```jldoctest julia> Matrix{Union{Missing, String}}(missing, 2, 3) 2ร—3 Matrix{Union{Missing, String}}: missing missing missing missing missing missing ``` """ Matrix{T}(::Missing, m, n) """ Array{T}(undef, dims) Array{T,N}(undef, dims) Construct an uninitialized `N`-dimensional [`Array`](@ref) containing elements of type `T`. `N` can either be supplied explicitly, as in `Array{T,N}(undef, dims)`, or be determined by the length or number of `dims`. `dims` may be a tuple or a series of integer arguments corresponding to the lengths in each dimension. If the rank `N` is supplied explicitly, then it must match the length or number of `dims`. Here [`undef`](@ref) is the [`UndefInitializer`](@ref). # Examples ```julia-repl julia> A = Array{Float64, 2}(undef, 2, 3) # N given explicitly 2ร—3 Matrix{Float64}: 6.90198e-310 6.90198e-310 6.90198e-310 6.90198e-310 6.90198e-310 0.0 julia> B = Array{Float64}(undef, 4) # N determined by the input 4-element Vector{Float64}: 2.360075077e-314 NaN 2.2671131793e-314 2.299821756e-314 julia> similar(B, 2, 4, 1) # use typeof(B), and the given size 2ร—4ร—1 Array{Float64, 3}: [:, :, 1] = 2.26703e-314 2.26708e-314 0.0 2.80997e-314 0.0 2.26703e-314 2.26708e-314 0.0 ``` """ Array{T,N}(::UndefInitializer, dims) """ Array{T}(nothing, dims) Array{T,N}(nothing, dims) Construct an `N`-dimensional [`Array`](@ref) containing elements of type `T`, initialized with [`nothing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Nothing <: T`. # Examples ```jldoctest julia> Array{Union{Nothing, String}}(nothing, 2) 2-element Vector{Union{Nothing, String}}: nothing nothing julia> Array{Union{Nothing, Int}}(nothing, 2, 3) 2ร—3 Matrix{Union{Nothing, Int64}}: nothing nothing nothing nothing nothing nothing ``` """ Array{T,N}(::Nothing, dims) """ Array{T}(missing, dims) Array{T,N}(missing, dims) Construct an `N`-dimensional [`Array`](@ref) containing elements of type `T`, initialized with [`missing`](@ref) entries. Element type `T` must be able to hold these values, i.e. `Missing <: T`. # Examples ```jldoctest julia> Array{Union{Missing, String}}(missing, 2) 2-element Vector{Union{Missing, String}}: missing missing julia> Array{Union{Missing, Int}}(missing, 2, 3) 2ร—3 Matrix{Union{Missing, Int64}}: missing missing missing missing missing missing ``` """ Array{T,N}(::Missing, dims) """ UndefInitializer Singleton type used in array initialization, indicating the array-constructor-caller would like an uninitialized array. See also [`undef`](@ref), an alias for `UndefInitializer()`. # Examples ```julia-repl julia> Array{Float64, 1}(UndefInitializer(), 3) 3-element Array{Float64, 1}: 2.2752528595e-314 2.202942107e-314 2.275252907e-314 ``` """ UndefInitializer """ undef Alias for `UndefInitializer()`, which constructs an instance of the singleton type [`UndefInitializer`](@ref), used in array initialization to indicate the array-constructor-caller would like an uninitialized array. See also: [`missing`](@ref), [`similar`](@ref). # Examples ```julia-repl julia> Array{Float64, 1}(undef, 3) 3-element Vector{Float64}: 2.2752528595e-314 2.202942107e-314 2.275252907e-314 ``` """ undef """ Ptr{T}() Creates a null pointer to type `T`. """ Ptr{T}() """ +(x, y...) Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. # Examples ```jldoctest julia> 1 + 20 + 4 25 julia> +(1, 20, 4) 25 ``` """ (+)(x, y...) """ -(x) Unary minus operator. See also: [`abs`](@ref), [`flipsign`](@ref). # Examples ```jldoctest julia> -1 -1 julia> -(2) -2 julia> -[1 2; 3 4] 2ร—2 Matrix{Int64}: -1 -2 -3 -4 ``` """ -(x) """ -(x, y) Subtraction operator. # Examples ```jldoctest julia> 2 - 3 -1 julia> -(2, 4.5) -2.5 ``` """ -(x, y) """ *(x, y...) Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. # Examples ```jldoctest julia> 2 * 7 * 8 112 julia> *(2, 7, 8) 112 ``` """ (*)(x, y...) """ /(x, y) Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives floating-point results for integer arguments. # Examples ```jldoctest julia> 1/2 0.5 julia> 4/2 2.0 julia> 4.5/2 2.25 ``` """ /(x, y) """ ArgumentError(msg) The arguments passed to a function are invalid. `msg` is a descriptive error message. """ ArgumentError """ MethodError(f, args) A method with the required type signature does not exist in the given generic function. Alternatively, there is no unique most-specific method. """ MethodError """ AssertionError([msg]) The asserted condition did not evaluate to `true`. Optional argument `msg` is a descriptive error string. # Examples ```jldoctest julia> @assert false "this is not true" ERROR: AssertionError: this is not true ``` `AssertionError` is usually thrown from [`@assert`](@ref). """ AssertionError """ LoadError(file::AbstractString, line::Int, error) An error occurred while [`include`](@ref Base.include)ing, [`require`](@ref Base.require)ing, or [`using`](@ref) a file. The error specifics should be available in the `.error` field. !!! compat "Julia 1.7" LoadErrors are no longer emitted by `@macroexpand`, `@macroexpand1`, and `macroexpand` as of Julia 1.7. """ LoadError """ InitError(mod::Symbol, error) An error occurred when running a module's `__init__` function. The actual error thrown is available in the `.error` field. """ InitError """ Any::DataType `Any` is the union of all types. It has the defining property `isa(x, Any) == true` for any `x`. `Any` therefore describes the entire universe of possible values. For example `Integer` is a subset of `Any` that includes `Int`, `Int8`, and other integer types. """ Any """ Union{} `Union{}`, the empty [`Union`](@ref) of types, is the type that has no values. That is, it has the defining property `isa(x, Union{}) == false` for any `x`. `Base.Bottom` is defined as its alias and the type of `Union{}` is `Core.TypeofBottom`. # Examples ```jldoctest julia> isa(nothing, Union{}) false ``` """ kw"Union{}", Base.Bottom """ Union{Types...} A type union is an abstract type which includes all instances of any of its argument types. The empty union [`Union{}`](@ref) is the bottom type of Julia. # Examples ```jldoctest julia> IntOrString = Union{Int,AbstractString} Union{Int64, AbstractString} julia> 1 isa IntOrString true julia> "Hello!" isa IntOrString true julia> 1.0 isa IntOrString false ``` """ Union """ UnionAll A union of types over all values of a type parameter. `UnionAll` is used to describe parametric types where the values of some parameters are not known. # Examples ```jldoctest julia> typeof(Vector) UnionAll julia> typeof(Vector{Int}) DataType ``` """ UnionAll """ :: The `::` operator either asserts that a value has the given type, or declares that a local variable or function return always has the given type. Given `expression::T`, `expression` is first evaluated. If the result is of type `T`, the value is simply returned. Otherwise, a [`TypeError`](@ref) is thrown. In local scope, the syntax `local x::T` or `x::T = expression` declares that local variable `x` always has type `T`. When a value is assigned to the variable, it will be converted to type `T` by calling [`convert`](@ref). In a method declaration, the syntax `function f(x)::T` causes any value returned by the method to be converted to type `T`. See the manual section on [Type Declarations](@ref). # Examples ```jldoctest julia> (1+2)::AbstractFloat ERROR: TypeError: typeassert: expected AbstractFloat, got a value of type Int64 julia> (1+2)::Int 3 julia> let local x::Int x = 2.0 x end 2 ``` """ kw"::" """ Vararg{T,N} The last parameter of a tuple type [`Tuple`](@ref) can be the special value `Vararg`, which denotes any number of trailing elements. `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. Finally `Vararg{T}` corresponds to zero or more elements of type `T`. `Vararg` tuple types are used to represent the arguments accepted by varargs methods (see the section on [Varargs Functions](@ref) in the manual.) See also [`NTuple`](@ref). # Examples ```jldoctest julia> mytupletype = Tuple{AbstractString, Vararg{Int}} Tuple{AbstractString, Vararg{Int64}} julia> isa(("1",), mytupletype) true julia> isa(("1",1), mytupletype) true julia> isa(("1",1,2), mytupletype) true julia> isa(("1",1,2,3.0), mytupletype) false ``` """ Vararg """ Tuple{Types...} A tuple is a fixed-length container that can hold any values of different types, but cannot be modified (it is immutable). The values can be accessed via indexing. Tuple literals are written with commas and parentheses: ```jldoctest julia> (1, 1+1) (1, 2) julia> (1,) (1,) julia> x = (0.0, "hello", 6*7) (0.0, "hello", 42) julia> x[2] "hello" julia> typeof(x) Tuple{Float64, String, Int64} ``` A length-1 tuple must be written with a comma, `(1,)`, since `(1)` would just be a parenthesized value. `()` represents the empty (length-0) tuple. A tuple can be constructed from an iterator by using a `Tuple` type as constructor: ```jldoctest julia> Tuple(["a", 1]) ("a", 1) julia> Tuple{String, Float64}(["a", 1]) ("a", 1.0) ``` Tuple types are covariant in their parameters: `Tuple{Int}` is a subtype of `Tuple{Any}`. Therefore `Tuple{Any}` is considered an abstract type, and tuple types are only concrete if their parameters are. Tuples do not have field names; fields are only accessed by index. Tuple types may have any number of parameters. See the manual section on [Tuple Types](@ref). See also [`Vararg`](@ref), [`NTuple`](@ref), [`ntuple`](@ref), [`tuple`](@ref), [`NamedTuple`](@ref). """ Tuple """ NamedTuple{names}(args::Tuple) Construct a named tuple with the given `names` (a tuple of Symbols) from a tuple of values. """ NamedTuple{names}(args::Tuple) """ NamedTuple{names,T}(args::Tuple) Construct a named tuple with the given `names` (a tuple of Symbols) and field types `T` (a `Tuple` type) from a tuple of values. """ NamedTuple{names,T}(args::Tuple) """ NamedTuple{names}(nt::NamedTuple) Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from another named tuple. """ NamedTuple{names}(nt::NamedTuple) """ NamedTuple(itr) Construct a named tuple from an iterator of key-value pairs (where the keys must be `Symbol`s). Equivalent to `(; itr...)`. !!! compat "Julia 1.6" This method requires at least Julia 1.6. """ NamedTuple(itr) """ typeassert(x, type) Throw a [`TypeError`](@ref) unless `x isa type`. The syntax `x::type` calls this function. # Examples ```jldoctest julia> typeassert(2.5, Int) ERROR: TypeError: in typeassert, expected Int64, got a value of type Float64 Stacktrace: [...] ``` """ typeassert """ getproperty(value, name::Symbol) getproperty(value, name::Symbol, order::Symbol) The syntax `a.b` calls `getproperty(a, :b)`. The syntax `@atomic order a.b` calls `getproperty(a, :b, :order)` and the syntax `@atomic a.b` calls `getproperty(a, :b, :sequentially_consistent)`. # Examples ```jldoctest julia> struct MyType{T <: Number} x::T end julia> function Base.getproperty(obj::MyType, sym::Symbol) if sym === :special return obj.x + 1 else # fallback to getfield return getfield(obj, sym) end end julia> obj = MyType(1); julia> obj.special 2 julia> obj.x 1 ``` One should overload `getproperty` only when necessary, as it can be confusing if the behavior of the syntax `obj.f` is unusual. Also note that using methods is often preferable. See also this style guide documentation for more information: [Prefer exported methods over direct field access](@ref). See also [`getfield`](@ref Core.getfield), [`propertynames`](@ref Base.propertynames) and [`setproperty!`](@ref Base.setproperty!). """ Base.getproperty """ setproperty!(value, name::Symbol, x) setproperty!(value, name::Symbol, x, order::Symbol) The syntax `a.b = c` calls `setproperty!(a, :b, c)`. The syntax `@atomic order a.b = c` calls `setproperty!(a, :b, c, :order)` and the syntax `@atomic a.b = c` calls `setproperty!(a, :b, c, :sequentially_consistent)`. !!! compat "Julia 1.8" `setproperty!` on modules requires at least Julia 1.8. See also [`setfield!`](@ref Core.setfield!), [`propertynames`](@ref Base.propertynames) and [`getproperty`](@ref Base.getproperty). """ Base.setproperty! """ swapproperty!(x, f::Symbol, v, order::Symbol=:not_atomic) The syntax `@atomic a.b, _ = c, a.b` returns `(c, swapproperty!(a, :b, c, :sequentially_consistent))`, where there must be one `getproperty` expression common to both sides. See also [`swapfield!`](@ref Core.swapfield!) and [`setproperty!`](@ref Base.setproperty!). """ Base.swapproperty! """ modifyproperty!(x, f::Symbol, op, v, order::Symbol=:not_atomic) The syntax `@atomic op(x.f, v)` (and its equivalent `@atomic x.f op v`) returns `modifyproperty!(x, :f, op, v, :sequentially_consistent)`, where the first argument must be a `getproperty` expression and is modified atomically. Invocation of `op(getproperty(x, f), v)` must return a value that can be stored in the field `f` of the object `x` by default. In particular, unlike the default behavior of [`setproperty!`](@ref Base.setproperty!), the `convert` function is not called automatically. See also [`modifyfield!`](@ref Core.modifyfield!) and [`setproperty!`](@ref Base.setproperty!). """ Base.modifyproperty! """ replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order) Perform a compare-and-swap operation on `x.f` from `expected` to `desired`, per egal. The syntax `@atomic_replace! x.f expected => desired` can be used instead of the function call form. See also [`replacefield!`](@ref Core.replacefield!) and [`setproperty!`](@ref Base.setproperty!). """ Base.replaceproperty! """ StridedArray{T, N} A hard-coded [`Union`](@ref) of common array types that follow the [strided array interface](@ref man-interface-strided-arrays), with elements of type `T` and `N` dimensions. If `A` is a `StridedArray`, then its elements are stored in memory with offsets, which may vary between dimensions but are constant within a dimension. For example, `A` could have stride 2 in dimension 1, and stride 3 in dimension 2. Incrementing `A` along dimension `d` jumps in memory by [`stride(A, d)`] slots. Strided arrays are particularly important and useful because they can sometimes be passed directly as pointers to foreign language libraries like BLAS. """ StridedArray """ StridedVector{T} One dimensional [`StridedArray`](@ref) with elements of type `T`. """ StridedVector """ StridedMatrix{T} Two dimensional [`StridedArray`](@ref) with elements of type `T`. """ StridedMatrix """ StridedVecOrMat{T} Union type of [`StridedVector`](@ref) and [`StridedMatrix`](@ref) with elements of type `T`. """ StridedVecOrMat """ Module A `Module` is a separate global variable workspace. See [`module`](@ref) and the [manual section about modules](@ref modules) for details. Module(name::Symbol=:anonymous, std_imports=true, default_names=true) Return a module with the specified name. A `baremodule` corresponds to `Module(:ModuleName, false)` An empty module containing no names at all can be created with `Module(:ModuleName, false, false)`. This module will not import `Base` or `Core` and does not contain a reference to itself. """ Module """ Core `Core` is the module that contains all identifiers considered "built in" to the language, i.e. part of the core language and not libraries. Every module implicitly specifies `using Core`, since you can't do anything without those definitions. """ Core.Core """ Main `Main` is the top-level module, and Julia starts with `Main` set as the current module. Variables defined at the prompt go in `Main`, and `varinfo` lists variables in `Main`. ```jldoctest julia> @__MODULE__ Main ``` """ Main.Main """ Base The base library of Julia. `Base` is a module that contains basic functionality (the contents of `base/`). All modules implicitly contain `using Base`, since this is needed in the vast majority of cases. """ Base.Base """ QuoteNode A quoted piece of code, that does not support interpolation. See the [manual section about QuoteNodes](@ref man-quote-node) for details. """ QuoteNode """ " `"` Is used to delimit string literals. A trailing `\\` can be used to continue a string literal on the next line. # Examples ```jldoctest julia> "Hello World!" "Hello World!" julia> "Hello World!\\n" "Hello World!\\n" julia> "Hello \\ World" "Hello World" ``` See also [`\"""`](@ref \"\"\"). """ kw"\"" """ \""" `\"""` is used to delimit string literals. Strings created by triple quotation marks can contain `"` characters without escaping and are dedented to the level of the least-indented line. This is useful for defining strings within code that is indented. # Examples ```jldoctest julia> \"""Hello World!\""" "Hello World!" julia> \"""Contains "quote" characters\""" "Contains \\"quote\\" characters" julia> \""" Hello, world.\""" "Hello,\\nworld." ``` See also [`"`](@ref \") """ kw"\"\"\"" """ Unsafe pointer operations are compatible with loading and storing pointers declared with `_Atomic` and `std::atomic` type in C11 and C++23 respectively. An error may be thrown if there is not support for atomically loading the Julia type `T`. See also: [`unsafe_load`](@ref), [`unsafe_modify!`](@ref), [`unsafe_replace!`](@ref), [`unsafe_store!`](@ref), [`unsafe_swap!`](@ref) """ kw"atomic" """ Base.donotdelete(args...) This function prevents dead-code elimination (DCE) of itself and any arguments passed to it, but is otherwise the lightest barrier possible. In particular, it is not a GC safepoint, does model an observable heap effect, does not expand to any code itself and may be re-ordered with respect to other side effects (though the total number of executions may not change). A useful model for this function is that it hashes all memory `reachable` from args and escapes this information through some observable side-channel that does not otherwise impact program behavior. Of course that's just a model. The function does nothing and returns `nothing`. This is intended for use in benchmarks that want to guarantee that `args` are actually computed. (Otherwise DCE may see that the result of the benchmark is unused and delete the entire benchmark code). !!! note `donotdelete` does not affect constant folding. For example, in `donotdelete(1+1)`, no add instruction needs to be executed at runtime and the code is semantically equivalent to `donotdelete(2).` # Examples ```julia function loop() for i = 1:1000 # The compiler must guarantee that there are 1000 program points (in the correct # order) at which the value of `i` is in a register, but has otherwise # total control over the program. donotdelete(i) end end ``` """ Base.donotdelete """ Base.compilerbarrier(setting::Symbol, val) This function puts a barrier at a specified compilation phase. It is supposed to only influence the compilation behavior according to `setting`, and its runtime semantics is just to return the second argument `val` (except that this function will perform additional checks on `setting` in a case when `setting` isn't known precisely at compile-time.) Currently either of the following `setting`s is allowed: - Barriers on abstract interpretation: * `:type`: the return type of this function call will be inferred as `Any` always (the strongest barrier on abstract interpretation) * `:const`: the return type of this function call will be inferred with widening constant information on `val` * `:conditional`: the return type of this function call will be inferred with widening conditional information on `val` (see the example below) - Any barriers on optimization aren't implemented yet !!! note This function is supposed to be used _with `setting` known precisely at compile-time_. Note that in a case when the `setting` isn't known precisely at compile-time, the compiler currently will put the most strongest barrier(s) rather than emitting a compile-time warning. # Examples ```julia julia> Base.return_types((Int,)) do a x = compilerbarrier(:type, a) # `x` won't be inferred as `x::Int` return x end |> only Any julia> Base.return_types() do x = compilerbarrier(:const, 42) if x == 42 # no constant information here, so inference also accounts for the else branch return x # but `x` is still inferred as `x::Int` at least here else return nothing end end |> only Union{Nothing, Int64} julia> Base.return_types((Union{Int,Nothing},)) do a if compilerbarrier(:conditional, isa(a, Int)) # the conditional information `a::Int` isn't available here (leading to less accurate return type inference) return a else return nothing end end |> only Union{Nothing, Int64} ``` """ Base.compilerbarrier """ Core.finalizer(f, o) This builtin is an implementation detail of [`Base.finalizer`](@ref) and end-users should use the latter instead. # Differences from `Base.finalizer` The interface of `Core.finalizer` is essentially the same as `Base.finalizer`, but there are a number of small differences. They are documented here for completeness only and (unlike `Base.finalizer`) have no stability guarantees. The current differences are: - `Core.finalizer` does not check for mutability of `o`. Attempting to register a finalizer for an immutable object is undefined behavior. - The value `f` must be a Julia object. `Core.finalizer` does not support a raw C function pointer. - `Core.finalizer` returns `nothing` rather than `o`. """ Core.finalizer end P/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/docs/Docs.jl+Z# This file is a part of Julia. License is MIT: https://julialang.org/license """ Docs The `Docs` module provides the `@doc` macro which can be used to set and retrieve documentation metadata for Julia objects. Please see the manual section on [documentation](@ref man-documentation) for more information. """ module Docs @nospecialize # don't specialize on any arguments of the methods declared herein """ # Documentation Functions, methods and types can be documented by placing a string before the definition: \"\"\" # The Foo Function `foo(x)`: Foo the living hell out of `x`. \"\"\" foo(x) = ... The `@doc` macro can be used directly to both set and retrieve documentation / metadata. The macro has special parsing so that the documented object may occur on the next line: @doc "blah" function foo() ... By default, documentation is written as Markdown, but any object can be used as the first argument. ## Documenting objects after they are defined You can document an object after its definition by @doc "foo" function_to_doc @doc "bar" TypeToDoc For macros, the syntax is `@doc "macro doc" :(Module.@macro)` or `@doc "macro doc" :(string_macro"")` for string macros. Without the quote `:()` the expansion of the macro will be documented. ## Retrieving Documentation You can retrieve docs for functions, macros and other objects as follows: @doc foo @doc @time @doc md"" ## Functions & Methods Placing documentation before a method definition (e.g. `function foo() ...` or `foo() = ...`) will cause that specific method to be documented, as opposed to the whole function. Method docs are concatenated together in the order they were defined to provide docs for the function. """ :(Core.@doc) include("bindings.jl") import .Base.Meta: quot, isexpr import .Base: Callable, with_output_color using .Base: RefValue, mapany import ..CoreDocs: lazy_iterpolate export doc # Basic API / Storage const modules = Module[] const META = gensym(:meta) const METAType = IdDict{Any,Any} function meta(m::Module; autoinit::Bool=true) if !isdefined(m, META) || getfield(m, META) === nothing autoinit ? initmeta(m) : return nothing end return getfield(m, META)::METAType end function initmeta(m::Module) if !isdefined(m, META) || getfield(m, META) === nothing Core.eval(m, :($META = $(METAType()))) push!(modules, m) end nothing end function signature!(tv::Vector{Any}, expr::Expr) is_macrocall = isexpr(expr, :macrocall) if is_macrocall || isexpr(expr, :call) sig = :(Union{Tuple{}}) first_arg = is_macrocall ? 3 : 2 # skip function arguments for arg in expr.args[first_arg:end] isexpr(arg, :parameters) && continue if isexpr(arg, :kw) # optional arg push!(sig.args, :(Tuple{$((sig.args[end]::Expr).args[2:end]...)})) end push!((sig.args[end]::Expr).args, argtype(arg)) end if isexpr(expr.args[1], :curly) && isempty(tv) append!(tv, mapany(tvar, (expr.args[1]::Expr).args[2:end])) end for i = length(tv):-1:1 push!(sig.args, :(Tuple{$((tv[i]::Expr).args[1])})) end for i = length(tv):-1:1 sig = Expr(:where, sig, tv[i]) end return sig elseif isexpr(expr, :where) append!(tv, mapany(tvar, expr.args[2:end])) return signature!(tv, expr.args[1]) else return signature!(tv, expr.args[1]) end end signature!(tv::Vector{Any}, @nospecialize(other)) = :(Union{}) signature(expr::Expr) = signature!([], expr) signature(@nospecialize other) = signature!([], other) function argtype(expr::Expr) isexpr(expr, :(::)) && return expr.args[end] isexpr(expr, :(...)) && return :(Vararg{$(argtype(expr.args[1]))}) if isexpr(expr, :meta) && length(expr.args) == 2 a1 = expr.args[1] if a1 === :nospecialize || a1 === :specialize return argtype(expr.args[2]) end end return argtype(expr.args[1]) end argtype(@nospecialize other) = :Any tvar(x::Expr) = x tvar(s::Symbol) = :($s <: Any) # Docsystem types. # ================ """ Docs.DocStr Stores the contents of a single docstring as well as related metadata. Both the raw text, `.text`, and the parsed markdown, `.object`, are tracked by this type. Parsing of the raw text is done lazily when a request is made to render the docstring, which helps to reduce total precompiled image size. The `.data` fields stores several values related to the docstring, such as: path, linenumber, source code, and fielddocs. """ mutable struct DocStr text :: Core.SimpleVector object :: Any data :: Dict{Symbol, Any} end function docstr(binding::Binding, typesig = Union{}) @nospecialize typesig for m in modules dict = meta(m; autoinit=false) isnothing(dict) && continue if haskey(dict, binding) docs = dict[binding].docs if haskey(docs, typesig) return docs[typesig] end end end error("could not find matching docstring for '$binding :: $typesig'.") end docstr(object, data = Dict{Symbol,Any}()) = _docstr(object, data) _docstr(vec::Core.SimpleVector, data::Dict{Symbol,Any}) = DocStr(vec, nothing, data) _docstr(str::AbstractString, data::Dict{Symbol,Any}) = DocStr(Core.svec(str), nothing, data) _docstr(object, data::Dict{Symbol,Any}) = DocStr(Core.svec(), object, data) _docstr(doc::DocStr, data::Dict{Symbol,Any}) = (doc.data = merge(data, doc.data); doc) macro ref(x) binding = bindingexpr(namify(x)) typesig = signature(x) return esc(docexpr(__source__, __module__, binding, typesig)) end docexpr(__source__, __module__, args...) = Expr(:call, docstr, args...) """ MultiDoc Stores a collection of docstrings for related objects, ie. a `Function`/`DataType` and associated `Method` objects. Each documented object in a `MultiDoc` is referred to by it's signature which is represented by a `Union` of `Tuple` types. For example, the following `Method` definition f(x, y) = ... is stored as `Tuple{Any, Any}` in the `MultiDoc` while f(x::T, y = ?) where {T} = ... is stored as `Union{Tuple{T, Any}, Tuple{T}} where T`. Note: The `Function`/`DataType` object's signature is always `Union{}`. """ mutable struct MultiDoc "Ordered (via definition order) vector of object signatures." order::Vector{Type} "Documentation for each object. Keys are signatures." docs::METAType MultiDoc() = new(Type[], METAType()) end # Docstring registration. # ======================= """ Docs.doc!(__module__, binding, str, sig) Adds a new docstring `str` to the docsystem of `__module__` for `binding` and signature `sig`. """ function doc!(__module__::Module, b::Binding, str::DocStr, @nospecialize sig = Union{}) # Module docstrings are in the module itself if defined(b) obj = resolve(b) if isa(obj, Module) __module__ = obj end end initmeta(__module__) m = get!(meta(__module__), b, MultiDoc()) if haskey(m.docs, sig) # We allow for docstrings to be updated, but print a warning since it is possible # that over-writing a docstring *may* have been accidental. The warning # is suppressed for symbols in Main (or current active module), # for interactive use (#23011). __module__ === Base.active_module() || @warn "Replacing docs for `$b :: $sig` in module `$(__module__)`" else # The ordering of docstrings for each Binding is defined by the order in which they # are initially added. Replacing a specific docstring does not change it's ordering. push!(m.order, sig) end m.docs[sig] = str str.data[:binding] = b str.data[:typesig] = sig return b end # Docstring lookup. # ================= """ getdoc(obj) getdoc(obj, sig) Return a custom docstring object associated with the object `obj` and, optionally, the tuple type signature `sig`. See `MultiDoc` docs for an explanation of the possible values of `sig`. The returned object can either be a markdown object generated by `Markdown.parse` or some other custom type used to display non-markdown formatted documentation. A return value of `nothing` can be used to signify to the docsystem that no documentation was found for `obj`, in which case the docsystem will fall back to searching for the `Binding` associated with `obj` instead. """ function getdoc end getdoc(@nospecialize(x), @nospecialize(sig)) = getdoc(x) getdoc(@nospecialize(x)) = nothing # Utilities. # ========== """ `catdoc(xs...)`: Combine the documentation metadata `xs` into a single meta object. """ catdoc() = nothing catdoc(xs...) = vcat(xs...) const keywords = Dict{Symbol, DocStr}() function unblock(@nospecialize ex) while isexpr(ex, :var"hygienic-scope") isexpr(ex.args[1], :escape) || break ex = ex.args[1].args[1] end isexpr(ex, :block) || return ex exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args) length(exs) == 1 || return ex return unblock(exs[1]) end # peek through ex to figure out what kind of expression it may eventually act like # but ignoring scopes and line numbers function unescape(@nospecialize ex) ex = unblock(ex) while isexpr(ex, :escape) || isexpr(ex, :var"hygienic-scope") ex = unblock(ex.args[1]) end return ex end uncurly(@nospecialize ex) = isexpr(ex, :curly) ? ex.args[1] : ex namify(@nospecialize x) = astname(x, isexpr(x, :macro))::Union{Symbol,Expr,GlobalRef} function astname(x::Expr, ismacro::Bool) head = x.head if head === :. ismacro ? macroname(x) : x elseif head === :call && isexpr(x.args[1], :(::)) return astname((x.args[1]::Expr).args[end], ismacro) else n = isexpr(x, (:module, :struct)) ? 2 : 1 astname(x.args[n], ismacro) end end astname(q::QuoteNode, ismacro::Bool) = astname(q.value, ismacro) astname(s::Symbol, ismacro::Bool) = ismacro ? macroname(s) : s astname(@nospecialize(other), ismacro::Bool) = other macroname(s::Symbol) = Symbol('@', s) macroname(x::Expr) = Expr(x.head, x.args[1], macroname(x.args[end].value)) isfield(@nospecialize x) = isexpr(x, :.) && (isa(x.args[1], Symbol) || isfield(x.args[1])) && (isa(x.args[2], QuoteNode) || isexpr(x.args[2], :quote)) # @doc expression builders. # ========================= """ Docs.metadata(source, module, expr, ismodule) Build a `Dict` expression containing metadata captured from the expression `expr`. Fields that may be included in the returned `Dict`: - `:path`: Symbol representing the file where `expr` is defined. - `:linenumber`: Linenumber where `expr` is defined. - `:module`: Module where the docstring is defined. - `:fields`: `Dict` of all field docs found in `expr`. Only for concrete types. """ function metadata(__source__, __module__, expr, ismodule) args = [] # Filename and linenumber of the docstring. __file__ = isa(__source__.file, Symbol) ? String(__source__.file) : "" push!(args, Pair(:path, __file__)) push!(args, Pair(:linenumber, __source__.line)) # Module in which the docstring is defined. if ismodule # Module docs go inside the module with name `expr` push!(args, :($Pair(:module, $expr))) else push!(args, Pair(:module, __module__)) end if isexpr(expr, :struct) # Field docs for concrete types. P = Pair{Symbol,Any} fields = P[] last_docstr = nothing for each in (expr.args[3]::Expr).args eachex = unescape(each) if isa(eachex, Symbol) || isexpr(eachex, :(::)) # a field declaration if last_docstr !== nothing push!(fields, P(namify(eachex::Union{Symbol,Expr}), last_docstr)) last_docstr = nothing end elseif isexpr(eachex, :function) || isexpr(eachex, :(=)) break elseif isa(eachex, String) || isexpr(eachex, :string) || isexpr(eachex, :call) || (isexpr(eachex, :macrocall) && eachex.args[1] === Symbol("@doc_str")) # forms that might be doc strings last_docstr = each end end dict = :($(Dict{Symbol,Any})($([(:($(P)($(quot(f)), $d)))::Expr for (f, d) in fields]...))) push!(args, :($(Pair)(:fields, $dict))) end return :($(Dict{Symbol,Any})($(args...))) end function keyworddoc(__source__, __module__, str, def::Base.BaseDocs.Keyword) @nospecialize str docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, def, false))) return :($setindex!($(keywords), $docstr, $(esc(quot(def.name)))); nothing) end function objectdoc(__source__, __module__, str, def, expr, sig = :(Union{})) @nospecialize str def expr sig binding = esc(bindingexpr(namify(expr))) docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, expr, false))) # Note: we want to avoid introducing line number nodes here (issue #24468) return Expr(:block, esc(def), :($(doc!)($__module__, $binding, $docstr, $(esc(sig))))) end function calldoc(__source__, __module__, str, def::Expr) @nospecialize str args = callargs(def) if isempty(args) || all(validcall, args) objectdoc(__source__, __module__, str, nothing, def, signature(def)) else docerror(def) end end callargs(ex::Expr) = isexpr(ex, :where) ? callargs(ex.args[1]) : isexpr(ex, :call) ? ex.args[2:end] : error("Invalid expression to callargs: $ex") validcall(x) = isa(x, Symbol) || isexpr(x, (:(::), :..., :kw, :parameters)) function moduledoc(__source__, __module__, meta, def, defโ€ฒ::Expr) @nospecialize meta def name = namify(defโ€ฒ) docex = Expr(:call, doc!, name, bindingexpr(name), docexpr(__source__, name, lazy_iterpolate(meta), metadata(__source__, __module__, name, true))) if def === nothing esc(:(Core.eval($name, $(quot(docex))))) else def = unblock(def) block = def.args[3].args if !def.args[1] pushfirst!(block, :(import Base: @doc)) end push!(block, docex) esc(Expr(:toplevel, def)) end end # Shares a single doc, `meta`, between several expressions from the tuple expression `ex`. function multidoc(__source__, __module__, meta, ex::Expr, define::Bool) @nospecialize meta out = Expr(:block) str = docexpr(__source__, __module__, lazy_iterpolate(meta), metadata(__source__, __module__, ex, false)) ref = RefValue{DocStr}() first = true for arg in ex.args # The first `arg` to be documented needs to also create the docstring for the group # (after doing the action defined by the argument). # Subsequent `arg`s just need `ref` to be able to find the docstring without having # to create an entirely new one each. if first first = false docstr = :($getindex($setindex!($(ref), $str))) else docstr = :($getindex($(ref))) end push!(out.args, docm(__source__, __module__, docstr, arg, define)) end return out end """ @__doc__(ex) Low-level macro used to mark expressions returned by a macro that should be documented. If more than one expression is marked then the same docstring is applied to each expression. macro example(f) quote \$(f)() = 0 @__doc__ \$(f)(x) = 1 \$(f)(x, y) = 2 end |> esc end `@__doc__` has no effect when a macro that uses it is not documented. """ :(Core.@__doc__) function __doc__!(source, mod, meta, def, define::Bool) @nospecialize source mod meta def # Two cases must be handled here to avoid redefining all definitions contained in `def`: if define # `def` has not been defined yet (this is the common case, i.e. when not generating # the Base image). We just need to convert each `@__doc__` marker to an `@doc`. finddoc(def) do each each.head = :macrocall each.args = Any[Symbol("@doc"), source, mod, nothing, meta, each.args[end], define] end else # `def` has already been defined during Base image gen so we just need to find and # document any subexpressions marked with `@__doc__`. docs = [] found = finddoc(def) do each push!(docs, :(@doc($source, $mod, $meta, $(each.args[end]), $define))) end # If any subexpressions have been documented then replace the entire expression with # just those documented subexpressions to avoid redefining any definitions. if found def.head = :toplevel def.args = docs end found end end # Walk expression tree `def` and call `ฮป` when any `@__doc__` markers are found. Returns # `true` to signify that at least one `@__doc__` has been found, and `false` otherwise. function finddoc(ฮป, def::Expr) if isexpr(def, :block, 2) && isexpr(def.args[1], :meta, 1) && (def.args[1]::Expr).args[1] === :doc # Found the macroexpansion of an `@__doc__` expression. ฮป(def) true else found = false for each in def.args found |= finddoc(ฮป, each) end found end end finddoc(ฮป, @nospecialize def) = false # Predicates and helpers for `docm` expression selection: const FUNC_HEADS = [:function, :macro, :(=)] const BINDING_HEADS = [:const, :global, :(=)] # For the special `:@mac` / `:(Base.@mac)` syntax for documenting a macro after definition. isquotedmacrocall(@nospecialize x) = isexpr(x, :copyast, 1) && isa(x.args[1], QuoteNode) && isexpr(x.args[1].value, :macrocall, 2) # Simple expressions / atoms the may be documented. isbasicdoc(@nospecialize x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol}) is_signature(@nospecialize x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where) function docm(source::LineNumberNode, mod::Module, ex) @nospecialize ex if isexpr(ex, :->) && length(ex.args) > 1 return docm(source, mod, ex.args...) elseif isassigned(Base.REPL_MODULE_REF) # TODO: this is a shim to continue to allow `@doc` for looking up docstrings REPL = Base.REPL_MODULE_REF[] return REPL.lookup_doc(ex) end return nothing end # Drop incorrect line numbers produced by nested macro calls. docm(source::LineNumberNode, mod::Module, _, _, x...) = docm(source, mod, x...) # iscallexpr checks if an expression is a :call expression. The call expression may be # also part of a :where expression, so it unwraps the :where layers until it reaches the # "actual" expression iscallexpr(ex::Expr) = isexpr(ex, :where) ? iscallexpr(ex.args[1]) : isexpr(ex, :call) iscallexpr(ex) = false function docm(source::LineNumberNode, mod::Module, meta, ex, define::Bool = true) @nospecialize meta ex # Some documented expressions may be decorated with macro calls which obscure the actual # expression. Expand the macro calls and remove extra blocks. x = unblock(macroexpand(mod, ex)) # Don't try to redefine expressions. This is only needed for `Base` img gen since # otherwise calling `loaddocs` would redefine all documented functions and types. def = define ? x : nothing if isa(x, GlobalRef) && (x::GlobalRef).mod == mod x = (x::GlobalRef).name end # Keywords using the `@kw_str` macro in `base/docs/basedocs.jl`. # # "..." # kw"if", kw"else" # doc = isa(x, Base.BaseDocs.Keyword) ? keyworddoc(source, mod, meta, x) : # Method / macro definitions and "call" syntax. # # function f(...) ... end # f(...) = ... # macro m(...) end # function f end # f(...) # # Including if the "call" expression is wrapped in "where" expression(s) (#32960), i.e. # # f(::T) where T # f(::T, ::U) where T where U # isexpr(x, FUNC_HEADS) && is_signature((x::Expr).args[1]) ? objectdoc(source, mod, meta, def, x::Expr, signature(x::Expr)) : isexpr(x, [:function, :macro]) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) : iscallexpr(x) ? calldoc(source, mod, meta, x::Expr) : # Type definitions. # # struct T ... end # abstract type T end # primitive type T N end # isexpr(x, [:struct, :abstract, :primitive]) ? objectdoc(source, mod, meta, def, x::Expr) : # "Bindings". Names that resolve to objects with different names, ie. # # const T = S # T = S # global T = S # isexpr(x, BINDING_HEADS) && !isexpr((x::Expr).args[1], :call) ? objectdoc(source, mod, meta, def, x::Expr) : # Quoted macrocall syntax. `:@time` / `:(Base.@time)`. isquotedmacrocall(x) ? objectdoc(source, mod, meta, def, x) : # Modules and baremodules. isexpr(x, :module) ? moduledoc(source, mod, meta, def, x::Expr) : # Document several expressions with the same docstring. `a, b, c`. isexpr(x, :tuple) ? multidoc(source, mod, meta, x::Expr, define) : # Errors generated by calling `macroexpand` are passed back to the call site. isexpr(x, :error) ? esc(x) : # When documenting macro-generated code we look for embedded `@__doc__` calls. __doc__!(source, mod, meta, x, define) ? esc(x) : # Any "basic" expression such as a bare function or module name or numeric literal. isbasicdoc(x) ? objectdoc(source, mod, meta, nothing, x) : # All other expressions are undocumentable and should be handled on a case-by-case basis # with `@__doc__`. Unbound string literals are also undocumentable since they cannot be # retrieved from the module's metadata `IdDict` without a reference to the string. docerror(ex) return doc end function docerror(@nospecialize ex) txt = """ cannot document the following expression: $(isa(ex, AbstractString) ? repr(ex) : ex)""" if isexpr(ex, :macrocall) txt *= "\n\n'$(ex.args[1])' not documentable. See 'Base.@__doc__' docs for details." end return :($(error)($txt, "\n")) end include("utils.jl") # Swap out the bootstrap macro with the real one. Core.atdoc!(docm) function loaddocs(docs::Vector{Core.SimpleVector}) for (mod, ex, str, file, line) in docs data = Dict{Symbol,Any}(:path => string(file), :linenumber => line) doc = docstr(str, data) lno = LineNumberNode(line, file) docstring = docm(lno, mod, doc, ex, false) # expand the real @doc macro now Core.eval(mod, Expr(:var"hygienic-scope", docstring, Docs, lno)) end empty!(docs) nothing end function formatdoc end function parsedoc end function apropos end function doc end end T/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/docs/bindings.jlั# This file is a part of Julia. License is MIT: https://julialang.org/license export @var struct Binding mod::Module var::Symbol function Binding(m::Module, v::Symbol) # Normalise the binding module for module symbols so that: # Binding(Base, :Base) === Binding(Main, :Base) m = nameof(m) === v ? parentmodule(m) : m new(Base.binding_module(m, v), v) end end bindingexpr(x) = Expr(:call, Binding, splitexpr(x)...) defined(b::Binding) = isdefined(b.mod, b.var) resolve(b::Binding) = getfield(b.mod, b.var) function splitexpr(x::Expr) isexpr(x, :macrocall) ? splitexpr(x.args[1]) : isexpr(x, :.) ? (x.args[1], x.args[2]) : error("Invalid @var syntax `$x`.") end splitexpr(s::Symbol) = Expr(:macrocall, getfield(Base, Symbol("@__MODULE__")), nothing), quot(s) splitexpr(r::GlobalRef) = r.mod, quot(r.name) splitexpr(other) = error("Invalid @var syntax `$other`.") macro var(x) esc(bindingexpr(x)) end function Base.show(io::IO, b::Binding) if b.mod === Base.active_module() print(io, b.var) else print(io, b.mod, '.', Base.isoperator(b.var) ? ":" : "", b.var) end end aliasof(b::Binding) = defined(b) ? (a = aliasof(resolve(b), b); defined(a) ? a : b) : b aliasof(d::DataType, b) = Binding(d.name.module, d.name.name) aliasof(ฮป::Function, b) = (m = typeof(ฮป).name.mt; Binding(m.module, m.name)) aliasof(m::Module, b) = Binding(m, nameof(m)) aliasof(other, b) = b Q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/base/docs/utils.jl๓# This file is a part of Julia. License is MIT: https://julialang.org/license # Text / HTML objects import .Base: print, show, ==, hash export HTML, @html_str export HTML, Text """ `HTML(s)`: Create an object that renders `s` as html. HTML("
foo
") You can also use a stream for large amounts of data: HTML() do io println(io, "
foo
") end !!! warning `HTML` is currently exported to maintain backwards compatibility, but this export is deprecated. It is recommended to use this type as `Docs.HTML` or to explicitly import it from `Docs`. """ mutable struct HTML{T} content::T end function HTML(xs...) HTML() do io for x in xs print(io, x) end end end show(io::IO, ::MIME"text/html", h::HTML) = print(io, h.content) show(io::IO, ::MIME"text/html", h::HTML{<:Function}) = h.content(io) """ @html_str -> Docs.HTML Create an `HTML` object from a literal string. # Examples ```jldoctest julia> html"Julia" HTML{String}("Julia") ``` """ macro html_str(s) :(HTML($s)) end function catdoc(xs::HTML...) HTML() do io for x in xs show(io, MIME"text/html"(), x) end end end export Text, @text_str """ `Text(s)`: Create an object that renders `s` as plain text. Text("foo") You can also use a stream for large amounts of data: Text() do io println(io, "foo") end !!! warning `Text` is currently exported to maintain backwards compatibility, but this export is deprecated. It is recommended to use this type as `Docs.Text` or to explicitly import it from `Docs`. """ mutable struct Text{T} content::T end print(io::IO, t::Text) = print(io, t.content) print(io::IO, t::Text{<:Function}) = t.content(io) show(io::IO, t::Text) = print(io, t) ==(t1::T, t2::T) where {T<:Union{HTML,Text}} = t1.content == t2.content hash(t::T, h::UInt) where {T<:Union{HTML,Text}} = hash(T, hash(t.content, h)) """ @text_str -> Docs.Text Create a `Text` object from a literal string. # Examples ```jldoctest julia> text"Julia" Julia ``` """ macro text_str(s) :(Text($s)) end function catdoc(xs::Text...) Text() do io for x in xs show(io, MIME"text/plain"(), x) end end end t/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/ArgTools/src/ArgTools.jl*module ArgTools export arg_read, ArgRead, arg_readers, arg_write, ArgWrite, arg_writers, arg_isdir, arg_mkdir, @arg_test import Base: AbstractCmd, CmdRedirect, Process if isdefined(Base.Filesystem, :prepare_for_deletion) using Base.Filesystem: prepare_for_deletion else function prepare_for_deletion(path::AbstractString) try prepare_for_deletion_core(path) catch end end function prepare_for_deletion_core(path::AbstractString) isdir(path) || return chmod(path, filemode(path) | 0o333) for name in readdir(path) pathโ€ฒ = joinpath(path, name) prepare_for_deletion_core(pathโ€ฒ) end end end const nolock_docstring = """ Note: when opening a file, ArgTools will pass `lock = false` to the file `open(...)` call. Therefore, the object returned by this function should not be used from multiple threads. This restriction may be relaxed in the future, which would not break any working code. """ if VERSION โ‰ฅย v"1.5" open_nolock(args...; kws...) = open(args...; kws..., lock=false) else open_nolock(args...; kws...) = open(args...; kws...) end ## main API ## """ ArgRead = Union{AbstractString, AbstractCmd, IO} The `ArgRead` types is a union of the types that the `arg_read` function knows how to convert into readable IO handles. See [`arg_read`](@ref) for details. """ const ArgRead = Union{AbstractString, AbstractCmd, IO} """ ArgWrite = Union{AbstractString, AbstractCmd, IO} The `ArgWrite` types is a union of the types that the `arg_write` function knows how to convert into writeable IO handles, except for `Nothing` which `arg_write` handles by generating a temporary file. See [`arg_write`](@ref) for details. """ const ArgWrite = Union{AbstractString, AbstractCmd, IO} """ arg_read(f::Function, arg::ArgRead) -> f(arg_io) The `arg_read` function accepts an argument `arg` that can be any of these: - `AbstractString`: a file path to be opened for reading - `AbstractCmd`: a command to be run, reading from its standard output - `IO`: an open IO handle to be read from Whether the body returns normally or throws an error, a path which is opened will be closed before returning from `arg_read` and an `IO` handle will be flushed but not closed before returning from `arg_read`. $(nolock_docstring) """ arg_read(f::Function, arg::AbstractString) = open_nolock(f, arg) arg_read(f::Function, arg::ArgRead) = open(f, arg) arg_read(f::Function, arg::IO) = f(arg) """ arg_write(f::Function, arg::ArgWrite) -> arg arg_write(f::Function, arg::Nothing) -> tempname() The `arg_read` function accepts an argument `arg` that can be any of these: - `AbstractString`: a file path to be opened for writing - `AbstractCmd`: a command to be run, writing to its standard input - `IO`: an open IO handle to be written to - `Nothing`: a temporary path should be written to If the body returns normally, a path that is opened will be closed upon completion; an IO handle argument is left open but flushed before return. If the argument is `nothing` then a temporary path is opened for writing and closed open completion and the path is returned from `arg_write`. In all other cases, `arg` itself is returned. This is a useful pattern since you can consistently return whatever was written, whether an argument was passed or not. If there is an error during the evaluation of the body, a path that is opened by `arg_write` for writing will be deleted, whether it's passed in as a string or a temporary path generated when `arg` is `nothing`. $(nolock_docstring) """ function arg_write(f::Function, arg::AbstractString) try open_nolock(f, arg, write=true) catch rm(arg, force=true) rethrow() end return arg end function arg_write(f::Function, arg::AbstractCmd) open(f, arg, write=true) return arg end function arg_write(f::Function, arg::Nothing) @static if VERSION โ‰ฅ v"1.5" file = tempname() io = open_nolock(file, write=true) else file, io = mktemp() end try f(io) catch close(io) rm(file, force=true) rethrow() end close(io) return file end function arg_write(f::Function, arg::IO) try f(arg) finally flush(arg) end return arg end """ arg_isdir(f::Function, arg::AbstractString) -> f(arg) The `arg_isdir` function takes `arg` which must be the path to an existing directory (an error is raised otherwise) and passes that path to `f` finally returning the result of `f(arg)`. This is definitely the least useful tool offered by `ArgTools` and mostly exists for symmetry with `arg_mkdir` and to give consistent error messages. """ function arg_isdir(f::Function, arg::AbstractString) isdir(arg) || error("arg_isdir: $(repr(arg)) not a directory") return f(arg) end """ arg_mkdir(f::Function, arg::AbstractString) -> arg arg_mkdir(f::Function, arg::Nothing) -> mktempdir() The `arg_mkdir` function takes `arg` which must either be one of: - a path to an already existing empty directory, - a non-existent path which can be created as a directory, or - `nothing` in which case a temporary directory is created. In all cases the path to the directory is returned. If an error occurs during `f(arg)`, the directory is returned to its original state: if it already existed but was empty, it will be emptied; if it did not exist it will be deleted. """ function arg_mkdir(f::Function, arg::Union{AbstractString, Nothing}) existed = false if arg === nothing arg = mktempdir() else st = stat(arg) if !ispath(st) mkdir(arg) elseif !isdir(st) error("arg_mkdir: $(repr(arg)) not a directory") else isempty(readdir(arg)) || error("arg_mkdir: $(repr(arg)) directory not empty") existed = true end end try f(arg) catch if existed for name in readdir(arg) path = joinpath(arg, name) prepare_for_deletion(path) rm(path, force=true, recursive=true) end else prepare_for_deletion(arg) rm(arg, force=true, recursive=true) end rethrow() end return arg end ## test utilities ## const ARG_READERS = [ String => path -> f -> f(path) Cmd => path -> f -> f(`cat $path`) CmdRedirect => path -> f -> f(pipeline(path, `cat`)) IOStream => path -> f -> open(f, path) Process => path -> f -> open(f, `cat $path`) ] const ARG_WRITERS = [ String => path -> f -> f(path) Cmd => path -> f -> f(`tee $path`) CmdRedirect => path -> f -> f(pipeline(`cat`, path)) IOStream => path -> f -> open(f, path, write=true) Process => path -> f -> open(f, pipeline(`cat`, path), write=true) ] @assert all(t <: ArgRead for t in map(first, ARG_READERS)) @assert all(t <: ArgWrite for t in map(first, ARG_WRITERS)) """ arg_readers(arg :: AbstractString, [ type = ArgRead ]) do arg::Function ## pre-test setup ## @arg_test arg begin arg :: ArgRead ## test using `arg` ## end ## post-test cleanup ## end The `arg_readers` function takes a path to be read and a single-argument do block, which is invoked once for each test reader type that `arg_read` can handle. If the optional `type` argument is given then the do block is only invoked for readers that produce arguments of that type. The `arg` passed to the do block is not the argument value itself, because some of test argument types need to be initialized and finalized for each test case. Consider an open file handle argument: once you've used it for one test, you can't use it again; you need to close it and open the file again for the next test. This function `arg` can be converted into an `ArgRead` instance using `@arg_test arg begin ... end`. """ function arg_readers( body::Function, path::AbstractString, type::Type = ArgRead, ) for (t, reader) in ARG_READERS t <: type || continue body(reader(path)) end end """ arg_writers([ type = ArgWrite ]) do path::String, arg::Function ## pre-test setup ## @arg_test arg begin arg :: ArgWrite ## test using `arg` ## end ## post-test cleanup ## end The `arg_writers` function takes a do block, which is invoked once for each test writer type that `arg_write` can handle with a temporary (non-existent) `path` and `arg` which can be converted into various writable argument types which write to `path`. If the optional `type` argument is given then the do block is only invoked for writers that produce arguments of that type. The `arg` passed to the do block is not the argument value itself, because some of test argument types need to be initialized and finalized for each test case. Consider an open file handle argument: once you've used it for one test, you can't use it again; you need to close it and open the file again for the next test. This function `arg` can be converted into an `ArgWrite` instance using `@arg_test arg begin ... end`. There is also an `arg_writers` method that takes a path name like `arg_readers`: arg_writers(path::AbstractString, [ type = ArgWrite ]) do arg::Function ## pre-test setup ## @arg_test arg begin # here `arg :: ArgWrite` ## test using `arg` ## end ## post-test cleanup ## end This method is useful if you need to specify `path` instead of using path name generated by `tempname()`. Since `path` is passed from outside of `arg_writers`, the path is not an argument to the do block in this form. """ function arg_writers( body::Function, type::Type = ArgWrite, ) for (t, writer) in ARG_WRITERS t <: type || continue path = tempname() try body(path, writer(path)) finally rm(path, force=true) end end end function arg_writers( body::Function, path::AbstractString, type::Type = ArgWrite, ) for (t, writer) in ARG_WRITERS t <: type || continue body(writer(path)) end end """ @arg_test arg1 arg2 ... body The `@arg_test` macro is used to convert `arg` functions provided by `arg_readers` and `arg_writers` into actual argument values. When you write `@arg_test arg body` it is equivalent to `arg(arg -> body)`. """ macro arg_test(args...) arg_test(args...) end function arg_test(var::Symbol, args...) var = esc(var) body = arg_test(args...) :($var($var -> $body)) end arg_test(ex::Expr) = esc(ex) end # module v/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Artifacts/src/Artifacts.jl@w# This file is a part of Julia. License is MIT: https://julialang.org/license """ Artifacts.jl is a Julia module that is used for managing and accessing artifacts in Julia packages. Artifacts are containers for platform-specific binaries, datasets, text, or any other kind of data that would be convenient to place within an immutable, life-cycled datastore. """ module Artifacts import Base: get, SHA1 using Base.BinaryPlatforms, Base.TOML export artifact_exists, artifact_path, artifact_meta, artifact_hash, select_downloadable_artifacts, find_artifacts_toml, @artifact_str """ parse_toml(path::String) Uses Base.TOML to parse a TOML file. Do not mutate the returned dictionary. """ function parse_toml(path::String) # Uses the caching mechanics for toml files in Base Base.parsed_toml(path) end # keep in sync with Base.project_names and Base.manifest_names const artifact_names = ("JuliaArtifacts.toml", "Artifacts.toml") const ARTIFACTS_DIR_OVERRIDE = Ref{Union{String,Nothing}}(nothing) """ with_artifacts_directory(f::Function, artifacts_dir::String) Helper function to allow temporarily changing the artifact installation and search directory. When this is set, no other directory will be searched for artifacts, and new artifacts will be installed within this directory. Similarly, removing an artifact will only effect the given artifact directory. To layer artifact installation locations, use the typical Julia depot path mechanism. """ function with_artifacts_directory(f::Function, artifacts_dir::String) try ARTIFACTS_DIR_OVERRIDE[] = artifacts_dir f() finally ARTIFACTS_DIR_OVERRIDE[] = nothing end end """ artifacts_dirs(args...) Return a list of paths joined into all possible artifacts directories, as dictated by the current set of depot paths and the current artifact directory override via the method `with_artifacts_dir()`. """ function artifacts_dirs(args...) if ARTIFACTS_DIR_OVERRIDE[] === nothing return String[abspath(depot, "artifacts", args...) for depot in Base.DEPOT_PATH] else # If we've been given an override, use _only_ that directory. return String[abspath(ARTIFACTS_DIR_OVERRIDE[]::String, args...)] end end # Recursive function, let's not make this a closure because it then has to # be boxed. function parse_mapping(mapping::String, name::String, override_file::String) if !isabspath(mapping) && !isempty(mapping) mapping = tryparse(Base.SHA1, mapping) if mapping === nothing @error("Invalid override in '$(override_file)': entry '$(name)' must map to an absolute path or SHA1 hash!") end end return mapping end function parse_mapping(mapping::Dict, name::String, override_file::String) return Dict(k => parse_mapping(v, name, override_file) for (k, v) in mapping) end # Fallthrough for invalid Overrides.toml files parse_mapping(mapping, name::String, override_file::String) = nothing """ ARTIFACT_OVERRIDES Artifact locations can be overridden by writing `Overrides.toml` files within the artifact directories of Pkg depots. For example, in the default depot `~/.julia`, one may create a `~/.julia/artifacts/Overrides.toml` file with the following contents: 78f35e74ff113f02274ce60dab6e92b4546ef806 = "/path/to/replacement" c76f8cda85f83a06d17de6c57aabf9e294eb2537 = "fb886e813a4aed4147d5979fcdf27457d20aa35d" [d57dbccd-ca19-4d82-b9b8-9d660942965b] c_simple = "/path/to/c_simple_dir" libfoo = "fb886e813a4aed4147d5979fcdf27457d20aa35d"" This file defines four overrides; two which override specific artifacts identified through their content hashes, two which override artifacts based on their bound names within a particular package's UUID. In both cases, there are two different targets of the override: overriding to an on-disk location through an absolute path, and overriding to another artifact by its content-hash. """ const ARTIFACT_OVERRIDES = Ref{Union{Dict{Symbol,Any},Nothing}}(nothing) function load_overrides(;force::Bool = false)::Dict{Symbol, Any} if ARTIFACT_OVERRIDES[] !== nothing && !force return ARTIFACT_OVERRIDES[] end # We organize our artifact location overrides into two camps: # - overrides per UUID with artifact names mapped to a new location # - overrides per hash, mapped to a new location. # # Overrides per UUID/bound name are intercepted upon Artifacts.toml load, and new # entries within the "hash" overrides are generated on-the-fly. Thus, all redirects # mechanistically happen through the "hash" overrides. overrides = Dict{Symbol,Any}( # Overrides by UUID :UUID => Dict{Base.UUID,Dict{String,Union{String,SHA1}}}(), # Overrides by hash :hash => Dict{SHA1,Union{String,SHA1}}(), ) for override_file in reverse(artifacts_dirs("Overrides.toml")) !isfile(override_file) && continue # Load the toml file depot_override_dict = parse_toml(override_file) for (k, mapping) in depot_override_dict # First, parse the mapping. Is it an absolute path, a valid SHA1-hash, or neither? mapping = parse_mapping(mapping, k, override_file) if mapping === nothing @error("Invalid override in '$(override_file)': failed to parse entry `$(k)`") continue end # Next, determine if this is a hash override or a UUID/name override if isa(mapping, String) || isa(mapping, SHA1) # if this mapping is a direct mapping (e.g. a String), store it as a hash override local hash_str hash = tryparse(Base.SHA1, k) if hash === nothing @error("Invalid override in '$(override_file)': Invalid SHA1 hash '$(k)'") continue end # If this mapping is the empty string, un-override it if mapping == "" delete!(overrides[:hash], hash) else overrides[:hash][hash] = mapping end elseif isa(mapping, Dict) # Convert `k` into a uuid uuid = tryparse(Base.UUID, k) if uuid === nothing @error("Invalid override in '$(override_file)': Invalid UUID '$(k)'") continue end # If this mapping is itself a dict, store it as a set of UUID/artifact name overrides ovruuid = overrides[:UUID]::Dict{Base.UUID,Dict{String,Union{String,SHA1}}} if !haskey(ovruuid, uuid) ovruuid[uuid] = Dict{String,Union{String,SHA1}}() end # For each name in the mapping, update appropriately for (name, override_value) in mapping # If the mapping for this name is the empty string, un-override it if override_value == "" delete!(ovruuid[uuid], name) else # Otherwise, store it! ovruuid[uuid][name] = override_value end end else @error("Invalid override in '$(override_file)': unknown mapping type for '$(k)': $(typeof(mapping))") end end end ARTIFACT_OVERRIDES[] = overrides return overrides end # Helpers to map an override to an actual path map_override_path(x::AbstractString) = String(x)::String map_override_path(x::SHA1) = artifact_path(x) map_override_path(x::Nothing) = nothing """ query_override(hash::SHA1; overrides::Dict = load_overrides()) Query the loaded `/artifacts/Overrides.toml` settings for artifacts that should be redirected to a particular path or another content-hash. """ function query_override(hash::SHA1; overrides::Dict{Symbol,Any} = load_overrides()) return map_override_path(get(overrides[:hash], hash, nothing)) end function query_override(pkg::Base.UUID, artifact_name::String; overrides::Dict{Symbol,Any} = load_overrides()) if haskey(overrides[:UUID], pkg) return map_override_path(get(overrides[:UUID][pkg], artifact_name, nothing)) end return nothing end """ artifact_paths(hash::SHA1; honor_overrides::Bool=true) Return all possible paths for an artifact given the current list of depots as returned by `Pkg.depots()`. All, some or none of these paths may exist on disk. """ function artifact_paths(hash::SHA1; honor_overrides::Bool=true) # First, check to see if we've got an override: if honor_overrides override = query_override(hash) if override !== nothing return [override] end end return artifacts_dirs(bytes2hex(hash.bytes)) end """ artifact_path(hash::SHA1; honor_overrides::Bool=true) Given an artifact (identified by SHA1 git tree hash), return its installation path. If the artifact does not exist, returns the location it would be installed to. !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ function artifact_path(hash::SHA1; honor_overrides::Bool=true) # Get all possible paths (rooted in all depots) possible_paths = artifact_paths(hash; honor_overrides=honor_overrides) # Find the first path that exists and return it for p in possible_paths if isdir(p) return p end end # If none exist, then just return the one that would exist within `depots1()`. return first(possible_paths) end """ artifact_exists(hash::SHA1; honor_overrides::Bool=true) Return whether or not the given artifact (identified by its sha1 git tree hash) exists on-disk. Note that it is possible that the given artifact exists in multiple locations (e.g. within multiple depots). !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ function artifact_exists(hash::SHA1; honor_overrides::Bool=true) return any(isdir, artifact_paths(hash; honor_overrides=honor_overrides)) end """ unpack_platform(entry::Dict, name::String, artifacts_toml::String) Given an `entry` for the artifact named `name`, located within the file `artifacts_toml`, returns the `Platform` object that this entry specifies. Returns `nothing` on error. """ function unpack_platform(entry::Dict{String,Any}, name::String, artifacts_toml::String)::Union{Nothing,Platform} if !haskey(entry, "os") @error("Invalid artifacts file at '$(artifacts_toml)': platform-specific artifact entry '$name' missing 'os' key") return nothing end if !haskey(entry, "arch") @error("Invalid artifacts file at '$(artifacts_toml)': platform-specific artifact entry '$name' missing 'arch' key") return nothing end # Collect all String-valued mappings in `entry` and use them as tags tags = Dict{String, String}() for (k, v) in entry if v isa String tags[k] = v end end # Removing some known entries that shouldn't be passed through `tags` delete!(tags, "os") delete!(tags, "arch") delete!(tags, "git-tree-sha1") return Platform(entry["arch"], entry["os"], tags) end function pack_platform!(meta::Dict, p::AbstractPlatform) for (k, v) in tags(p) if v !== nothing meta[k] = v end end return meta end """ load_artifacts_toml(artifacts_toml::String; pkg_uuid::Union{UUID,Nothing}=nothing) Loads an `(Julia)Artifacts.toml` file from disk. If `pkg_uuid` is set to the `UUID` of the owning package, UUID/name overrides stored in a depot `Overrides.toml` will be resolved. """ function load_artifacts_toml(artifacts_toml::String; pkg_uuid::Union{Base.UUID,Nothing} = nothing) artifact_dict = parse_toml(artifacts_toml) # Process overrides for this `pkg_uuid` process_overrides(artifact_dict, pkg_uuid) return artifact_dict end """ process_overrides(artifact_dict::Dict, pkg_uuid::Base.UUID) When loading an `Artifacts.toml` file, we must check `Overrides.toml` files to see if any of the artifacts within it have been overridden by UUID. If they have, we honor the overrides by inspecting the hashes of the targeted artifacts, then overriding them to point to the given override, punting the actual redirection off to the hash-based override system. This does not modify the `artifact_dict` object, it merely dynamically adds more hash-based overrides as `Artifacts.toml` files that are overridden are loaded. """ function process_overrides(artifact_dict::Dict, pkg_uuid::Base.UUID) # Insert just-in-time hash overrides by looking up the names of anything we need to # override for this UUID, and inserting new overrides for those hashes. overrides = load_overrides() if haskey(overrides[:UUID], pkg_uuid) pkg_overrides = overrides[:UUID][pkg_uuid]::Dict{String, <:Any} for name in keys(artifact_dict) # Skip names that we're not overriding if !haskey(pkg_overrides, name) continue end # If we've got a platform-specific friend, override all hashes: if isa(artifact_dict[name], Array) for entry in artifact_dict[name] hash = SHA1(entry["git-tree-sha1"]) overrides[:hash][hash] = overrides[:UUID][pkg_uuid][name] end elseif isa(artifact_dict[name], Dict) hash = SHA1(artifact_dict[name]["git-tree-sha1"]) overrides[:hash][hash] = overrides[:UUID][pkg_uuid][name] end end end return artifact_dict end # If someone tries to call process_overrides() with `nothing`, do exactly that process_overrides(artifact_dict::Dict, pkg_uuid::Nothing) = nothing """ artifact_meta(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Base.UUID,Nothing}=nothing) Get metadata about a given artifact (identified by name) stored within the given `(Julia)Artifacts.toml` file. If the artifact is platform-specific, use `platform` to choose the most appropriate mapping. If none is found, return `nothing`. !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ function artifact_meta(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Base.UUID,Nothing}=nothing) if !isfile(artifacts_toml) return nothing end # Parse the toml of the artifacts_toml file artifact_dict = load_artifacts_toml(artifacts_toml; pkg_uuid=pkg_uuid) return artifact_meta(name, artifact_dict, artifacts_toml; platform=platform) end function artifact_meta(name::String, artifact_dict::Dict, artifacts_toml::String; platform::AbstractPlatform = HostPlatform()) if !haskey(artifact_dict, name) return nothing end meta = artifact_dict[name] # If it's an array, find the entry that best matches our current platform if isa(meta, Vector) dl_dict = Dict{AbstractPlatform,Dict{String,Any}}() for x in meta x::Dict{String} dl_dict[unpack_platform(x, name, artifacts_toml)] = x end meta = select_platform(dl_dict, platform) # If it's NOT a dict, complain elseif !isa(meta, Dict) @error("Invalid artifacts file at $(artifacts_toml): artifact '$name' malformed, must be array or dict!") return nothing end # This is such a no-no, we are going to call it out right here, right now. if meta !== nothing && !haskey(meta, "git-tree-sha1") @error("Invalid artifacts file at $(artifacts_toml): artifact '$name' contains no `git-tree-sha1`!") return nothing end # Return the full meta-dict. return meta end """ artifact_hash(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform()) Thin wrapper around `artifact_meta()` to return the hash of the specified, platform- collapsed artifact. Returns `nothing` if no mapping can be found. !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ function artifact_hash(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Base.UUID,Nothing}=nothing)::Union{Nothing, SHA1} meta = artifact_meta(name, artifacts_toml; platform=platform) if meta === nothing return nothing end return SHA1(meta["git-tree-sha1"]) end function select_downloadable_artifacts(artifact_dict::Dict, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Nothing,Base.UUID} = nothing, include_lazy::Bool = false) artifacts = Dict{String,Any}() for name in keys(artifact_dict) # Get the metadata about this name for the requested platform meta = artifact_meta(name, artifact_dict, artifacts_toml; platform=platform) # If there are no instances of this name for the desired platform, skip it # Also skip if there's no `download` stanza (e.g. it's only a local artifact) # or if it's lazy and we're not explicitly looking for lazy artifacts. if meta === nothing || !haskey(meta, "download") || (get(meta, "lazy", false) && !include_lazy) continue end # Else, welcome it into the meta-fold artifacts[name] = meta end return artifacts end """ select_downloadable_artifacts(artifacts_toml::String; platform = HostPlatform, include_lazy = false, pkg_uuid = nothing) Return a dictionary where every entry is an artifact from the given `Artifacts.toml` that should be downloaded for the requested platform. Lazy artifacts are included if `include_lazy` is set. """ function select_downloadable_artifacts(artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), include_lazy::Bool = false, pkg_uuid::Union{Nothing,Base.UUID} = nothing) if !isfile(artifacts_toml) return Dict{String,Any}() end artifact_dict = load_artifacts_toml(artifacts_toml; pkg_uuid=pkg_uuid) return select_downloadable_artifacts(artifact_dict, artifacts_toml; platform, pkg_uuid, include_lazy) end """ find_artifacts_toml(path::String) Given the path to a `.jl` file, (such as the one returned by `__source__.file` in a macro context), find the `(Julia)Artifacts.toml` that is contained within the containing project (if it exists), otherwise return `nothing`. !!! compat "Julia 1.3" This function requires at least Julia 1.3. """ function find_artifacts_toml(path::String) if !isdir(path) path = dirname(path) end # Run until we hit the root directory. while dirname(path) != path for f in artifact_names artifacts_toml_path = joinpath(path, f) if isfile(artifacts_toml_path) return abspath(artifacts_toml_path) end end # Does a `(Julia)Project.toml` file exist here, in the absence of an Artifacts.toml? # If so, stop the search as we've probably hit the top-level of this package, # and we don't want to escape out into the larger filesystem. for f in Base.project_names if isfile(joinpath(path, f)) return nothing end end # Move up a directory path = dirname(path) end # We never found anything, just return `nothing` return nothing end # We do this to avoid doing the `joinpath()` work if we don't have to, and also to # avoid a trailing slash due to `joinpath()`'s habit of including one when the last # argument is the empty string. function jointail(dir, tail) if !isempty(tail) return joinpath(dir, tail) else return dir end end function _artifact_str(__module__, artifacts_toml, name, path_tail, artifact_dict, hash, platform, @nospecialize(lazyartifacts)) moduleroot = Base.moduleroot(__module__) if haskey(Base.module_keys, moduleroot) # Process overrides for this UUID, if we know what it is process_overrides(artifact_dict, Base.module_keys[moduleroot].uuid) end # If the artifact exists, we're in the happy path and we can immediately # return the path to the artifact: dirs = artifact_paths(hash; honor_overrides=true) for dir in dirs if isdir(dir) return jointail(dir, path_tail) end end # If not, try determining what went wrong: meta = artifact_meta(name, artifact_dict, artifacts_toml; platform) if meta !== nothing && get(meta, "lazy", false) if lazyartifacts isa Module && isdefined(lazyartifacts, :ensure_artifact_installed) if nameof(lazyartifacts) in (:Pkg, :Artifacts) Base.depwarn("using Pkg instead of using LazyArtifacts is deprecated", :var"@artifact_str", force=true) end return jointail(lazyartifacts.ensure_artifact_installed(string(name), meta, artifacts_toml; platform), path_tail) end error("Artifact $(repr(name)) is a lazy artifact; package developers must call `using LazyArtifacts` in $(__module__) before using lazy artifacts.") end path_str = if length(dirs) == 1 "path \"$(first(dirs))\". " else string("paths:\n", join(" " .* contractuser.(dirs), '\n'), '\n') end suggestion_str = if query_override(hash) !== nothing "Check that your `Overrides.toml` file is correct (https://pkgdocs.julialang.org/v1/artifacts/#Overriding-artifact-locations)." else "Try `using Pkg; Pkg.instantiate()` to re-install all missing resources if the artifact is part of a package \ or call `Pkg.ensure_artifact_installed` (https://pkgdocs.julialang.org/v1/api/#Pkg.Artifacts.ensure_artifact_installed) if not." end error("Artifact $(repr(name)) was not found by looking in the $(path_str)$suggestion_str") end raw""" split_artifact_slash(name::String) Splits an artifact indexing string by path delimiters, isolates the first path element, returning that and the `joinpath()` of the remaining arguments. This normalizes all path separators to the native path separator for the current platform. Examples: # Examples ```jldoctest; setup = :(using Artifacts: split_artifact_slash) julia> split_artifact_slash("Foo") ("Foo", "") julia> ret = split_artifact_slash("Foo/bar/baz.so"); julia> if Sys.iswindows() ret == ("Foo", "bar\\baz.so") else ret == ("Foo", "bar/baz.so") end true julia> ret = split_artifact_slash("Foo\\bar\\baz.so"); julia> if Sys.iswindows() ret == ("Foo", "bar\\baz.so") else ret == ("Foo", "bar/baz.so") end true ``` """ function split_artifact_slash(name::String) split_name = split(name, r"(/|\\)") if length(split_name) == 1 return (split_name[1], "") else return (split_name[1], joinpath(split_name[2:end]...)) end end """ artifact_slash_lookup(name::String, atifact_dict::Dict, artifacts_toml::String, platform::Platform) Return `artifact_name`, `artifact_path_tail`, and `hash` by looking the results up in the given `artifacts_toml`, first extracting the name and path tail from the given `name` to support slash-indexing within the given artifact. """ function artifact_slash_lookup(name::String, artifact_dict::Dict, artifacts_toml::String, platform::Platform) artifact_name, artifact_path_tail = split_artifact_slash(name) meta = artifact_meta(artifact_name, artifact_dict, artifacts_toml; platform) if meta === nothing error("Cannot locate artifact '$(name)' for $(triplet(platform)) in '$(artifacts_toml)'") end hash = SHA1(meta["git-tree-sha1"]) return artifact_name, artifact_path_tail, hash end """ macro artifact_str(name) Return the on-disk path to an artifact. Automatically looks the artifact up by name in the project's `(Julia)Artifacts.toml` file. Throws an error on if the requested artifact is not present. If run in the REPL, searches for the toml file starting in the current directory, see `find_artifacts_toml()` for more. If the artifact is marked "lazy" and the package has `using LazyArtifacts` defined, the artifact will be downloaded on-demand with `Pkg` the first time this macro tries to compute the path. The files will then be left installed locally for later. If `name` contains a forward or backward slash, all elements after the first slash will be taken to be path names indexing into the artifact, allowing for an easy one-liner to access a single file/directory within an artifact. Example: ffmpeg_path = @artifact"FFMPEG/bin/ffmpeg" !!! compat "Julia 1.3" This macro requires at least Julia 1.3. !!! compat "Julia 1.6" Slash-indexing requires at least Julia 1.6. """ macro artifact_str(name, platform=nothing) # Find Artifacts.toml file we're going to load from srcfile = string(__source__.file) if ((isinteractive() && startswith(srcfile, "REPL[")) || (!isinteractive() && srcfile == "none")) && !isfile(srcfile) srcfile = pwd() end local artifacts_toml = find_artifacts_toml(srcfile) if artifacts_toml === nothing error(string( "Cannot locate '(Julia)Artifacts.toml' file when attempting to use artifact '", name, "' in '", __module__, "'", )) end # Load Artifacts.toml at compile time, so that we don't have to use `__source__.file` # at runtime, which gets stale if the `.ji` file is relocated. local artifact_dict = load_artifacts_toml(artifacts_toml) # Invalidate calling .ji file if Artifacts.toml file changes Base.include_dependency(artifacts_toml) # Check if the user has provided `LazyArtifacts`, and thus supports lazy artifacts # If not, check to see if `Pkg` or `Pkg.Artifacts` has been imported. lazyartifacts = nothing for module_name in (:LazyArtifacts, :Pkg, :Artifacts) if isdefined(__module__, module_name) lazyartifacts = GlobalRef(__module__, module_name) break end end # If `name` is a constant, (and we're using the default `Platform`) we can actually load # and parse the `Artifacts.toml` file now, saving the work from runtime. if isa(name, AbstractString) && platform === nothing # To support slash-indexing, we need to split the artifact name from the path tail: platform = HostPlatform() artifact_name, artifact_path_tail, hash = artifact_slash_lookup(name, artifact_dict, artifacts_toml, platform) return quote Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), $(artifact_name), $(artifact_path_tail), $(artifact_dict), $(hash), $(platform), $(lazyartifacts))::String end else if platform === nothing platform = :($(HostPlatform)()) end return quote local platform = $(esc(platform)) local artifact_name, artifact_path_tail, hash = artifact_slash_lookup($(esc(name)), $(artifact_dict), $(artifacts_toml), platform) Base.invokelatest(_artifact_str, $(__module__), $(artifacts_toml), artifact_name, artifact_path_tail, $(artifact_dict), hash, platform, $(lazyartifacts))::String end end end # Support `AbstractString`s, but avoid compilers needing to track backedges for callers # of these functions in case a user defines a new type that is `<: AbstractString` with_artifacts_directory(f::Function, artifacts_dir::AbstractString) = with_artifacts_directory(f, String(artifacts_dir)::String) query_override(pkg::Base.UUID, artifact_name::AbstractString; overrides::Dict=load_overrides()) = query_override(pkg, String(artifact_name)::String; overrides=convert(Dict{Symbol, Any}, overrides)) unpack_platform(entry::Dict, name::AbstractString, artifacts_toml::AbstractString) = unpack_platform(convert(Dict{String, Any}, entry), String(name)::String, String(artifacts_toml)::String) load_artifacts_toml(artifacts_toml::AbstractString; kwargs...) = load_artifacts_toml(String(artifacts_toml)::String; kwargs...) artifact_meta(name::AbstractString, artifacts_toml::AbstractString; kwargs...) = artifact_meta(String(name)::String, String(artifacts_toml)::String; kwargs...) artifact_meta(name::AbstractString, artifact_dict::Dict, artifacts_toml::AbstractString; kwargs...) = artifact_meta(String(name)::String, artifact_dict, String(artifacts_toml)::String; kwargs...) artifact_hash(name::AbstractString, artifacts_toml::AbstractString; kwargs...) = artifact_hash(String(name)::String, String(artifacts_toml)::String; kwargs...) select_downloadable_artifacts(artifact_dict::Dict, artifacts_toml::AbstractString; kwargs...) = select_downloadable_artifacts(artifact_dict, String(artifacts_toml)::String, kwargs...) select_downloadable_artifacts(artifacts_toml::AbstractString; kwargs...) = select_downloadable_artifacts(String(artifacts_toml)::String, kwargs...) find_artifacts_toml(path::AbstractString) = find_artifacts_toml(String(path)::String) split_artifact_slash(name::AbstractString) = split_artifact_slash(String(name)::String) artifact_slash_lookup(name::AbstractString, artifact_dict::Dict, artifacts_toml::AbstractString) = artifact_slash_lookup(String(name)::String, artifact_dict, String(artifacts_toml)::String) # Precompilation to reduce latency precompile(load_artifacts_toml, (String,)) precompile(NamedTuple{(:pkg_uuid,)}, (Tuple{Base.UUID},)) precompile(Core.kwfunc(load_artifacts_toml), (NamedTuple{(:pkg_uuid,), Tuple{Base.UUID}}, typeof(load_artifacts_toml), String)) precompile(parse_mapping, (String, String, String)) precompile(parse_mapping, (Dict{String, Any}, String, String)) end # module Artifacts p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Base64/src/Base64.jls# This file is a part of Julia. License is MIT: https://julialang.org/license """ Base64 Functionality for [base64 encoding and decoding](https://en.wikipedia.org/wiki/Base64), a method to represent binary data using text, common on the web. """ module Base64 using Base: require_one_based_indexing export Base64EncodePipe, base64encode, Base64DecodePipe, base64decode, stringmime # Base64EncodePipe is a pipe-like IO object, which converts into base64 data # sent to a stream. (You must close the pipe to complete the encode, separate # from closing the target stream). We also have a function base64encode(f, # args...) which works like sprint except that it produces base64-encoded data, # along with base64encode(args...) which is equivalent to base64encode(write, # args...), to return base64 strings. A Base64DecodePipe object can be used to # decode base64-encoded data read from a stream , while function base64decode is # useful for decoding strings include("buffer.jl") include("encode.jl") include("decode.jl") """ stringmime(mime, x; context=nothing) Return an `AbstractString` containing the representation of `x` in the requested `mime` type. This is similar to [`repr(mime, x)`](@ref) except that binary data is base64-encoded as an ASCII string. The optional keyword argument `context` can be set to `:key=>value` pair or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O stream passed to [`show`](@ref). """ stringmime(m::MIME, x; context=nothing) = istextmime(m) ? Base.Multimedia._textrepr(m, x, context) : _binstringmime(m, x, context) stringmime(m::AbstractString, x; context=nothing) = stringmime(MIME(m), x; context=context) _binstringmime(m::MIME, x, context) = Base64.base64encode(show, m, x; context=context) _binstringmime(m::MIME, x::Vector{UInt8}, context) = Base64.base64encode(write, x; context=context) end p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Base64/src/buffer.jl# This file is a part of Julia. License is MIT: https://julialang.org/license # Data buffer for pipes. mutable struct Buffer data::Vector{UInt8} ptr::Ptr{UInt8} size::Int function Buffer(bufsize) data = Vector{UInt8}(undef, bufsize) return new(data, pointer(data), 0) end end Base.empty!(buffer::Buffer) = buffer.size = 0 Base.getindex(buffer::Buffer, i::Integer) = unsafe_load(buffer.ptr, i) Base.setindex!(buffer::Buffer, v::UInt8, i::Integer) = unsafe_store!(buffer.ptr, v, i) Base.firstindex(buffer::Buffer) = 1 Base.lastindex(buffer::Buffer) = buffer.size Base.pointer(buffer::Buffer) = buffer.ptr capacity(buffer::Buffer) = Int(pointer(buffer.data, lastindex(buffer.data) + 1) - buffer.ptr) function consumed!(buffer::Buffer, n::Integer) @assert n โ‰ค buffer.size buffer.ptr += n buffer.size -= n end function read_to_buffer(io::IO, buffer::Buffer) offset = buffer.ptr - pointer(buffer.data) copyto!(buffer.data, 1, buffer.data, offset + 1, buffer.size) buffer.ptr = pointer(buffer.data) if !eof(io) n = min(bytesavailable(io), capacity(buffer) - buffer.size) unsafe_read(io, buffer.ptr + buffer.size, n) buffer.size += n end return end p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Base64/src/encode.jlb# This file is a part of Julia. License is MIT: https://julialang.org/license # Generate encode table. const BASE64_ENCODE = [UInt8(x) for x in append!(['A':'Z'; 'a':'z'; '0':'9'], ['+', '/'])] encode(x::UInt8) = @inbounds return BASE64_ENCODE[(x & 0x3f) + 1] encodepadding() = UInt8('=') """ Base64EncodePipe(ostream) Return a new write-only I/O stream, which converts any bytes written to it into base64-encoded ASCII bytes written to `ostream`. Calling [`close`](@ref) on the `Base64EncodePipe` stream is necessary to complete the encoding (but does not close `ostream`). # Examples ```jldoctest julia> io = IOBuffer(); julia> iob64_encode = Base64EncodePipe(io); julia> write(iob64_encode, "Hello!") 6 julia> close(iob64_encode); julia> str = String(take!(io)) "SGVsbG8h" julia> String(base64decode(str)) "Hello!" ``` """ struct Base64EncodePipe <: IO io::IO buffer::Buffer function Base64EncodePipe(io::IO) # The buffer size must be at least 3. buffer = Buffer(512) pipe = new(io, buffer) finalizer(_ -> close(pipe), buffer) return pipe end end Base.isreadable(::Base64EncodePipe) = false Base.iswritable(pipe::Base64EncodePipe) = iswritable(pipe.io) function Base.unsafe_write(pipe::Base64EncodePipe, ptr::Ptr{UInt8}, n::UInt)::Int buffer = pipe.buffer m = buffer.size b1, b2, b3, k = loadtriplet!(buffer, ptr, n) @assert k โ‰ฅ m p = ptr + k - m if k < 3 if k == 1 buffer[1] = b1 buffer.size = 1 elseif k == 2 buffer[1] = b1 buffer[2] = b2 buffer.size = 2 end return p - ptr end @assert buffer.size == 0 i = 0 p_end = ptr + n while true buffer[i+1] = encode(b1 >> 2 ) buffer[i+2] = encode(b1 << 4 | b2 >> 4) buffer[i+3] = encode(b2 << 2 | b3 >> 6) buffer[i+4] = encode( b3 ) i += 4 if p + 2 < p_end b1 = unsafe_load(p, 1) b2 = unsafe_load(p, 2) b3 = unsafe_load(p, 3) p += 3 else break end if i + 4 > capacity(buffer) unsafe_write(pipe.io, pointer(buffer), i) i = 0 end end if i > 0 unsafe_write(pipe.io, pointer(buffer), i) end while p < p_end buffer[buffer.size+=1] = unsafe_load(p) p += 1 end return p - ptr end function Base.write(pipe::Base64EncodePipe, x::UInt8) buffer = pipe.buffer buffer[buffer.size+=1] = x if buffer.size == 3 unsafe_write(pipe, C_NULL, 0) end return 1 end function Base.close(pipe::Base64EncodePipe) b1, b2, b3, k = loadtriplet!(pipe.buffer, Ptr{UInt8}(C_NULL), UInt(0)) if k == 0 # no leftover and padding elseif k == 1 write(pipe.io, encode(b1 >> 2), encode(b1 << 4), encodepadding(), encodepadding()) elseif k == 2 write(pipe.io, encode( b1 >> 2), encode(b1 << 4 | b2 >> 4), encode(b2 << 2 ), encodepadding()) else @assert k == 3 write(pipe.io, encode(b1 >> 2 ), encode(b1 << 4 | b2 >> 4), encode(b2 << 2 | b3 >> 6), encode( b3 )) end return nothing end # Load three bytes from buffer and ptr. function loadtriplet!(buffer::Buffer, ptr::Ptr{UInt8}, n::UInt) b1 = b2 = b3 = 0x00 if buffer.size == 0 if n == 0 k = 0 elseif n == 1 b1 = unsafe_load(ptr, 1) k = 1 elseif n == 2 b1 = unsafe_load(ptr, 1) b2 = unsafe_load(ptr, 2) k = 2 else b1 = unsafe_load(ptr, 1) b2 = unsafe_load(ptr, 2) b3 = unsafe_load(ptr, 3) k = 3 end elseif buffer.size == 1 b1 = buffer[1] if n == 0 k = 1 elseif n == 1 b2 = unsafe_load(ptr, 1) k = 2 else b2 = unsafe_load(ptr, 1) b3 = unsafe_load(ptr, 2) k = 3 end elseif buffer.size == 2 b1 = buffer[1] b2 = buffer[2] if n == 0 k = 2 else b3 = unsafe_load(ptr, 1) k = 3 end else @assert buffer.size == 3 b1 = buffer[1] b2 = buffer[2] b3 = buffer[3] k = 3 end empty!(buffer) return b1, b2, b3, k end """ base64encode(writefunc, args...; context=nothing) base64encode(args...; context=nothing) Given a [`write`](@ref)-like function `writefunc`, which takes an I/O stream as its first argument, `base64encode(writefunc, args...)` calls `writefunc` to write `args...` to a base64-encoded string, and returns the string. `base64encode(args...)` is equivalent to `base64encode(write, args...)`: it converts its arguments into bytes using the standard [`write`](@ref) functions and returns the base64-encoded string. The optional keyword argument `context` can be set to `:key=>value` pair or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O stream passed to `writefunc` or `write`. See also [`base64decode`](@ref). """ function base64encode(f::Function, args...; context=nothing) s = IOBuffer() b = Base64EncodePipe(s) if context === nothing f(b, args...) else f(IOContext(b, context), args...) end close(b) return String(take!(s)) end base64encode(args...; context=nothing) = base64encode(write, args...; context=context) p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Base64/src/decode.jl้# This file is a part of Julia. License is MIT: https://julialang.org/license # Generate decode table. const BASE64_CODE_END = 0x40 const BASE64_CODE_PAD = 0x41 const BASE64_CODE_IGN = 0x42 const BASE64_DECODE = fill(BASE64_CODE_IGN, 256) for (i, c) in enumerate(BASE64_ENCODE) BASE64_DECODE[Int(c)+1] = UInt8(i - 1) end BASE64_DECODE[Int(encodepadding())+1] = BASE64_CODE_PAD decode(x::UInt8) = @inbounds return BASE64_DECODE[x + 1] """ Base64DecodePipe(istream) Return a new read-only I/O stream, which decodes base64-encoded data read from `istream`. # Examples ```jldoctest julia> io = IOBuffer(); julia> iob64_decode = Base64DecodePipe(io); julia> write(io, "SGVsbG8h") 8 julia> seekstart(io); julia> String(read(iob64_decode)) "Hello!" ``` """ struct Base64DecodePipe <: IO io::IO buffer::Buffer rest::Vector{UInt8} function Base64DecodePipe(io::IO) buffer = Buffer(512) return new(io, buffer, UInt8[]) end end Base.isreadable(pipe::Base64DecodePipe) = !isempty(pipe.rest) || isreadable(pipe.io) Base.iswritable(::Base64DecodePipe) = false function Base.unsafe_read(pipe::Base64DecodePipe, ptr::Ptr{UInt8}, n::UInt) p = read_until_end(pipe, ptr, n) if p < ptr + n throw(EOFError()) end return nothing end # Read and decode as much data as possible. function read_until_end(pipe::Base64DecodePipe, ptr::Ptr{UInt8}, n::UInt) p = ptr p_end = ptr + n while !isempty(pipe.rest) && p < p_end unsafe_store!(p, popfirst!(pipe.rest)) p += 1 end buffer = pipe.buffer i = 0 b1 = b2 = b3 = b4 = BASE64_CODE_IGN while true if b1 < 0x40 && b2 < 0x40 && b3 < 0x40 && b4 < 0x40 && p + 2 < p_end # fast path to decode unsafe_store!(p , b1 << 2 | b2 >> 4) unsafe_store!(p + 1, b2 << 4 | b3 >> 2) unsafe_store!(p + 2, b3 << 6 | b4 ) p += 3 else i, p, ended = decode_slow(b1, b2, b3, b4, buffer, i, pipe.io, p, p_end - p, pipe.rest) if ended break end end if p < p_end if i + 4 โ‰ค lastindex(buffer) b1 = decode(buffer[i+1]) b2 = decode(buffer[i+2]) b3 = decode(buffer[i+3]) b4 = decode(buffer[i+4]) i += 4 else consumed!(buffer, i) read_to_buffer(pipe.io, buffer) i = 0 b1 = b2 = b3 = b4 = BASE64_CODE_IGN end else break end end consumed!(buffer, i) return p end function Base.read(pipe::Base64DecodePipe, ::Type{UInt8}) if isempty(pipe.rest) unsafe_read(pipe, convert(Ptr{UInt8}, C_NULL), 0) if isempty(pipe.rest) throw(EOFError()) end end return popfirst!(pipe.rest) end function Base.readbytes!(pipe::Base64DecodePipe, data::AbstractVector{UInt8}, nb::Integer=length(data)) require_one_based_indexing(data) filled::Int = 0 while filled < nb && !eof(pipe) if length(data) == filled resize!(data, min(length(data) * 2, nb)) end p = pointer(data, filled + 1) p_end = read_until_end(pipe, p, UInt(min(length(data), nb) - filled)) filled += p_end - p end resize!(data, filled) return filled end Base.eof(pipe::Base64DecodePipe) = isempty(pipe.rest) && eof(pipe.io)::Bool Base.close(pipe::Base64DecodePipe) = nothing # Decode data from (b1, b2, b3, b5, buffer, input) into (ptr, rest). function decode_slow(b1, b2, b3, b4, buffer, i, input, ptr, n, rest) # Skip ignore code. while true if b1 == BASE64_CODE_IGN b1, b2, b3 = b2, b3, b4 elseif b2 == BASE64_CODE_IGN b2, b3 = b3, b4 elseif b3 == BASE64_CODE_IGN b3 = b4 elseif b4 == BASE64_CODE_IGN # pass else break end if i + 1 โ‰ค lastindex(buffer) b4 = decode(buffer[i+=1]) elseif !eof(input) b4 = decode(read(input, UInt8)) else b4 = BASE64_CODE_END end end # Check the decoded quadruplet. k = 0 if b1 < 0x40 && b2 < 0x40 && b3 < 0x40 && b4 < 0x40 k = 3 elseif b1 < 0x40 && b2 < 0x40 && b3 < 0x40 && (b4 == BASE64_CODE_PAD || b4 == BASE64_CODE_END) b4 = 0x00 k = 2 elseif b1 < 0x40 && b2 < 0x40 && (b3 == BASE64_CODE_PAD || b3 == BASE64_CODE_END) && (b4 == BASE64_CODE_PAD || b4 == BASE64_CODE_END) b3 = b4 = 0x00 k = 1 elseif b1 == b2 == b3 == b4 == BASE64_CODE_END b1 = b2 = b3 = b4 = 0x00 else throw(ArgumentError("malformed base64 sequence")) end # Write output. p::Ptr{UInt8} = ptr p_end = ptr + n function output(b) if p < p_end unsafe_store!(p, b) p += 1 else push!(rest, b) end end k โ‰ฅ 1 && output(b1 << 2 | b2 >> 4) k โ‰ฅ 2 && output(b2 << 4 | b3 >> 2) k โ‰ฅ 3 && output(b3 << 6 | b4 ) return i, p, k == 0 end """ base64decode(string) Decode the base64-encoded `string` and returns a `Vector{UInt8}` of the decoded bytes. See also [`base64encode`](@ref). # Examples ```jldoctest julia> b = base64decode("SGVsbG8h") 6-element Vector{UInt8}: 0x48 0x65 0x6c 0x6c 0x6f 0x21 julia> String(b) "Hello!" ``` """ function base64decode(s) b = IOBuffer(s) try return read(Base64DecodePipe(b)) finally close(b) end end p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/CRC32c/src/CRC32c.jl*# This file is a part of Julia. License is MIT: https://julialang.org/license """ Standard library module for computing the CRC-32c checksum. See [`CRC32c.crc32c`](@ref) for more information. """ module CRC32c import Base.FastContiguousSubArray export crc32c """ crc32c(data, crc::UInt32=0x00000000) Compute the CRC-32c checksum of the given `data`, which can be an `Array{UInt8}`, a contiguous subarray thereof, or a `String`. Optionally, you can pass a starting `crc` integer to be mixed in with the checksum. The `crc` parameter can be used to compute a checksum on data divided into chunks: performing `crc32c(data2, crc32c(data1))` is equivalent to the checksum of `[data1; data2]`. (Technically, a little-endian checksum is computed.) There is also a method `crc32c(io, nb, crc)` to checksum `nb` bytes from a stream `io`, or `crc32c(io, crc)` to checksum all the remaining bytes. Hence you can do [`open(crc32c, filename)`](@ref) to checksum an entire file, or `crc32c(seekstart(buf))` to checksum an [`IOBuffer`](@ref) without calling [`take!`](@ref). For a `String`, note that the result is specific to the UTF-8 encoding (a different checksum would be obtained from a different Unicode encoding). To checksum an `a::Array` of some other bitstype, you can do `crc32c(reinterpret(UInt8,a))`, but note that the result may be endian-dependent. """ function crc32c end crc32c(a::Union{Array{UInt8},FastContiguousSubArray{UInt8,N,<:Array{UInt8}} where N}, crc::UInt32=0x00000000) = Base._crc32c(a, crc) crc32c(s::Union{String, SubString{String}}, crc::UInt32=0x00000000) = Base._crc32c(s, crc) """ crc32c(io::IO, [nb::Integer,] crc::UInt32=0x00000000) Read up to `nb` bytes from `io` and return the CRC-32c checksum, optionally mixed with a starting `crc` integer. If `nb` is not supplied, then `io` will be read until the end of the stream. """ crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) = Base._crc32c(io, nb, crc) crc32c(io::IO, crc::UInt32=0x00000000) = Base._crc32c(io, crc) crc32c(io::IOStream, crc::UInt32=0x00000000) = Base._crc32c(io, crc) end |/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/FileWatching/src/FileWatching.jls# This file is a part of Julia. License is MIT: https://julialang.org/license """ Utilities for monitoring files and file descriptors for events. """ module FileWatching export # one-shot API (returns results): watch_file, # efficient for small numbers of files watch_folder, # efficient for large numbers of files unwatch_folder, poll_file, # very inefficient alternative to above poll_fd, # very efficient, unrelated to above # continuous API (returns objects): FileMonitor, FolderMonitor, PollingFileWatcher, FDWatcher, # pidfile: mkpidlock, trymkpidlock import Base: @handle_as, wait, close, eventloop, notify_error, IOError, _sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close, uv_error, _UVError, iolock_begin, iolock_end, associate_julia_struct, disassociate_julia_struct, preserve_handle, unpreserve_handle, isreadable, iswritable, isopen, |, getproperty, propertynames import Base.Filesystem.StatStruct if Sys.iswindows() import Base.WindowsRawSocket end # libuv file watching event flags const UV_RENAME = Int32(1) const UV_CHANGE = Int32(2) struct FileEvent renamed::Bool changed::Bool timedout::Bool FileEvent(r::Bool, c::Bool, t::Bool) = new(r, c, t) end FileEvent() = FileEvent(false, false, true) FileEvent(flags::Integer) = FileEvent((flags & UV_RENAME) != 0, (flags & UV_CHANGE) != 0, false) |(a::FileEvent, b::FileEvent) = FileEvent(a.renamed | b.renamed, a.changed | b.changed, a.timedout | b.timedout) # libuv file descriptor event flags const UV_READABLE = Int32(1) const UV_WRITABLE = Int32(2) const UV_DISCONNECT = Int32(4) const UV_PRIORITIZED = Int32(8) struct FDEvent events::Int32 FDEvent(flags::Integer=0) = new(flags) end FDEvent(r::Bool, w::Bool, d::Bool, t::Bool) = FDEvent((UV_READABLE * r) | (UV_WRITABLE * w) | (UV_DISCONNECT * d)) # deprecated method function getproperty(f::FDEvent, field::Symbol) events = getfield(f, :events) field === :readable && return (events & UV_READABLE) != 0 field === :writable && return (events & UV_WRITABLE) != 0 field === :disconnect && return (events & UV_DISCONNECT) != 0 field === :prioritized && return (events & UV_PRIORITIZED) != 0 field === :timedout && return events == 0 field === :events && return Int(events) getfield(f, field)::Union{} end propertynames(f::FDEvent) = (:readable, :writable, :disconnect, :prioritized, :timedout, :events) isreadable(f::FDEvent) = f.readable iswritable(f::FDEvent) = f.writable |(a::FDEvent, b::FDEvent) = FDEvent(getfield(a, :events) | getfield(b, :events)) mutable struct FileMonitor @atomic handle::Ptr{Cvoid} file::String notify::Base.ThreadSynchronizer events::Int32 active::Bool FileMonitor(file::AbstractString) = FileMonitor(String(file)) function FileMonitor(file::String) handle = Libc.malloc(_sizeof_uv_fs_event) this = new(handle, file, Base.ThreadSynchronizer(), 0, false) associate_julia_struct(handle, this) iolock_begin() err = ccall(:uv_fs_event_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), handle) if err != 0 Libc.free(handle) throw(_UVError("FileMonitor", err)) end iolock_end() finalizer(uvfinalize, this) return this end end mutable struct FolderMonitor @atomic handle::Ptr{Cvoid} # notify::Channel{Any} # eltype = Union{Pair{String, FileEvent}, IOError} notify::Base.ThreadSynchronizer channel::Vector{Any} # eltype = Pair{String, FileEvent} FolderMonitor(folder::AbstractString) = FolderMonitor(String(folder)) function FolderMonitor(folder::String) handle = Libc.malloc(_sizeof_uv_fs_event) this = new(handle, Base.ThreadSynchronizer(), []) associate_julia_struct(handle, this) iolock_begin() err = ccall(:uv_fs_event_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), handle) if err != 0 Libc.free(handle) throw(_UVError("FolderMonitor", err)) end finalizer(uvfinalize, this) uv_error("FolderMonitor (start)", ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), handle, uv_jl_fseventscb_folder::Ptr{Cvoid}, folder, 0)) iolock_end() return this end end mutable struct PollingFileWatcher @atomic handle::Ptr{Cvoid} file::String interval::UInt32 notify::Base.ThreadSynchronizer active::Bool curr_error::Int32 curr_stat::StatStruct PollingFileWatcher(file::AbstractString, interval::Float64=5.007) = PollingFileWatcher(String(file), interval) function PollingFileWatcher(file::String, interval::Float64=5.007) # same default as nodejs handle = Libc.malloc(_sizeof_uv_fs_poll) this = new(handle, file, round(UInt32, interval * 1000), Base.ThreadSynchronizer(), false, 0, StatStruct()) associate_julia_struct(handle, this) iolock_begin() err = ccall(:uv_fs_poll_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), handle) if err != 0 Libc.free(handle) throw(_UVError("PollingFileWatcher", err)) end finalizer(uvfinalize, this) iolock_end() return this end end mutable struct _FDWatcher @atomic handle::Ptr{Cvoid} fdnum::Int # this is NOT the file descriptor refcount::Tuple{Int, Int} notify::Base.ThreadSynchronizer events::Int32 active::Tuple{Bool, Bool} let FDWatchers = Vector{Any}() # n.b.: this structure and the refcount are protected by the iolock global _FDWatcher, uvfinalize @static if Sys.isunix() _FDWatcher(fd::RawFD, mask::FDEvent) = _FDWatcher(fd, mask.readable, mask.writable) function _FDWatcher(fd::RawFD, readable::Bool, writable::Bool) if !readable && !writable throw(ArgumentError("must specify at least one of readable or writable to create a FDWatcher")) end fdnum = Core.Intrinsics.bitcast(Int32, fd) + 1 iolock_begin() if fdnum > length(FDWatchers) old_len = length(FDWatchers) resize!(FDWatchers, fdnum) FDWatchers[(old_len + 1):fdnum] .= nothing elseif FDWatchers[fdnum] !== nothing this = FDWatchers[fdnum]::_FDWatcher this.refcount = (this.refcount[1] + Int(readable), this.refcount[2] + Int(writable)) iolock_end() return this end if ccall(:jl_uv_unix_fd_is_watched, Int32, (RawFD, Ptr{Cvoid}, Ptr{Cvoid}), fd, C_NULL, eventloop()) == 1 throw(ArgumentError("$(fd) is already being watched by libuv")) end handle = Libc.malloc(_sizeof_uv_poll) this = new( handle, fdnum, (Int(readable), Int(writable)), Base.ThreadSynchronizer(), Int32(0), (false, false)) associate_julia_struct(handle, this) err = ccall(:uv_poll_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, RawFD), eventloop(), handle, fd) if err != 0 Libc.free(handle) throw(_UVError("FDWatcher", err)) end finalizer(uvfinalize, this) FDWatchers[fdnum] = this iolock_end() return this end end function uvfinalize(t::_FDWatcher) iolock_begin() lock(t.notify) try if t.handle != C_NULL disassociate_julia_struct(t) ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) @atomic :monotonic t.handle = C_NULL end t.refcount = (0, 0) t.active = (false, false) @static if Sys.isunix() if FDWatchers[t.fdnum] === t FDWatchers[t.fdnum] = nothing end end notify(t.notify, Int32(0)) finally unlock(t.notify) end iolock_end() nothing end end @static if Sys.iswindows() _FDWatcher(fd::RawFD, mask::FDEvent) = _FDWatcher(fd, mask.readable, mask.writable) function _FDWatcher(fd::RawFD, readable::Bool, writable::Bool) handle = Libc._get_osfhandle(fd) return _FDWatcher(handle, readable, writable) end _FDWatcher(fd::WindowsRawSocket, mask::FDEvent) = _FDWatcher(fd, mask.readable, mask.writable) function _FDWatcher(fd::WindowsRawSocket, readable::Bool, writable::Bool) if !readable && !writable throw(ArgumentError("must specify at least one of readable or writable to create a FDWatcher")) end handle = Libc.malloc(_sizeof_uv_poll) this = new( handle, 0, (Int(readable), Int(writable)), Base.ThreadSynchronizer(), 0, (false, false)) associate_julia_struct(handle, this) iolock_begin() err = ccall(:uv_poll_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, WindowsRawSocket), eventloop(), handle, fd) iolock_end() if err != 0 Libc.free(handle) throw(_UVError("FDWatcher", err)) end finalizer(uvfinalize, this) return this end end end mutable struct FDWatcher # WARNING: make sure `close` has been manually called on this watcher before closing / destroying `fd` watcher::_FDWatcher mask::FDEvent function FDWatcher(fd::RawFD, readable::Bool, writable::Bool) return FDWatcher(fd, FDEvent(readable, writable, false, false)) end function FDWatcher(fd::RawFD, mask::FDEvent) this = new(_FDWatcher(fd, mask), mask) finalizer(close, this) return this end @static if Sys.iswindows() function FDWatcher(fd::WindowsRawSocket, readable::Bool, writable::Bool) return FDWatcher(fd, FDEvent(readable, writable, false, false)) end function FDWatcher(fd::WindowsRawSocket, mask::FDEvent) this = new(_FDWatcher(fd, mask), mask) finalizer(close, this) return this end end end function getproperty(fdw::FDWatcher, s::Symbol) # support deprecated field names s === :readable && return fdw.mask.readable s === :writable && return fdw.mask.writable return getfield(fdw, s) end close(t::_FDWatcher, mask::FDEvent) = close(t, mask.readable, mask.writable) function close(t::_FDWatcher, readable::Bool, writable::Bool) iolock_begin() if t.refcount != (0, 0) t.refcount = (t.refcount[1] - Int(readable), t.refcount[2] - Int(writable)) end if t.refcount == (0, 0) uvfinalize(t) else @lock t.notify notify(t.notify, Int32(0)) end iolock_end() nothing end function close(t::FDWatcher) mask = t.mask t.mask = FDEvent() close(t.watcher, mask) end function uvfinalize(uv::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) iolock_begin() if uv.handle != C_NULL disassociate_julia_struct(uv) # close (and free) without notify ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), uv.handle) end iolock_end() end function close(t::Union{FileMonitor, FolderMonitor, PollingFileWatcher}) iolock_begin() if t.handle != C_NULL ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t.handle) end iolock_end() end function _uv_hook_close(uv::_FDWatcher) # fyi: jl_atexit_hook can cause this to get called too Libc.free(@atomicswap :monotonic uv.handle = C_NULL) uvfinalize(uv) nothing end function _uv_hook_close(uv::PollingFileWatcher) lock(uv.notify) try uv.active = false Libc.free(@atomicswap :monotonic uv.handle = C_NULL) notify(uv.notify, StatStruct()) finally unlock(uv.notify) end nothing end function _uv_hook_close(uv::FileMonitor) lock(uv.notify) try uv.active = false Libc.free(@atomicswap :monotonic uv.handle = C_NULL) notify(uv.notify, FileEvent()) finally unlock(uv.notify) end nothing end function _uv_hook_close(uv::FolderMonitor) lock(uv.notify) try Libc.free(@atomicswap :monotonic uv.handle = C_NULL) notify_error(uv.notify, EOFError()) finally unlock(uv.notify) end nothing end isopen(fm::FileMonitor) = fm.handle != C_NULL isopen(fm::FolderMonitor) = fm.handle != C_NULL isopen(pfw::PollingFileWatcher) = pfw.handle != C_NULL isopen(pfw::_FDWatcher) = pfw.refcount != (0, 0) isopen(pfw::FDWatcher) = !pfw.mask.timedout function uv_fseventscb_file(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) t = @handle_as handle FileMonitor lock(t.notify) try if status != 0 notify_error(t.notify, _UVError("FileMonitor", status)) else t.events |= events notify(t.notify, FileEvent(events)) end finally unlock(t.notify) end nothing end function uv_fseventscb_folder(handle::Ptr{Cvoid}, filename::Ptr, events::Int32, status::Int32) t = @handle_as handle FolderMonitor lock(t.notify) try if status != 0 notify_error(t.notify, _UVError("FolderMonitor", status)) else fname = (filename == C_NULL) ? "" : unsafe_string(convert(Cstring, filename)) push!(t.channel, fname => FileEvent(events)) notify(t.notify) end finally unlock(t.notify) end nothing end function uv_pollcb(handle::Ptr{Cvoid}, status::Int32, events::Int32) t = @handle_as handle _FDWatcher lock(t.notify) try if status != 0 notify_error(t.notify, _UVError("FDWatcher", status)) else t.events |= events if t.active[1] || t.active[2] if isempty(t.notify) # if we keep hearing about events when nobody appears to be listening, # stop the poll to save cycles t.active = (false, false) ccall(:uv_poll_stop, Int32, (Ptr{Cvoid},), t.handle) end end notify(t.notify, events) end finally unlock(t.notify) end nothing end function uv_fspollcb(handle::Ptr{Cvoid}, status::Int32, prev::Ptr, curr::Ptr) t = @handle_as handle PollingFileWatcher old_status = t.curr_error t.curr_error = status if status == 0 t.curr_stat = StatStruct(convert(Ptr{UInt8}, curr)) end if status == 0 || status != old_status prev_stat = StatStruct(convert(Ptr{UInt8}, prev)) lock(t.notify) try notify(t.notify, prev_stat) finally unlock(t.notify) end end nothing end function __init__() global uv_jl_pollcb = @cfunction(uv_pollcb, Cvoid, (Ptr{Cvoid}, Cint, Cint)) global uv_jl_fspollcb = @cfunction(uv_fspollcb, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Cvoid}, Ptr{Cvoid})) global uv_jl_fseventscb_file = @cfunction(uv_fseventscb_file, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) global uv_jl_fseventscb_folder = @cfunction(uv_fseventscb_folder, Cvoid, (Ptr{Cvoid}, Ptr{Int8}, Int32, Int32)) Base.mkpidlock_hook = mkpidlock Base.trymkpidlock_hook = trymkpidlock Base.parse_pidfile_hook = Pidfile.parse_pidfile nothing end function start_watching(t::_FDWatcher) iolock_begin() t.handle == C_NULL && throw(ArgumentError("FDWatcher is closed")) readable = t.refcount[1] > 0 writable = t.refcount[2] > 0 if t.active[1] != readable || t.active[2] != writable # make sure the READABLE / WRITEABLE state is updated uv_error("FDWatcher (start)", ccall(:uv_poll_start, Int32, (Ptr{Cvoid}, Int32, Ptr{Cvoid}), t.handle, (readable ? UV_READABLE : 0) | (writable ? UV_WRITABLE : 0), uv_jl_pollcb::Ptr{Cvoid})) t.active = (readable, writable) end iolock_end() nothing end function start_watching(t::PollingFileWatcher) iolock_begin() t.handle == C_NULL && throw(ArgumentError("PollingFileWatcher is closed")) if !t.active uv_error("PollingFileWatcher (start)", ccall(:uv_fs_poll_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, UInt32), t.handle, uv_jl_fspollcb::Ptr{Cvoid}, t.file, t.interval)) t.active = true end iolock_end() nothing end function stop_watching(t::PollingFileWatcher) iolock_begin() lock(t.notify) try if t.active && isempty(t.notify) t.active = false uv_error("PollingFileWatcher (stop)", ccall(:uv_fs_poll_stop, Int32, (Ptr{Cvoid},), t.handle)) end finally unlock(t.notify) end iolock_end() nothing end function start_watching(t::FileMonitor) iolock_begin() t.handle == C_NULL && throw(ArgumentError("FileMonitor is closed")) if !t.active uv_error("FileMonitor (start)", ccall(:uv_fs_event_start, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32), t.handle, uv_jl_fseventscb_file::Ptr{Cvoid}, t.file, 0)) t.active = true end iolock_end() nothing end function stop_watching(t::FileMonitor) iolock_begin() lock(t.notify) try if t.active && isempty(t.notify) t.active = false uv_error("FileMonitor (stop)", ccall(:uv_fs_event_stop, Int32, (Ptr{Cvoid},), t.handle)) end finally unlock(t.notify) end iolock_end() nothing end # n.b. this _wait may return spuriously early with a timedout event function _wait(fdw::_FDWatcher, mask::FDEvent) iolock_begin() preserve_handle(fdw) lock(fdw.notify) try events = FDEvent(fdw.events & mask.events) if !isopen(fdw) # !open throw(EOFError()) elseif events.timedout start_watching(fdw) # make sure the poll is active iolock_end() return FDEvent(wait(fdw.notify)::Int32) else iolock_end() return events end finally unlock(fdw.notify) unpreserve_handle(fdw) end end function wait(fdw::_FDWatcher; readable=true, writable=true) return wait(fdw, FDEvent(readable, writable, false, false)) end function wait(fdw::_FDWatcher, mask::FDEvent) while true mask.timedout && return mask events = _wait(fdw, mask) if !events.timedout @lock fdw.notify fdw.events &= ~events.events return events end end end function wait(fdw::FDWatcher) isopen(fdw) || throw(EOFError()) while true events = GC.@preserve fdw _wait(fdw.watcher, fdw.mask) isopen(fdw) || throw(EOFError()) if !events.timedout @lock fdw.watcher.notify fdw.watcher.events &= ~events.events return events end end end function wait(socket::RawFD; readable=false, writable=false) return wait(socket, FDEvent(readable, writable, false, false)) end function wait(fd::RawFD, mask::FDEvent) fdw = _FDWatcher(fd, mask) try return wait(fdw, mask) finally close(fdw, mask) end end if Sys.iswindows() function wait(socket::WindowsRawSocket; readable=false, writable=false) return wait(socket, FDEvent(readable, writable, false, false)) end function wait(socket::WindowsRawSocket, mask::FDEvent) fdw = _FDWatcher(socket, mask) try return wait(fdw, mask) finally close(fdw, mask) end end end function wait(pfw::PollingFileWatcher) iolock_begin() preserve_handle(pfw) lock(pfw.notify) local prevstat try start_watching(pfw) iolock_end() prevstat = wait(pfw.notify)::StatStruct unlock(pfw.notify) iolock_begin() lock(pfw.notify) finally unlock(pfw.notify) unpreserve_handle(pfw) end stop_watching(pfw) iolock_end() if pfw.handle == C_NULL return prevstat, EOFError() elseif pfw.curr_error != 0 return prevstat, _UVError("PollingFileWatcher", pfw.curr_error) else return prevstat, pfw.curr_stat end end function wait(m::FileMonitor) iolock_begin() preserve_handle(m) lock(m.notify) local events try start_watching(m) iolock_end() events = wait(m.notify)::FileEvent events |= FileEvent(m.events) m.events = 0 unlock(m.notify) iolock_begin() lock(m.notify) finally unlock(m.notify) unpreserve_handle(m) end stop_watching(m) iolock_end() return events end function wait(m::FolderMonitor) m.handle == C_NULL && throw(EOFError()) preserve_handle(m) lock(m.notify) evt = try m.handle == C_NULL && throw(EOFError()) while isempty(m.channel) wait(m.notify) end popfirst!(m.channel) finally unlock(m.notify) unpreserve_handle(m) end return evt::Pair{String, FileEvent} end """ poll_fd(fd, timeout_s::Real=-1; readable=false, writable=false) Monitor a file descriptor `fd` for changes in the read or write availability, and with a timeout given by `timeout_s` seconds. The keyword arguments determine which of read and/or write status should be monitored; at least one of them must be set to `true`. The returned value is an object with boolean fields `readable`, `writable`, and `timedout`, giving the result of the polling. """ function poll_fd(s::Union{RawFD, Sys.iswindows() ? WindowsRawSocket : Union{}}, timeout_s::Real=-1; readable=false, writable=false) mask = FDEvent(readable, writable, false, false) mask.timedout && return mask fdw = _FDWatcher(s, mask) local timer # we need this flag to explicitly track whether we call `close` already, to update the internal refcount correctly timedout = false # TODO: make this atomic try if timeout_s >= 0 # delay creating the timer until shortly before we start the poll wait timer = Timer(timeout_s) do t timedout && return timedout = true close(fdw, mask) end try while true events = _wait(fdw, mask) if timedout || !events.timedout @lock fdw.notify fdw.events &= ~events.events return events end end catch ex ex isa EOFError() || rethrow() return FDEvent() end else return wait(fdw, mask) end finally if @isdefined(timer) if !timedout timedout = true close(timer) close(fdw, mask) end else close(fdw, mask) end end end """ watch_file(path::AbstractString, timeout_s::Real=-1) Watch file or directory `path` for changes until a change occurs or `timeout_s` seconds have elapsed. This function does not poll the file system and instead uses platform-specific functionality to receive notifications from the operating system (e.g. via inotify on Linux). See the NodeJS documentation linked below for details. The returned value is an object with boolean fields `renamed`, `changed`, and `timedout`, giving the result of watching the file. This behavior of this function varies slightly across platforms. See for more detailed information. """ function watch_file(s::String, timeout_s::Float64=-1.0) fm = FileMonitor(s) local timer try if timeout_s >= 0 timer = Timer(timeout_s) do t close(fm) end end return wait(fm) finally close(fm) @isdefined(timer) && close(timer) end end watch_file(s::AbstractString, timeout_s::Real=-1) = watch_file(String(s), Float64(timeout_s)) """ watch_folder(path::AbstractString, timeout_s::Real=-1) Watches a file or directory `path` for changes until a change has occurred or `timeout_s` seconds have elapsed. This function does not poll the file system and instead uses platform-specific functionality to receive notifications from the operating system (e.g. via inotify on Linux). See the NodeJS documentation linked below for details. This will continuing tracking changes for `path` in the background until `unwatch_folder` is called on the same `path`. The returned value is an pair where the first field is the name of the changed file (if available) and the second field is an object with boolean fields `renamed`, `changed`, and `timedout`, giving the event. This behavior of this function varies slightly across platforms. See for more detailed information. """ watch_folder(s::AbstractString, timeout_s::Real=-1) = watch_folder(String(s), timeout_s) function watch_folder(s::String, timeout_s::Real=-1) fm = get!(watched_folders, s) do return FolderMonitor(s) end local timer if timeout_s >= 0 @lock fm.notify isempty(fm.channel) || return popfirst!(fm.channel) if timeout_s <= 0.010 # for very small timeouts, we can just sleep for the whole timeout-interval (timeout_s == 0) ? yield() : sleep(timeout_s) @lock fm.notify isempty(fm.channel) || return popfirst!(fm.channel) return "" => FileEvent() # timeout else timer = Timer(timeout_s) do t @lock fm.notify notify(fm.notify) end end end # inline a copy of `wait` with added support for checking timer fm.handle == C_NULL && throw(EOFError()) preserve_handle(fm) lock(fm.notify) evt = try fm.handle == C_NULL && throw(EOFError()) while isempty(fm.channel) if @isdefined(timer) isopen(timer) || return "" => FileEvent() # timeout end wait(fm.notify) end popfirst!(fm.channel) finally unlock(fm.notify) unpreserve_handle(fm) @isdefined(timer) && close(timer) end return evt::Pair{String, FileEvent} end """ unwatch_folder(path::AbstractString) Stop background tracking of changes for `path`. It is not recommended to do this while another task is waiting for `watch_folder` to return on the same path, as the result may be unpredictable. """ unwatch_folder(s::AbstractString) = unwatch_folder(String(s)) function unwatch_folder(s::String) fm = pop!(watched_folders, s, nothing) fm === nothing || close(fm) nothing end const watched_folders = Dict{String, FolderMonitor}() """ poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current) Monitor a file for changes by polling every `interval_s` seconds until a change occurs or `timeout_s` seconds have elapsed. The `interval_s` should be a long period; the default is 5.007 seconds. Returns a pair of status objects `(previous, current)` when a change is detected. The `previous` status is always a `StatStruct`, but it may have all of the fields zeroed (indicating the file didn't previously exist, or wasn't previously accessible). The `current` status object may be a `StatStruct`, an `EOFError` (indicating the timeout elapsed), or some other `Exception` subtype (if the `stat` operation failed - for example, if the path does not exist). To determine when a file was modified, compare `current isa StatStruct && mtime(prev) != mtime(current)` to detect notification of changes. However, using [`watch_file`](@ref) for this operation is preferred, since it is more reliable and efficient, although in some situations it may not be available. """ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::Real=-1) pfw = PollingFileWatcher(s, Float64(interval_seconds)) local timer try if timeout_s >= 0 timer = Timer(timeout_s) do t close(pfw) end end statdiff = wait(pfw) if isa(statdiff[2], IOError) # file didn't initially exist, continue watching for it to be created (or the error to change) statdiff = wait(pfw) end return statdiff finally close(pfw) @isdefined(timer) && close(timer) end end include("pidfile.jl") import .Pidfile: mkpidlock, trymkpidlock end w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jlค+module Pidfile export mkpidlock, trymkpidlock using Base: IOError, UV_EEXIST, UV_ESRCH, Process using Base.Libc: rand using Base.Filesystem: File, open, JL_O_CREAT, JL_O_RDWR, JL_O_RDONLY, JL_O_EXCL, rename, samefile, path_separator using ..FileWatching: watch_file using Base.Sys: iswindows """ mkpidlock([f::Function], at::String, [pid::Cint, proc::Process]; kwopts...) Create a pidfile lock for the path "at" for the current process or the process identified by pid or proc. Can take a function to execute once locked, for usage in `do` blocks, after which the lock will be automatically closed. If the lock fails and `wait` is false, then an error is thrown. The lock will be released by either `close`, a `finalizer`, or shortly after `proc` exits. Make sure the return value is live through the end of the critical section of your program, so the `finalizer` does not reclaim it early. Optional keyword arguments: - `mode`: file access mode (modified by the process umask). Defaults to world-readable. - `poll_interval`: Specify the maximum time to between attempts (if `watch_file` doesn't work) - `stale_age`: Delete an existing pidfile (ignoring the lock) if it is older than this many seconds, based on its mtime. The file won't be deleted until 5x longer than this if the pid in the file appears that it may be valid. Or 25x longer if `refresh` is overridden to 0 to disable lock refreshing. By default this is disabled (`stale_age` = 0), but a typical recommended value would be about 3-5x an estimated normal completion time. - `refresh`: Keeps a lock from becoming stale by updating the mtime every interval of time that passes. By default, this is set to `stale_age/2`, which is the recommended value. - `wait`: If true, block until we get the lock, if false, raise error if lock fails. """ function mkpidlock end """ trymkpidlock([f::Function], at::String, [pid::Cint, proc::Process]; kwopts...) Like `mkpidlock` except returns `false` instead of waiting if the file is already locked. !!! compat "Julia 1.10" This function requires at least Julia 1.10. """ function trymkpidlock end # mutable only because we want to add a finalizer mutable struct LockMonitor const path::String const fd::File const update::Union{Nothing,Timer} global function mkpidlock(at::String, pid::Cint; stale_age::Real=0, refresh::Real=stale_age/2, kwopts...) local lock atdir, atname = splitdir(at) isempty(atdir) && (atdir = pwd()) at = realpath(atdir) * path_separator * atname fd = open_exclusive(at; stale_age, refresh, kwopts...) update = nothing try write_pidfile(fd, pid) if refresh > 0 # N.b.: to ensure our finalizer works we are careful to capture # `fd` here instead of `lock`. update = Timer(t -> isopen(t) && touch(fd), refresh; interval=refresh) end lock = new(at, fd, update) finalizer(close, lock) catch ex tryrmopenfile(at) close(fd) rethrow(ex) end return lock end end mkpidlock(at::String; kwopts...) = mkpidlock(at, getpid(); kwopts...) mkpidlock(f::Function, at::String; kwopts...) = mkpidlock(f, at, getpid(); kwopts...) function mkpidlock(f::Function, at::String, pid::Cint; kwopts...) lock = mkpidlock(at, pid; kwopts...) try return f() finally close(lock) end end function mkpidlock(at::String, proc::Process; kwopts...) lock = mkpidlock(at, getpid(proc); kwopts...) closer = @async begin wait(proc) close(lock) end isdefined(Base, :errormonitor) && Base.errormonitor(closer) return lock end function trymkpidlock(args...; kwargs...) try mkpidlock(args...; kwargs..., wait=false) catch ex if ex isa PidlockedError return false else rethrow() end end end """ Base.touch(::Pidfile.LockMonitor) Update the `mtime` on the lock, to indicate it is still fresh. See also the `refresh` keyword in the [`mkpidlock`](@ref) constructor. """ Base.touch(lock::LockMonitor) = (touch(lock.fd); lock) """ write_pidfile(io, pid) Write our pidfile format to an open IO descriptor. """ function write_pidfile(io::IO, pid::Cint) print(io, "$pid $(gethostname())") end """ parse_pidfile(file::Union{IO, String}) => (pid, hostname, age) Attempt to parse our pidfile format, replaced an element with (0, "", 0.0), respectively, for any read that failed. """ function parse_pidfile(io::IO) fields = split(read(io, String), ' ', limit = 2) pid = tryparse(Cuint, fields[1]) pid === nothing && (pid = Cuint(0)) hostname = (length(fields) == 2) ? fields[2] : "" when = mtime(io) age = time() - when return (pid, hostname, age) end function parse_pidfile(path::String) try existing = open(path, JL_O_RDONLY) try return parse_pidfile(existing) finally close(existing) end catch ex isa(ex, EOFError) || isa(ex, IOError) || rethrow(ex) return (Cuint(0), "", 0.0) end end """ isvalidpid(hostname::String, pid::Cuint) :: Bool Attempt to conservatively estimate whether pid is a valid process id. """ function isvalidpid(hostname::AbstractString, pid::Cuint) # can't inspect remote hosts (hostname == "" || hostname == gethostname()) || return true # pid < 0 is never valid (must be a parser error or different OS), # and would have a completely different meaning when passed to kill !iswindows() && pid > typemax(Cint) && return false # (similarly for pid 0) pid == 0 && return false # see if the process id exists by querying kill without sending a signal # and checking if it returned ESRCH (no such process) return ccall(:uv_kill, Cint, (Cuint, Cint), pid, 0) != UV_ESRCH end """ stale_pidfile(path::String, stale_age::Real, refresh::Real) :: Bool Helper function for `open_exclusive` for deciding if a pidfile is stale. """ function stale_pidfile(path::String, stale_age::Real, refresh::Real) pid, hostname, age = parse_pidfile(path) age < -stale_age && @warn "filesystem time skew detected" path=path longer_factor = refresh == 0 ? 25 : 5 if age > stale_age if (age > stale_age * longer_factor) || !isvalidpid(hostname, pid) return true end end return false end """ tryopen_exclusive(path::String, mode::Integer = 0o444) :: Union{Void, File} Try to create a new file for read-write advisory-exclusive access, return nothing if it already exists. """ function tryopen_exclusive(path::String, mode::Integer = 0o444) try return open(path, JL_O_RDWR | JL_O_CREAT | JL_O_EXCL, mode) catch ex (isa(ex, IOError) && ex.code == UV_EEXIST) || rethrow(ex) end return nothing end struct PidlockedError <: Exception msg::AbstractString end """ open_exclusive(path::String; mode, poll_interval, wait, stale_age, refresh) :: File Create a new a file for read-write advisory-exclusive access. If `wait` is `false` then error out if the lock files exist otherwise block until we get the lock. For a description of the keyword arguments, see [`mkpidlock`](@ref). """ function open_exclusive(path::String; mode::Integer = 0o444 #= read-only =#, poll_interval::Real = 10 #= seconds =#, wait::Bool = true #= return on failure if false =#, stale_age::Real = 0 #= disabled =#, refresh::Real = stale_age/2) # fast-path: just try to open it file = tryopen_exclusive(path, mode) file === nothing || return file if !wait if file === nothing && stale_age > 0 if stale_age > 0 && stale_pidfile(path, stale_age, refresh) @warn "attempting to remove probably stale pidfile" path=path tryrmopenfile(path) end file = tryopen_exclusive(path, mode) end if file === nothing throw(PidlockedError("Failed to get pidfile lock for $(repr(path)).")) else return file end end # fall-back: wait for the lock while true # start the file-watcher prior to checking for the pidfile existence t = @async try watch_file(path, poll_interval) catch ex isa(ex, IOError) || rethrow(ex) sleep(poll_interval) # if the watch failed, convert to just doing a sleep end # now try again to create it file = tryopen_exclusive(path, mode) file === nothing || return file Base.wait(t) # sleep for a bit before trying again if stale_age > 0 && stale_pidfile(path, stale_age, refresh) # if the file seems stale, try to remove it before attempting again # set stale_age to zero so we won't attempt again, even if the attempt fails stale_age -= stale_age @warn "attempting to remove probably stale pidfile" path=path tryrmopenfile(path) end end end function _rand_filename(len::Int=4) # modified from Base.Libc slug = Base.StringVector(len) chars = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" for i = 1:len slug[i] = chars[(Libc.rand() % length(chars)) + 1] end return String(slug) end function tryrmopenfile(path::String) # Deleting open file on Windows is a bit hard # if we want to reuse the name immediately after: # we need to first rename it, then delete it. if Sys.iswindows() try local rmpath rmdir, rmname = splitdir(path) while true rmpath = string(rmdir, isempty(rmdir) ? "" : path_separator, "\$", _rand_filename(), rmname, ".deleted") ispath(rmpath) || break end rename(path, rmpath) path = rmpath catch ex isa(ex, IOError) || rethrow(ex) end end return try rm(path) true catch ex isa(ex, IOError) || rethrow(ex) ex end end """ close(lock::LockMonitor) Release a pidfile lock. """ function Base.close(lock::LockMonitor) update = lock.update update === nothing || close(update) isopen(lock.fd) || return false removed = false path = lock.path pathstat = try # Windows sometimes likes to return EACCES here, # if the path is in the process of being deleted stat(path) catch ex ex isa IOError || rethrow() removed = ex nothing end if pathstat !== nothing && samefile(stat(lock.fd), pathstat) # try not to delete someone else's lock removed = tryrmopenfile(path) end close(lock.fd) havelock = removed === true havelock || @warn "failed to remove pidfile on close" path=path removed=removed return havelock end end # module n/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Libdl/src/Libdl.jlh# This file is a part of Julia. License is MIT: https://julialang.org/license module Libdl # Just re-export Base.Libc.Libdl: export DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, dlpath, find_library, dlext, dllist import Base.Libc.Libdl: DL_LOAD_PATH, RTLD_DEEPBIND, RTLD_FIRST, RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_NOW, dlclose, dlopen, dlopen_e, dlsym, dlsym_e, dlpath, find_library, dlext, dllist end # module r/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Logging/src/Logging.jlM# This file is a part of Julia. License is MIT: https://julialang.org/license """ Utilities for capturing, filtering and presenting streams of log events. Normally you don't need to import `Logging` to create log events; for this the standard logging macros such as `@info` are already exported by `Base` and available by default. """ module Logging # Import the CoreLogging implementation into Logging as new const bindings. # Doing it this way (rather than with import) makes these symbols accessible to # tab completion. for sym in [ :LogLevel, :BelowMinLevel, :AboveMaxLevel, :AbstractLogger, :NullLogger, :handle_message, :shouldlog, :min_enabled_level, :catch_exceptions, Symbol("@debug"), Symbol("@info"), Symbol("@warn"), Symbol("@error"), Symbol("@logmsg"), :with_logger, :current_logger, :global_logger, :disable_logging, :SimpleLogger] @eval const $sym = Base.CoreLogging.$sym end # LogLevel aliases (re-)documented here (JuliaLang/julia#40978) """ Debug Alias for [`LogLevel(-1000)`](@ref LogLevel). """ const Debug = Base.CoreLogging.Debug """ Info Alias for [`LogLevel(0)`](@ref LogLevel). """ const Info = Base.CoreLogging.Info """ Warn Alias for [`LogLevel(1000)`](@ref LogLevel). """ const Warn = Base.CoreLogging.Warn """ Error Alias for [`LogLevel(2000)`](@ref LogLevel). """ const Error = Base.CoreLogging.Error using Base.CoreLogging: closed_stream export AbstractLogger, LogLevel, NullLogger, @debug, @info, @warn, @error, @logmsg, with_logger, current_logger, global_logger, disable_logging, SimpleLogger, ConsoleLogger, BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel include("ConsoleLogger.jl") # The following are also part of the public API, but not exported: # # 1. Log levels: # BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel, # # 2. AbstractLogger message related functions: # handle_message, shouldlog, min_enabled_level, catch_exceptions, function __init__() global_logger(ConsoleLogger()) end end x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Logging/src/ConsoleLogger.jlF# This file is a part of Julia. License is MIT: https://julialang.org/license """ ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) Logger with formatting optimized for readability in a text console, for example interactive work with the Julia REPL. Log levels less than `min_level` are filtered out. Message formatting can be controlled by setting keyword arguments: * `meta_formatter` is a function which takes the log event metadata `(level, _module, group, id, file, line)` and returns a color (as would be passed to printstyled), prefix and suffix for the log message. The default is to prefix with the log level and a suffix containing the module, file and line location. * `show_limited` limits the printing of large data structures to something which can fit on the screen by setting the `:limit` `IOContext` key during formatting. * `right_justify` is the integer column which log metadata is right justified at. The default is zero (metadata goes on its own line). """ struct ConsoleLogger <: AbstractLogger stream::IO min_level::LogLevel meta_formatter show_limited::Bool right_justify::Int message_limits::Dict{Any,Int} end function ConsoleLogger(stream::IO, min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) ConsoleLogger(stream, min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end function ConsoleLogger(min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) ConsoleLogger(closed_stream, min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end shouldlog(logger::ConsoleLogger, level, _module, group, id) = get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::ConsoleLogger) = logger.min_level # Formatting of values in key value pairs showvalue(io, msg) = show(io, "text/plain", msg) function showvalue(io, e::Tuple{Exception,Any}) ex,bt = e showerror(io, ex, bt; backtrace = bt!==nothing) end showvalue(io, ex::Exception) = showerror(io, ex) function default_logcolor(level::LogLevel) level < Info ? Base.debug_color() : level < Warn ? Base.info_color() : level < Error ? Base.warn_color() : Base.error_color() end function default_metafmt(level::LogLevel, _module, group, id, file, line) @nospecialize color = default_logcolor(level) prefix = string(level == Warn ? "Warning" : string(level), ':') suffix::String = "" Info <= level < Warn && return color, prefix, suffix _module !== nothing && (suffix *= string(_module)::String) if file !== nothing _module !== nothing && (suffix *= " ") suffix *= contractuser(file)::String if line !== nothing suffix *= ":$(isa(line, UnitRange) ? "$(first(line))-$(last(line))" : line)" end end !isempty(suffix) && (suffix = "@ " * suffix) return color, prefix, suffix end # Length of a string as it will appear in the terminal (after ANSI color codes # are removed) function termlength(str) N = 0 in_esc = false for c in str if in_esc if c == 'm' in_esc = false end else if c == '\e' in_esc = true else N += 1 end end end return N end function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module, group, id, filepath, line; kwargs...) @nospecialize hasmaxlog = haskey(kwargs, :maxlog) ? 1 : 0 maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts remaining = get!(logger.message_limits, id, Int(maxlog)::Int) logger.message_limits[id] = remaining - 1 remaining > 0 || return end # Generate a text representation of the message and all key value pairs, # split into lines. msglines = [(indent=0, msg=l) for l in split(chomp(convert(String, string(message))::String), '\n')] stream::IO = logger.stream if !(isopen(stream)::Bool) stream = stderr end dsize = displaysize(stream)::Tuple{Int,Int} nkwargs = length(kwargs)::Int if nkwargs > hasmaxlog valbuf = IOBuffer() rows_per_value = max(1, dsize[1] รท (nkwargs + 1 - hasmaxlog)) valio = IOContext(IOContext(valbuf, stream), :displaysize => (rows_per_value, dsize[2] - 5), :limit => logger.show_limited) for (key, val) in kwargs key === :maxlog && continue showvalue(valio, val) vallines = split(String(take!(valbuf)), '\n') if length(vallines) == 1 push!(msglines, (indent=2, msg=SubString("$key = $(vallines[1])"))) else push!(msglines, (indent=2, msg=SubString("$key ="))) append!(msglines, ((indent=3, msg=line) for line in vallines)) end end end # Format lines as text with appropriate indentation and with a box # decoration on the left. color, prefix, suffix = logger.meta_formatter(level, _module, group, id, filepath, line)::Tuple{Union{Symbol,Int},String,String} minsuffixpad = 2 buf = IOBuffer() iob = IOContext(buf, stream) nonpadwidth = 2 + (isempty(prefix) || length(msglines) > 1 ? 0 : length(prefix)+1) + msglines[end].indent + termlength(msglines[end].msg) + (isempty(suffix) ? 0 : length(suffix)+minsuffixpad) justify_width = min(logger.right_justify, dsize[2]) if nonpadwidth > justify_width && !isempty(suffix) push!(msglines, (indent=0, msg=SubString(""))) minsuffixpad = 0 nonpadwidth = 2 + length(suffix) end for (i, (indent, msg)) in enumerate(msglines) boxstr = length(msglines) == 1 ? "[ " : i == 1 ? "โ”Œ " : i < length(msglines) ? "โ”‚ " : "โ”” " printstyled(iob, boxstr, bold=true, color=color) if i == 1 && !isempty(prefix) printstyled(iob, prefix, " ", bold=true, color=color) end print(iob, " "^indent, msg) if i == length(msglines) && !isempty(suffix) npad = max(0, justify_width - nonpadwidth) + minsuffixpad print(iob, " "^npad) printstyled(iob, suffix, color=:light_black) end println(iob) end write(stream, take!(buf)) nothing end l/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Mmap/src/Mmap.jlG@# This file is a part of Julia. License is MIT: https://julialang.org/license """ Low level module for mmap (memory mapping of files). """ module Mmap import Base: OS_HANDLE, INVALID_OS_HANDLE export mmap const PAGESIZE = Int(Sys.isunix() ? ccall(:jl_getpagesize, Clong, ()) : ccall(:jl_getallocationgranularity, Clong, ())) # for mmaps not backed by files mutable struct Anonymous <: IO name::String readonly::Bool create::Bool end """ Mmap.Anonymous(name::AbstractString="", readonly::Bool=false, create::Bool=true) Create an `IO`-like object for creating zeroed-out mmapped-memory that is not tied to a file for use in [`mmap`](@ref mmap). Used by `SharedArray` for creating shared memory arrays. # Examples ```jldoctest julia> using Mmap julia> anon = Mmap.Anonymous(); julia> isreadable(anon) true julia> iswritable(anon) true julia> isopen(anon) true ``` """ Anonymous() = Anonymous("",false,true) Base.isopen(::Anonymous) = true Base.isreadable(::Anonymous) = true Base.iswritable(a::Anonymous) = !a.readonly # const used for zeroed, anonymous memory gethandle(io::Anonymous) = INVALID_OS_HANDLE # platform-specific mmap utilities if Sys.isunix() const PROT_READ = Cint(1) const PROT_WRITE = Cint(2) const MAP_SHARED = Cint(1) const MAP_PRIVATE = Cint(2) const MAP_ANONYMOUS = Cint(Sys.isbsd() ? 0x1000 : 0x20) const F_GETFL = Cint(3) gethandle(io::IO) = RawFD(fd(io)) # Determine a stream's read/write mode, and return prot & flags appropriate for mmap function settings(s::RawFD, shared::Bool) flags = shared ? MAP_SHARED : MAP_PRIVATE if s == INVALID_OS_HANDLE flags |= MAP_ANONYMOUS prot = PROT_READ | PROT_WRITE else mode = ccall(:fcntl, Cint, (RawFD, Cint, Cint...), s, F_GETFL) systemerror("fcntl F_GETFL", mode == -1) mode = mode & 3 prot = (mode == 0) ? PROT_READ : ((mode == 1) ? PROT_WRITE : (PROT_READ | PROT_WRITE)) if prot & PROT_READ == 0 throw(ArgumentError("mmap requires read permissions on the file (open with \"r+\" mode to override)")) end end return prot, flags, (prot & PROT_WRITE) > 0 end # Before mapping, grow the file to sufficient size # Note: a few mappable streams do not support lseek. When Julia # supports structures in ccall, switch to fstat. grow!(::Anonymous,o::Integer,l::Integer) = return function grow!(io::IO, offset::Integer, len::Integer) pos = position(io) filelen = filesize(io) if filelen < offset + len failure = ccall(:jl_ftruncate, Cint, (Cint, Int64), fd(io), offset+len) Base.systemerror(:ftruncate, failure != 0) end seek(io, pos) return end elseif Sys.iswindows() const DWORD = Culong const PAGE_READONLY = DWORD(0x02) const PAGE_READWRITE = DWORD(0x04) const PAGE_WRITECOPY = DWORD(0x08) const PAGE_EXECUTE_READ = DWORD(0x20) const PAGE_EXECUTE_READWRITE = DWORD(0x40) const PAGE_EXECUTE_WRITECOPY = DWORD(0x80) const FILE_MAP_COPY = DWORD(0x01) const FILE_MAP_WRITE = DWORD(0x02) const FILE_MAP_READ = DWORD(0x04) const FILE_MAP_EXECUTE = DWORD(0x20) function gethandle(io::IO) handle = Libc._get_osfhandle(RawFD(fd(io))) Base.windowserror(:mmap, handle == INVALID_OS_HANDLE) return handle end settings(sh::Anonymous) = sh.name, sh.readonly, sh.create settings(io::IO) = Ptr{Cwchar_t}(0), isreadonly(io), true else error("mmap not defined for this OS") end # os-test # core implementation of mmap """ mmap(io::Union{IOStream,AbstractString,Mmap.AnonymousMmap}[, type::Type{Array{T,N}}, dims, offset]; grow::Bool=true, shared::Bool=true) mmap(type::Type{Array{T,N}}, dims) Create an `Array` whose values are linked to a file, using memory-mapping. This provides a convenient way of working with data too large to fit in the computer's memory. The type is an `Array{T,N}` with a bits-type element of `T` and dimension `N` that determines how the bytes of the array are interpreted. Note that the file must be stored in binary format, and no format conversions are possible (this is a limitation of operating systems, not Julia). `dims` is a tuple or single [`Integer`](@ref) specifying the size or length of the array. The file is passed via the stream argument, either as an open [`IOStream`](@ref) or filename string. When you initialize the stream, use `"r"` for a "read-only" array, and `"w+"` to create a new array used to write values to disk. If no `type` argument is specified, the default is `Vector{UInt8}`. Optionally, you can specify an offset (in bytes) if, for example, you want to skip over a header in the file. The default value for the offset is the current stream position for an `IOStream`. The `grow` keyword argument specifies whether the disk file should be grown to accommodate the requested size of array (if the total file size is < requested array size). Write privileges are required to grow the file. The `shared` keyword argument specifies whether the resulting `Array` and changes made to it will be visible to other processes mapping the same file. For example, the following code ```julia # Create a file for mmapping # (you could alternatively use mmap to do this step, too) using Mmap A = rand(1:20, 5, 30) s = open("/tmp/mmap.bin", "w+") # We'll write the dimensions of the array as the first two Ints in the file write(s, size(A,1)) write(s, size(A,2)) # Now write the data write(s, A) close(s) # Test by reading it back in s = open("/tmp/mmap.bin") # default is read-only m = read(s, Int) n = read(s, Int) A2 = mmap(s, Matrix{Int}, (m,n)) ``` creates a `m`-by-`n` `Matrix{Int}`, linked to the file associated with stream `s`. A more portable file would need to encode the word size -- 32 bit or 64 bit -- and endianness information in the header. In practice, consider encoding binary data using standard formats like HDF5 (which can be used with memory-mapping). """ function mmap(io::IO, ::Type{Array{T,N}}=Vector{UInt8}, dims::NTuple{N,Integer}=(div(filesize(io)-position(io),sizeof(T)),), offset::Integer=position(io); grow::Bool=true, shared::Bool=true) where {T,N} # check inputs isopen(io) || throw(ArgumentError("$io must be open to mmap")) isbitstype(T) || throw(ArgumentError("unable to mmap $T; must satisfy isbitstype(T) == true")) len = sizeof(T) for l in dims len, overflow = Base.Checked.mul_with_overflow(promote(len, l)...) overflow && throw(ArgumentError("requested size prod($((sizeof(T), dims...))) too large, would overflow typeof(size(T)) == $(typeof(len))")) end len >= 0 || throw(ArgumentError("requested size must be โ‰ฅ 0, got $len")) len == 0 && return Array{T}(undef, ntuple(x->0,Val(N))) len < typemax(Int) - PAGESIZE || throw(ArgumentError("requested size must be < $(typemax(Int)-PAGESIZE), got $len")) offset >= 0 || throw(ArgumentError("requested offset must be โ‰ฅ 0, got $offset")) # shift `offset` to start of page boundary offset_page::Int64 = div(offset, PAGESIZE) * PAGESIZE # add (offset - offset_page) to `len` to get total length of memory-mapped region mmaplen = (offset - offset_page) + len file_desc = gethandle(io) szfile = convert(Csize_t, len + offset) requestedSizeLarger = false if !(io isa Mmap.Anonymous) @static if !Sys.isapple() requestedSizeLarger = szfile > filesize(io) end end # platform-specific mmapping @static if Sys.isunix() prot, flags, iswrite = settings(file_desc, shared) if requestedSizeLarger if iswrite if grow grow!(io, offset, len) else throw(ArgumentError("requested size $szfile larger than file size $(filesize(io)), but requested not to grow")) end else throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) end end @static if Sys.isapple() iswrite && grow && grow!(io, offset, len) end # mmap the file ptr = ccall(:jl_mmap, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t, Cint, Cint, RawFD, Int64), C_NULL, mmaplen, prot, flags, file_desc, offset_page) systemerror("memory mapping failed", reinterpret(Int, ptr) == -1) else name, readonly, create = settings(io) if requestedSizeLarger if readonly throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) elseif !grow throw(ArgumentError("requested size $szfile larger than file size $(filesize(io)), but requested not to grow")) end end handle = create ? ccall(:CreateFileMappingW, stdcall, Ptr{Cvoid}, (OS_HANDLE, Ptr{Cvoid}, DWORD, DWORD, DWORD, Cwstring), file_desc, C_NULL, readonly ? PAGE_READONLY : PAGE_READWRITE, szfile >> 32, szfile & typemax(UInt32), name) : ccall(:OpenFileMappingW, stdcall, Ptr{Cvoid}, (DWORD, Cint, Cwstring), readonly ? FILE_MAP_READ : FILE_MAP_WRITE, true, name) Base.windowserror(:mmap, handle == C_NULL) ptr = ccall(:MapViewOfFile, stdcall, Ptr{Cvoid}, (Ptr{Cvoid}, DWORD, DWORD, DWORD, Csize_t), handle, readonly ? FILE_MAP_READ : FILE_MAP_WRITE, offset_page >> 32, offset_page & typemax(UInt32), mmaplen) Base.windowserror(:mmap, ptr == C_NULL) end # os-test # convert mmapped region to Julia Array at `ptr + (offset - offset_page)` since file was mapped at offset_page A = unsafe_wrap(Array, convert(Ptr{T}, UInt(ptr) + UInt(offset - offset_page)), dims) finalizer(A) do x @static if Sys.isunix() systemerror("munmap", ccall(:munmap, Cint, (Ptr{Cvoid}, Int), ptr, mmaplen) != 0) else status = ccall(:UnmapViewOfFile, stdcall, Cint, (Ptr{Cvoid},), ptr)!=0 status |= ccall(:CloseHandle, stdcall, Cint, (Ptr{Cvoid},), handle)!=0 Base.windowserror(:UnmapViewOfFile, status == 0) end end return A end mmap(file::AbstractString, ::Type{T}=Vector{UInt8}, dims::NTuple{N,Integer}=(div(filesize(file),sizeof(eltype(T))),), offset::Integer=Int64(0); grow::Bool=true, shared::Bool=true) where {T<:Array,N} = open(io->mmap(io, T, dims, offset; grow=grow, shared=shared), file, isfile(file) ? "r" : "w+")::Array{eltype(T),N} # using a length argument instead of dims mmap(io::IO, ::Type{T}, len::Integer, offset::Integer=position(io); grow::Bool=true, shared::Bool=true) where {T<:Array} = mmap(io, T, (len,), offset; grow=grow, shared=shared) mmap(file::AbstractString, ::Type{T}, len::Integer, offset::Integer=Int64(0); grow::Bool=true, shared::Bool=true) where {T<:Array} = open(io->mmap(io, T, (len,), offset; grow=grow, shared=shared), file, isfile(file) ? "r" : "w+")::Vector{eltype(T)} # constructors for non-file-backed (anonymous) mmaps mmap(::Type{T}, dims::NTuple{N,Integer}; shared::Bool=true) where {T<:Array,N} = mmap(Anonymous(), T, dims, Int64(0); shared=shared) mmap(::Type{T}, i::Integer...; shared::Bool=true) where {T<:Array} = mmap(Anonymous(), T, convert(Tuple{Vararg{Int}},i), Int64(0); shared=shared) """ mmap(io, BitArray, [dims, offset]) Create a [`BitArray`](@ref) whose values are linked to a file, using memory-mapping; it has the same purpose, works in the same way, and has the same arguments, as [`mmap`](@ref mmap), but the byte representation is different. # Examples ```jldoctest julia> using Mmap julia> io = open("mmap.bin", "w+"); julia> B = mmap(io, BitArray, (25,30000)); julia> B[3, 4000] = true; julia> Mmap.sync!(B); julia> close(io); julia> io = open("mmap.bin", "r+"); julia> C = mmap(io, BitArray, (25,30000)); julia> C[3, 4000] true julia> C[2, 4000] false julia> close(io) julia> rm("mmap.bin") ``` This creates a 25-by-30000 `BitArray`, linked to the file associated with stream `io`. """ function mmap(io::IOStream, ::Type{<:BitArray}, dims::NTuple{N,Integer}, offset::Int64=position(io); grow::Bool=true, shared::Bool=true) where N n = prod(dims) nc = Base.num_bit_chunks(n) chunks = mmap(io, Vector{UInt64}, (nc,), offset; grow=grow, shared=shared) if !isreadonly(io) chunks[end] &= Base._msk_end(n) else if chunks[end] != chunks[end] & Base._msk_end(n) throw(ArgumentError("the given file does not contain a valid BitArray of size $(join(dims, 'x')) (open with \"r+\" mode to override)")) end end B = BitArray{N}(undef, ntuple(i->0,Val(N))...) B.chunks = chunks B.len = n if N != 1 B.dims = dims end return B end mmap(file::AbstractString, ::Type{T}, dims::NTuple{N,Integer}, offset::Integer=Int64(0);grow::Bool=true, shared::Bool=true) where {T<:BitArray,N} = open(io->mmap(io, T, dims, offset; grow=grow, shared=shared), file, isfile(file) ? "r" : "w+")::BitArray{N} # using a length argument instead of dims mmap(io::IO, ::Type{T}, len::Integer, offset::Integer=position(io); grow::Bool=true, shared::Bool=true) where {T<:BitArray} = mmap(io, T, (len,), offset; grow=grow, shared=shared) mmap(file::AbstractString, ::Type{T}, len::Integer, offset::Integer=Int64(0); grow::Bool=true, shared::Bool=true) where {T<:BitArray} = open(io->mmap(io, T, (len,), offset; grow=grow, shared=shared), file, isfile(file) ? "r" : "w+")::BitVector # constructors for non-file-backed (anonymous) mmaps mmap(::Type{T}, dims::NTuple{N,Integer}; shared::Bool=true) where {T<:BitArray,N} = mmap(Anonymous(), T, dims, Int64(0); shared=shared) mmap(::Type{T}, i::Integer...; shared::Bool=true) where {T<:BitArray} = mmap(Anonymous(), T, convert(Tuple{Vararg{Int}},i), Int64(0); shared=shared) # msync flags for unix const MS_ASYNC = 1 const MS_INVALIDATE = 2 const MS_SYNC = 4 """ Mmap.sync!(array) Forces synchronization between the in-memory version of a memory-mapped `Array` or [`BitArray`](@ref) and the on-disk version. """ function sync!(m::Array, flags::Integer=MS_SYNC) offset = rem(UInt(pointer(m)), PAGESIZE) ptr = pointer(m) - offset mmaplen = sizeof(m) + offset GC.@preserve m @static if Sys.isunix() systemerror("msync", ccall(:msync, Cint, (Ptr{Cvoid}, Csize_t, Cint), ptr, mmaplen, flags) != 0) else Base.windowserror(:FlushViewOfFile, ccall(:FlushViewOfFile, stdcall, Cint, (Ptr{Cvoid}, Csize_t), ptr, mmaplen) == 0) end end sync!(B::BitArray, flags::Integer=MS_SYNC) = sync!(B.chunks, flags) @static if Sys.isunix() const MADV_NORMAL = 0 const MADV_RANDOM = 1 const MADV_SEQUENTIAL = 2 const MADV_WILLNEED = 3 const MADV_DONTNEED = 4 if Sys.islinux() const MADV_FREE = 8 const MADV_REMOVE = 9 const MADV_DONTFORK = 10 const MADV_DOFORK = 11 const MADV_MERGEABLE = 12 const MADV_UNMERGEABLE = 13 const MADV_HUGEPAGE = 14 const MADV_NOHUGEPAGE = 15 const MADV_DONTDUMP = 16 const MADV_DODUMP = 17 const MADV_WIPEONFORK = 18 const MADV_KEEPONFORK = 19 const MADV_COLD = 20 const MADV_PAGEOUT = 21 const MADV_HWPOISON = 100 const MADV_SOFT_OFFLINE = 101 elseif Sys.isapple() const MADV_FREE = 5 elseif Sys.isfreebsd() || Sys.isdragonfly() const MADV_FREE = 5 const MADV_NOSYNC = 6 const MADV_AUTOSYNC = 7 const MADV_NOCORE = 8 const MADV_CORE = 9 if Sys.isfreebsd() const MADV_PROTECT = 10 else const MADV_INVAL = 10 const MADV_SETMAP = 11 end elseif Sys.isopenbsd() || Sys.isnetbsd() const MADV_SPACEAVAIL = 5 const MADV_FREE = 6 end """ Mmap.madvise!(array, flag::Integer = Mmap.MADV_NORMAL) Advises the kernel on the intended usage of the memory-mapped `array`, with the intent `flag` being one of the available `MADV_*` constants. """ function madvise!(m::Array, flag::Integer=MADV_NORMAL) offset = rem(UInt(pointer(m)), PAGESIZE) ptr = pointer(m) - offset mmaplen = sizeof(m) + offset GC.@preserve m begin systemerror("madvise", ccall(:madvise, Cint, (Ptr{Cvoid}, Csize_t, Cint), ptr, mmaplen, flag) != 0) end end madvise!(B::BitArray, flag::Integer=MADV_NORMAL) = madvise!(B.chunks, flag) end # Sys.isunix() end # module €/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/NetworkOptions/src/NetworkOptions.jl๕module NetworkOptions include("ca_roots.jl") include("ssh_options.jl") include("verify_host.jl") function __init__() SYSTEM_CA_ROOTS[] = nothing BUNDLED_KNOWN_HOSTS_FILE[] = nothing empty!(ENV_HOST_PATTERN_CACHE) end end # module z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/NetworkOptions/src/ca_roots.jlิexport ca_roots, ca_roots_path """ ca_roots() :: Union{Nothing, String} The `ca_roots()` function tells the caller where, if anywhere, to find a file or directory of PEM-encoded certificate authority roots. By default, on systems like Windows and macOS where the built-in TLS engines know how to verify hosts using the system's built-in certificate verification mechanism, this function will return `nothing`. On classic UNIX systems (excluding macOS), root certificates are typically stored in a file in `/etc`: the common places for the current UNIX system will be searched and if one of these paths exists, it will be returned; if none of these typical root certificate paths exist, then the path to the set of root certificates that are bundled with Julia is returned. The default value returned by `ca_roots()` may be overridden by setting the `JULIA_SSL_CA_ROOTS_PATH`, `SSL_CERT_DIR`, or `SSL_CERT_FILE` environment variables, in which case this function will always return the value of the first of these variables that is set (whether the path exists or not). If `JULIA_SSL_CA_ROOTS_PATH` is set to the empty string, then the other variables are ignored (as if unset); if the other variables are set to the empty string, they behave is if they are not set. """ ca_roots()::Union{Nothing,String} = _ca_roots(true) """ ca_roots_path() :: String The `ca_roots_path()` function is similar to the `ca_roots()` function except that it always returns a path to a file or directory of PEM-encoded certificate authority roots. When called on a system like Windows or macOS, where system root certificates are not stored in the file system, it will currently return the path to the set of root certificates that are bundled with Julia. (In the future, this function may instead extract the root certificates from the system and save them to a file whose path would be returned.) If it is possible to configure a library that uses TLS to use the system certificates that is generally preferable: i.e. it is better to use `ca_roots()` which returns `nothing` to indicate that the system certs should be used. The `ca_roots_path()` function should only be used when configuring libraries which _require_ a path to a file or directory for root certificates. The default value returned by `ca_roots_path()` may be overridden by setting the `JULIA_SSL_CA_ROOTS_PATH`, `SSL_CERT_DIR`, or `SSL_CERT_FILE` environment variables, in which case this function will always return the value of the first of these variables that is set (whether the path exists or not). If `JULIA_SSL_CA_ROOTS_PATH` is set to the empty string, then the other variables are ignored (as if unset); if the other variables are set to the empty string, they behave is if they are not set. """ ca_roots_path()::String = _ca_roots(false) # NOTE: this has to be a function not a constant since the # value of Sys.BINDIR changes from build time to run time. bundled_ca_roots() = normpath(Sys.BINDIR::String, "..", "share", "julia", "cert.pem") const LINUX_CA_ROOTS = [ "/etc/ssl/cert.pem" # Alpine Linux "/etc/ssl/ca-bundle.pem" # OpenSUSE "/etc/ssl/ca-certificates.pem" # OpenSUSE "/etc/ssl/certs/ca-bundle.crt" # Debian/Ubuntu/Gentoo etc. "/etc/ssl/certs/ca-certificates.crt" # Debian/Ubuntu/Gentoo etc. "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" # CentOS/RHEL 7 "/etc/pki/tls/certs/ca-bundle.crt" # Fedora/RHEL 6 "/etc/pki/tls/certs/ca-certificates.crt" # Fedora/RHEL 6 "/etc/pki/tls/cacert.pem" # OpenELEC ] const BSD_CA_ROOTS = [ "/etc/ssl/cert.pem" # OpenBSD "/usr/local/share/certs/ca-root-nss.crt" # FreeBSD "/usr/local/etc/ssl/cert.pem" # FreeBSD ] const SYSTEM_CA_ROOTS_LOCK = ReentrantLock() const SYSTEM_CA_ROOTS = Ref{Union{Nothing, String}}(nothing) const BEGIN_CERT_REGULAR = "-----BEGIN CERTIFICATE-----" const BEGIN_CERT_OPENSSL = "-----BEGIN TRUSTED CERTIFICATE-----" const OPENSSL_WARNING = """ NetworkOptions could only find OpenSSL-specific TLS certificates which cannot be used by MbedTLS. Please open an issue at https://github.com/JuliaLang/NetworkOptions.jl/issues with details about your system, especially where generic non-OpenSSL certificates can be found. See https://stackoverflow.com/questions/55447752/what-does-begin-trusted-certificate-in-a-certificate-mean for more details. """ |> split |> text -> join(text, " ") function system_ca_roots() lock(SYSTEM_CA_ROOTS_LOCK) do SYSTEM_CA_ROOTS[] !== nothing && return # from lock() search_path = Sys.islinux() ? LINUX_CA_ROOTS : Sys.isbsd() && !Sys.isapple() ? BSD_CA_ROOTS : String[] openssl_only = false for path in search_path ispath(path) || continue for line in eachline(path) if line == BEGIN_CERT_REGULAR SYSTEM_CA_ROOTS[] = path return # from lock() elseif line == BEGIN_CERT_OPENSSL openssl_only = true end end end # warn if we: # 1. did not find any regular certs # 2. did find OpenSSL-only certs openssl_only && @warn OPENSSL_WARNING # TODO: extract system certs on Windows & macOS SYSTEM_CA_ROOTS[] = bundled_ca_roots() end return SYSTEM_CA_ROOTS[] end const CA_ROOTS_VARS = [ "JULIA_SSL_CA_ROOTS_PATH" "SSL_CERT_DIR" "SSL_CERT_FILE" ] function _ca_roots(allow_nothing::Bool) for var in CA_ROOTS_VARS path = get(ENV, var, nothing) if path == "" && startswith(var, "JULIA_") break # ignore other vars end if !isempty(something(path, "")) return path end end if Sys.iswindows() || Sys.isapple() allow_nothing && return # use system certs end return system_ca_roots() end }/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/NetworkOptions/src/ssh_options.jlลexport ssh_dir, ssh_key_pass, ssh_key_name, ssh_key_path, ssh_pub_key_path, ssh_known_hosts_files, ssh_known_hosts_file """ ssh_dir() :: String The `ssh_dir()` function returns the location of the directory where the `ssh` program keeps/looks for configuration files. By default this is `~/.ssh` but this can be overridden by setting the environment variable `SSH_DIR`. """ ssh_dir() = get(ENV, "SSH_DIR", joinpath(homedir(), ".ssh")) """ ssh_key_pass() :: String The `ssh_key_pass()` function returns the value of the environment variable `SSH_KEY_PASS` if it is set or `nothing` if it is not set. In the future, this may be able to find a password by other means, such as secure system storage, so packages that need a password to decrypt an SSH private key should use this API instead of directly checking the environment variable so that they gain such capabilities automatically when they are added. """ ssh_key_pass() = get(ENV, "SSH_KEY_PASS", nothing) """ ssh_key_name() :: String The `ssh_key_name()` function returns the base name of key files that SSH should use for when establishing a connection. There is usually no reason that this function should be called directly and libraries should generally use the `ssh_key_path` and `ssh_pub_key_path` functions to get full paths. If the environment variable `SSH_KEY_NAME` is set then this function returns that; otherwise it returns `id_rsa` by default. """ ssh_key_name() = get(ENV, "SSH_KEY_NAME", "id_rsa") """ ssh_key_path() :: String The `ssh_key_path()` function returns the path of the SSH private key file that should be used for SSH connections. If the `SSH_KEY_PATH` environment variable is set then it will return that value. Otherwise it defaults to returning joinpath(ssh_dir(), ssh_key_name()) This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME` environment variables. """ function ssh_key_path() key_path = get(ENV, "SSH_KEY_PATH", "") !isempty(key_path) && return key_path return joinpath(ssh_dir(), ssh_key_name()) end """ ssh_pub_key_path() :: String The `ssh_pub_key_path()` function returns the path of the SSH public key file that should be used for SSH connections. If the `SSH_PUB_KEY_PATH` environment variable is set then it will return that value. If that isn't set but `SSH_KEY_PATH` is set, it will return that path with the `.pub` suffix appended. If neither is set, it defaults to returning joinpath(ssh_dir(), ssh_key_name() * ".pub") This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME` environment variables. """ function ssh_pub_key_path() pub_key_path = get(ENV, "SSH_PUB_KEY_PATH", "") !isempty(pub_key_path) && return pub_key_path key_path = get(ENV, "SSH_KEY_PATH", "") !isempty(key_path) && return "$key_path.pub" return joinpath(ssh_dir(), ssh_key_name() * ".pub") end """ ssh_known_hosts_files() :: Vector{String} The `ssh_known_hosts_files()` function returns a vector of paths of SSH known hosts files that should be used when establishing the identities of remote servers for SSH connections. By default this function returns [joinpath(ssh_dir(), "known_hosts"), bundled_known_hosts] where `bundled_known_hosts` is the path of a copy of a known hosts file that is bundled with this package (containing known hosts keys for `github.com` and `gitlab.com`). If the environment variable `SSH_KNOWN_HOSTS_FILES` is set, however, then its value is split into paths on the `:` character (or on `;` on Windows) and this vector of paths is returned instead. If any component of this vector is empty, it is expanded to the default known hosts paths. Packages that use `ssh_known_hosts_files()` should ideally look for matching entries by comparing the host name and key types, considering the first entry in any of the files which matches to be the definitive identity of the host. If the caller cannot compare the key type (e.g. because it has been hashes) then it must approximate the above algorithm by looking for all matching entries for a host in each file: if a file has any entries for a host then one of them must match; the caller should only continue to search further known hosts files if there are no entries for the host in question in an earlier file. """ function ssh_known_hosts_files() bundled = bundled_known_hosts() default = joinpath(ssh_dir(), "known_hosts") value = get(ENV, "SSH_KNOWN_HOSTS_FILES", nothing) value === nothing && return [default, bundled] isempty(value) && return String[] paths = String[] for path in split(value, Sys.iswindows() ? ';' : ':') if !isempty(path) path in paths || push!(paths, path) else default in paths || push!(paths, default) bundled in paths || push!(paths, bundled) end end return paths end """ ssh_known_hosts_file() :: String The `ssh_known_hosts_file()` function returns a single path of an SSH known hosts file that should be used when establishing the identities of remote servers for SSH connections. It returns the first path returned by `ssh_known_hosts_files` that actually exists. Callers who can look in more than one known hosts file should use `ssh_known_hosts_files` instead and look for host matches in all the files returned as described in that function's docs. """ function ssh_known_hosts_file() files = ssh_known_hosts_files() for file in files ispath(file) && return file end return !isempty(files) ? files[1] : isfile("/dev/null") ? "/dev/null" : tempname() end ## helper functions const BUNDLED_KNOWN_HOSTS_LOCK = ReentrantLock() const BUNDLED_KNOWN_HOSTS_FILE = Ref{Union{Nothing, String}}(nothing) function bundled_known_hosts() lock(BUNDLED_KNOWN_HOSTS_LOCK) do file = BUNDLED_KNOWN_HOSTS_FILE[] if file === nothing file, io = mktemp() BUNDLED_KNOWN_HOSTS_FILE[] = file write(io, BUNDLED_KNOWN_HOSTS) close(io) end return file::String end end const BUNDLED_KNOWN_HOSTS = """ github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg= github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk= gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 """ }/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/NetworkOptions/src/verify_host.jlDexport verify_host """ verify_host(url::AbstractString, [transport::AbstractString]) :: Bool The `verify_host` function tells the caller whether the identity of a host should be verified when communicating over secure transports like TLS or SSH. The `url` argument may be: 1. a proper URL staring with `proto://` 2. an `ssh`-style bare host name or host name prefixed with `user@` 3. an `scp`-style host as above, followed by `:` and a path location In each case the host name part is parsed out and the decision about whether to verify or not is made based solely on the host name, not anything else about the input URL. In particular, the protocol of the URL does not matter (more below). The `transport` argument indicates the kind of transport that the query is about. The currently known values are `SSL`/`ssl` (alias `TLS`/`tls`) and `SSH`/`ssh`. If the transport is omitted, the query will return `true` only if the host name should not be verified regardless of transport. The host name is matched against the host patterns in the relevant environment variables depending on whether `transport` is supplied and what its value is: - `JULIA_NO_VERIFY_HOSTS` โ€”ย hosts that should not be verified for any transport - `JULIA_SSL_NO_VERIFY_HOSTS` โ€”ย hosts that should not be verified for SSL/TLS - `JULIA_SSH_NO_VERIFY_HOSTS` โ€”ย hosts that should not be verified for SSH - `JULIA_ALWAYS_VERIFY_HOSTS` โ€” hosts that should always be verified The values of each of these variables is a comma-separated list of host name patterns with the following syntax โ€” each pattern is split on `.` into parts and each part must one of: 1. A literal domain name component consisting of one or more ASCII letter, digit, hyphen or underscore (technically not part of a legal host name, but sometimes used). A literal domain name component matches only itself. 2. A `**`, which matches zero or more domain name components. 3. A `*`, which match any one domain name component. When matching a host name against a pattern list in one of these variables, the host name is split on `.` into components and that sequence of words is matched against the pattern: a literal pattern matches exactly one host name component with that value; a `*` pattern matches exactly one host name component with any value; a `**` pattern matches any number of host name components. For example: - `**` matches any host name - `**.org` matches any host name in the `.org` top-level domain - `example.com` matches only the exact host name `example.com` - `*.example.com` matches `api.example.com` but not `example.com` or `v1.api.example.com` - `**.example.com` matches any domain under `example.com`, including `example.com` itself, `api.example.com` and `v1.api.example.com` """ function verify_host( url :: AbstractString, tr :: Union{AbstractString, Nothing} = nothing, ) host = url_host(url) env_host_pattern_match("JULIA_ALWAYS_VERIFY_HOSTS", host) && return true env_host_pattern_match("JULIA_NO_VERIFY_HOSTS", host) && return false tr === nothing && return true return if tr == "SSL" || tr == "ssl" || tr == "TLS" || tr == "tls" !env_host_pattern_match("JULIA_SSL_NO_VERIFY_HOSTS", host) elseif tr == "SSH" || tr == "ssh" !env_host_pattern_match("JULIA_SSH_NO_VERIFY_HOSTS", host) else true # do verify end end function url_host(url::AbstractString) m = match(r"^(?:[a-z]+)://(?:[^@/]+@)?([-\w\.]+)"ai, url) m !== nothing && return m.captures[1] m = match(r"^(?:[-\w\.]+@)?([-\w\.]+)(?:$|:)"a, url) m !== nothing && return m.captures[1] return nothing # couldn't parse end const MATCH_ANY_RE = r"" const MATCH_NONE_RE = r"$.^" # TODO: What to do when `env_host_pattern_regex` returns `nothing`? env_host_pattern_match(var::AbstractString, host::AbstractString) = occursin(env_host_pattern_regex(var)::Regex, host) env_host_pattern_match(var::AbstractString, host::Nothing) = env_host_pattern_regex(var) === MATCH_ANY_RE const ENV_HOST_PATTERN_LOCK = ReentrantLock() const ENV_HOST_PATTERN_CACHE = Dict{String,Tuple{String,Regex}}() function env_host_pattern_regex(var::AbstractString) lock(ENV_HOST_PATTERN_LOCK) do value = get(ENV, var, nothing) if value === nothing delete!(ENV_HOST_PATTERN_CACHE, var) return MATCH_NONE_RE end old_value, regex = get(ENV_HOST_PATTERN_CACHE, var, (nothing, nothing)) old_value == value && return regex regex = host_pattern_regex(value, var) ENV_HOST_PATTERN_CACHE[var] = (value, regex) return regex end end if !@isdefined(contains) contains(needle) = haystack -> occursin(needle, haystack) end function host_pattern_regex(value::AbstractString, var::AbstractString="") match_any = false patterns = Vector{String}[] for pattern in split(value, r"\s*,\s*", keepempty=false) match_any |= pattern == "**" parts = split(pattern, '.') # emit warning but ignore any pattern we don't recognize; # this allows adding syntax without breaking old versions if !all(contains(r"^([-\w]+|\*\*?)$"a), parts) in = isempty(var) ? "" : " in ENV[$(repr(var))]" @warn("bad host pattern$in: $(repr(pattern))") continue end push!(patterns, parts) end match_any && return MATCH_ANY_RE isempty(patterns) && return MATCH_NONE_RE regex = "" for parts in patterns re = "" for (i, part) in enumerate(parts) re *= if i < length(parts) part == "*" ? "[-\\w]+\\." : part == "**" ? "(?:[-\\w]+\\.)*" : "$part\\." else part == "*" ? "[-\\w]+" : part == "**" ? "(?:[-\\w]+\\.)*[-\\w]+" : part end end regex = isempty(regex) ? re : "$regex|$re" end return Regex("^(?:$regex)\$", "ai") end j/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/SHA.jl""" SHA The SHA module provides hashing functionality for SHA1, SHA2 and SHA3 algorithms. They are implemented as both pure functions for hashing single pieces of data, or a stateful context which can be updated with the `update!` function and finalized with `digest!`. ```julia-repl julia> sha1(b"some data") 20-element Vector{UInt8}: 0xba 0xf3 โ‹ฎ 0xe3 0x56 julia> ctx = SHA1_CTX() SHA1 hash state julia> update!(ctx, b"some data") 0x0000000000000009 julia> digest!(ctx) 20-element Vector{UInt8}: 0xba 0xf3 โ‹ฎ 0xe3 0x56 """ module SHA # Export convenience functions, context types, update!() and digest!() functions export sha1, SHA1_CTX, update!, digest! export sha224, sha256, sha384, sha512 export sha2_224, sha2_256, sha2_384, sha2_512 export sha3_224, sha3_256, sha3_384, sha3_512 export SHA224_CTX, SHA256_CTX, SHA384_CTX, SHA512_CTX export SHA2_224_CTX, SHA2_256_CTX, SHA2_384_CTX, SHA2_512_CTX export SHA3_224_CTX, SHA3_256_CTX, SHA3_384_CTX, SHA3_512_CTX export HMAC_CTX, hmac_sha1 export hmac_sha224, hmac_sha256, hmac_sha384, hmac_sha512 export hmac_sha2_224, hmac_sha2_256, hmac_sha2_384, hmac_sha2_512 export hmac_sha3_224, hmac_sha3_256, hmac_sha3_384, hmac_sha3_512 # data to be hashed: const AbstractBytes = Union{AbstractVector{UInt8},NTuple{N,UInt8} where N} include("constants.jl") include("types.jl") include("base_functions.jl") include("sha1.jl") include("sha2.jl") include("sha3.jl") include("common.jl") include("hmac.jl") # Create data types and convenience functions for each hash implemented for (f, ctx) in [(:sha1, :SHA1_CTX), (:sha224, :SHA224_CTX), (:sha256, :SHA256_CTX), (:sha384, :SHA384_CTX), (:sha512, :SHA512_CTX), (:sha2_224, :SHA2_224_CTX), (:sha2_256, :SHA2_256_CTX), (:sha2_384, :SHA2_384_CTX), (:sha2_512, :SHA2_512_CTX), (:sha3_224, :SHA3_224_CTX), (:sha3_256, :SHA3_256_CTX), (:sha3_384, :SHA3_384_CTX), (:sha3_512, :SHA3_512_CTX),] g = Symbol(:hmac_, f) @eval begin # Our basic function is to process arrays of bytes """ $($f)(data) Hash data using the `$($f)` algorithm and return the resulting digest. See also [`$($ctx)`](@ref). """ function $f(data::AbstractBytes) ctx = $ctx() update!(ctx, data) return digest!(ctx) end """ $($g)(key, data) Hash data using the `$($f)` algorithm using the passed key. See also [`HMAC_CTX`](@ref). """ function $g(key::Vector{UInt8}, data::AbstractBytes) ctx = HMAC_CTX($ctx(), key) update!(ctx, data) return digest!(ctx) end # AbstractStrings are a pretty handy thing to be able to crunch through $f(str::AbstractString) = $f(String(str)) # always crunch UTF-8 repr $f(str::String) = $f(codeunits(str)) $g(key::Vector{UInt8}, str::AbstractString) = $g(key, String(str)) $g(key::Vector{UInt8}, str::String) = $g(key, codeunits(str)) """ $($f)(io::IO) Hash data from io using `$($f)` algorithm. """ function $f(io::IO, chunk_size=4*1024) ctx = $ctx() buff = Vector{UInt8}(undef, chunk_size) while !eof(io) num_read = readbytes!(io, buff) update!(ctx, buff, num_read) end return digest!(ctx) end """ $($g)(key, io::IO) Hash data from `io` with the passed key using `$($f)` algorithm. """ function $g(key::Vector{UInt8}, io::IO, chunk_size=4*1024) ctx = HMAC_CTX($ctx(), key) buff = Vector{UInt8}(undef, chunk_size) while !eof(io) num_read = readbytes!(io, buff) update!(ctx, buff, num_read) end return digest!(ctx) end end end end #module SHA p/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/constants.jlะ# SHA initial hash values and constants # Hash constant words K for SHA1 const K1 = UInt32[ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 ] # Initial hash value H for SHA1 const SHA1_initial_hash_value = UInt32[ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ] # Hash constant words K for SHA-256: const K256 = ( 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ) # Initial hash value H for SHA-224: const SHA2_224_initial_hash_value = UInt32[ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ] const SHA2_256_initial_hash_value = UInt32[ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ] # Hash constant words K for SHA-384 and SHA-512: const K512 = ( 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 ) # Initial hash value H for SHA-384 const SHA2_384_initial_hash_value = UInt64[ 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4 ] # Initial hash value H for SHA-512 const SHA2_512_initial_hash_value = UInt64[ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 ] # Round constants for SHA3 rounds const SHA3_ROUND_CONSTS = UInt64[ 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 ] # Rotation constants for SHA3 rounds const SHA3_ROTC = UInt64[ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 ] # Permutation indices for SHA3 rounds (+1'ed so as to work with julia's 1-based indexing) const SHA3_PILN = Int[ 11, 8, 12, 18, 19, 4, 6, 17, 9, 22, 25, 5, 16, 24, 20, 14, 13, 3, 21, 15, 23, 10, 7, 2 ] l/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/types.jl€# Type hierarchy to aid in splitting up of SHA2 algorithms # as SHA224/256 are similar, and SHA-384/512 are similar abstract type SHA_CTX end abstract type SHA2_CTX <: SHA_CTX end abstract type SHA3_CTX <: SHA_CTX end import Base: copy # We derive SHA1_CTX straight from SHA_CTX since it doesn't have a # family of types like SHA2 or SHA3 do mutable struct SHA1_CTX <: SHA_CTX state::Array{UInt32,1} bytecount::UInt64 buffer::Array{UInt8,1} W::Array{UInt32,1} used::Bool end # SHA2 224/256/384/512-bit Context Structures mutable struct SHA2_224_CTX <: SHA2_CTX state::Array{UInt32,1} bytecount::UInt64 buffer::Array{UInt8,1} used::Bool end mutable struct SHA2_256_CTX <: SHA2_CTX state::Array{UInt32,1} bytecount::UInt64 buffer::Array{UInt8,1} used::Bool end mutable struct SHA2_384_CTX <: SHA2_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} used::Bool end mutable struct SHA2_512_CTX <: SHA2_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} used::Bool end function Base.getproperty(ctx::SHA2_CTX, fieldname::Symbol) if fieldname === :state return getfield(ctx, :state)::Union{Vector{UInt32},Vector{UInt64}} elseif fieldname === :bytecount return getfield(ctx, :bytecount)::Union{UInt64,UInt128} elseif fieldname === :buffer return getfield(ctx, :buffer)::Vector{UInt8} elseif fieldname === :W return getfield(ctx, :W)::Vector{UInt32} elseif fieldname === :used return getfield(ctx, :used)::Bool else error("SHA2_CTX has no field ", fieldname) end end # Typealias common nicknames for SHA2 family of functions const SHA224_CTX = SHA2_224_CTX const SHA256_CTX = SHA2_256_CTX const SHA384_CTX = SHA2_384_CTX const SHA512_CTX = SHA2_512_CTX # SHA3 224/256/384/512-bit context structures mutable struct SHA3_224_CTX <: SHA3_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} bc::Array{UInt64,1} used::Bool end mutable struct SHA3_256_CTX <: SHA3_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} bc::Array{UInt64,1} used::Bool end mutable struct SHA3_384_CTX <: SHA3_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} bc::Array{UInt64,1} used::Bool end mutable struct SHA3_512_CTX <: SHA3_CTX state::Array{UInt64,1} bytecount::UInt128 buffer::Array{UInt8,1} bc::Array{UInt64,1} used::Bool end function Base.getproperty(ctx::SHA3_CTX, fieldname::Symbol) if fieldname === :state return getfield(ctx, :state)::Vector{UInt64} elseif fieldname === :bytecount return getfield(ctx, :bytecount)::UInt128 elseif fieldname === :buffer return getfield(ctx, :buffer)::Vector{UInt8} elseif fieldname === :bc return getfield(ctx, :bc)::Vector{UInt64} elseif fieldname === :used return getfield(ctx, :used)::Bool else error("type ", typeof(ctx), " has no field ", fieldname) end end # Define constants via functions so as not to bloat context objects. Yay dispatch! # Digest lengths for SHA1, SHA2 and SHA3. This is easy to figure out from the typename digestlen(::Type{SHA1_CTX}) = 20 digestlen(::Type{SHA2_224_CTX}) = 28 digestlen(::Type{SHA3_224_CTX}) = 28 digestlen(::Type{SHA2_256_CTX}) = 32 digestlen(::Type{SHA3_256_CTX}) = 32 digestlen(::Type{SHA2_384_CTX}) = 48 digestlen(::Type{SHA3_384_CTX}) = 48 digestlen(::Type{SHA2_512_CTX}) = 64 digestlen(::Type{SHA3_512_CTX}) = 64 # SHA1 and SHA2 have differing element types for the internal state objects state_type(::Type{SHA1_CTX}) = UInt32 state_type(::Type{SHA2_224_CTX}) = UInt32 state_type(::Type{SHA2_256_CTX}) = UInt32 state_type(::Type{SHA2_384_CTX}) = UInt64 state_type(::Type{SHA2_512_CTX}) = UInt64 state_type(::Type{SHA3_CTX}) = UInt64 # blocklen is the number of bytes of data processed by the transform!() function at once blocklen(::Type{SHA1_CTX}) = UInt64(64) blocklen(::Type{SHA2_224_CTX}) = UInt64(64) blocklen(::Type{SHA2_256_CTX}) = UInt64(64) blocklen(::Type{SHA2_384_CTX}) = UInt64(128) blocklen(::Type{SHA2_512_CTX}) = UInt64(128) blocklen(::Type{SHA3_224_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_224_CTX)) blocklen(::Type{SHA3_256_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_256_CTX)) blocklen(::Type{SHA3_384_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_384_CTX)) blocklen(::Type{SHA3_512_CTX}) = UInt64(25*8 - 2*digestlen(SHA3_512_CTX)) # short_blocklen is the size of a block minus the width of bytecount short_blocklen(::Type{T}) where {T<:SHA_CTX} = blocklen(T) - 2*sizeof(state_type(T)) # Once the "blocklen" methods are defined, we can define our outer constructors for SHA types: """ SHA2_224_CTX() Construct an empty SHA2_224 context. """ SHA2_224_CTX() = SHA2_224_CTX(copy(SHA2_224_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_224_CTX)), false) """ SHA2_256_CTX() Construct an empty SHA2_256 context. """ SHA2_256_CTX() = SHA2_256_CTX(copy(SHA2_256_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_256_CTX)), false) """ SHA2_384() Construct an empty SHA2_384 context. """ SHA2_384_CTX() = SHA2_384_CTX(copy(SHA2_384_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_384_CTX)), false) """ SHA2_512_CTX() Construct an empty SHA2_512 context. """ SHA2_512_CTX() = SHA2_512_CTX(copy(SHA2_512_initial_hash_value), 0, zeros(UInt8, blocklen(SHA2_512_CTX)), false) """ SHA3_224_CTX() Construct an empty SHA3_224 context. """ SHA3_224_CTX() = SHA3_224_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_224_CTX)), Vector{UInt64}(undef, 5), false) """ SHA3_256_CTX() Construct an empty SHA3_256 context. """ SHA3_256_CTX() = SHA3_256_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_256_CTX)), Vector{UInt64}(undef, 5), false) """ SHA3_384_CTX() Construct an empty SHA3_384 context. """ SHA3_384_CTX() = SHA3_384_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_384_CTX)), Vector{UInt64}(undef, 5), false) """ SHA3_512_CTX() Construct an empty SHA3_512 context. """ SHA3_512_CTX() = SHA3_512_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHA3_512_CTX)), Vector{UInt64}(undef, 5), false) # Nickname'd outer constructor methods for SHA2 const SHA224_CTX = SHA2_224_CTX const SHA256_CTX = SHA2_256_CTX const SHA384_CTX = SHA2_384_CTX const SHA512_CTX = SHA2_512_CTX # SHA1 is special; he needs extra workspace """ SHA1_CTX() Construct an empty SHA1 context. """ SHA1_CTX() = SHA1_CTX(copy(SHA1_initial_hash_value), 0, zeros(UInt8, blocklen(SHA1_CTX)), Vector{UInt32}(undef, 80), false) # Copy functions copy(ctx::T) where {T<:SHA1_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer), copy(ctx.W), ctx.used) copy(ctx::T) where {T<:SHA2_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer), ctx.used) copy(ctx::T) where {T<:SHA3_CTX} = T(copy(ctx.state), ctx.bytecount, copy(ctx.buffer), Vector{UInt64}(undef, 5), ctx.used) # Make printing these types a little friendlier import Base.show show(io::IO, ::SHA1_CTX) = print(io, "SHA1 hash state") show(io::IO, ::SHA2_224_CTX) = print(io, "SHA2 224-bit hash state") show(io::IO, ::SHA2_256_CTX) = print(io, "SHA2 256-bit hash state") show(io::IO, ::SHA2_384_CTX) = print(io, "SHA2 384-bit hash state") show(io::IO, ::SHA2_512_CTX) = print(io, "SHA2 512-bit hash state") show(io::IO, ::SHA3_224_CTX) = print(io, "SHA3 224-bit hash state") show(io::IO, ::SHA3_256_CTX) = print(io, "SHA3 256-bit hash state") show(io::IO, ::SHA3_384_CTX) = print(io, "SHA3 384-bit hash state") show(io::IO, ::SHA3_512_CTX) = print(io, "SHA3 512-bit hash state") # use our types to define a method to get a pointer to the state buffer buffer_pointer(ctx::T) where {T<:SHA_CTX} = Ptr{state_type(T)}(pointer(ctx.buffer)) u/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/base_functions.jl.# THE SIX LOGICAL FUNCTIONS # # Bit shifting and rotation (used by the six SHA-XYZ logical functions: # # NOTE: The naming of R and S appears backwards here (R is a SHIFT and # S is a ROTATION) because the SHA2-256/384/512 description document # (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this # same "backwards" definition. # 32-bit Rotate-right (equivalent to S32 in SHA-256) and rotate-left rrot(b,x,width) = ((x >> b) | (x << (width - b))) lrot(b,x,width) = ((x << b) | (x >> (width - b))) # Shift-right (used in SHA-256, SHA-384, and SHA-512): R(b,x) = (x >> b) # 32-bit Rotate-right (used in SHA-256): S32(b,x) = rrot(b,x,32) # 64-bit Rotate-right (used in SHA-384 and SHA-512): S64(b,x) = rrot(b,x,64) # 64-bit Rotate-left (used in SHA3) L64(b,x) = lrot(b,x,64) # Two of six logical functions used in SHA-256, SHA-384, and SHA-512: Ch(x,y,z) = ((x & y) โŠป (~x & z)) Maj(x,y,z) = ((x & y) โŠป (x & z) โŠป (y & z)) # Four of six logical functions used in SHA-256: Sigma0_256(x) = (S32(2, UInt32(x)) โŠป S32(13, UInt32(x)) โŠป S32(22, UInt32(x))) Sigma1_256(x) = (S32(6, UInt32(x)) โŠป S32(11, UInt32(x)) โŠป S32(25, UInt32(x))) sigma0_256(x) = (S32(7, UInt32(x)) โŠป S32(18, UInt32(x)) โŠป R(3 , UInt32(x))) sigma1_256(x) = (S32(17, UInt32(x)) โŠป S32(19, UInt32(x)) โŠป R(10, UInt32(x))) # Four of six logical functions used in SHA-384 and SHA-512: Sigma0_512(x) = (S64(28, UInt64(x)) โŠป S64(34, UInt64(x)) โŠป S64(39, UInt64(x))) Sigma1_512(x) = (S64(14, UInt64(x)) โŠป S64(18, UInt64(x)) โŠป S64(41, UInt64(x))) sigma0_512(x) = (S64( 1, UInt64(x)) โŠป S64( 8, UInt64(x)) โŠป R( 7, UInt64(x))) sigma1_512(x) = (S64(19, UInt64(x)) โŠป S64(61, UInt64(x)) โŠป R( 6, UInt64(x))) # Let's be able to bswap arrays of these types as well bswap!(x::Vector{<:Integer}) = map!(bswap, x, x) k/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/sha1.jl # Nonlinear functions, in order to encourage inlining, these sadly are not an array of lambdas function Round0(b,c,d) return UInt32((b & c) | (~b & d)) end function Round1And3(b,c,d) return UInt32(b โŠป c โŠป d) end function Round2(b,c,d) return UInt32((b & c) | (b & d) | (c & d)) end function transform!(context::SHA1_CTX) # Buffer is 16 elements long, we expand to 80 pbuf = buffer_pointer(context) for i in 1:16 context.W[i] = bswap(unsafe_load(pbuf, i)) end # First round of expansions for i in 17:32 @inbounds begin context.W[i] = lrot(1, context.W[i-3] โŠป context.W[i-8] โŠป context.W[i-14] โŠป context.W[i-16], 32) end end # Second round of expansions (possibly 4-way SIMD-able) for i in 33:80 @inbounds begin context.W[i] = lrot(2, context.W[i-6] โŠป context.W[i-16] โŠป context.W[i-28] โŠป context.W[i-32], 32) end end # Initialize registers with the previous intermediate values (our state) a = context.state[1] b = context.state[2] c = context.state[3] d = context.state[4] e = context.state[5] # Run our rounds, manually separated into the four rounds, unfortunately using an array of lambdas # really kills performance and causes a huge number of allocations, so we make it easy on the compiler for i = 1:20 @inbounds begin temp = UInt32(lrot(5, a, 32) + Round0(b,c,d) + e + context.W[i] + K1[1]) e = d d = c c = lrot(30, b, 32) b = a a = temp end end for i = 21:40 @inbounds begin temp = UInt32(lrot(5, a, 32) + Round1And3(b,c,d) + e + context.W[i] + K1[2]) e = d d = c c = lrot(30, b, 32) b = a a = temp end end for i = 41:60 @inbounds begin temp = UInt32(lrot(5, a, 32) + Round2(b,c,d) + e + context.W[i] + K1[3]) e = d d = c c = lrot(30, b, 32) b = a a = temp end end for i = 61:80 @inbounds begin temp = UInt32(lrot(5, a, 32) + Round1And3(b,c,d) + e + context.W[i] + K1[4]) e = d d = c c = lrot(30, b, 32) b = a a = temp end end context.state[1] += a context.state[2] += b context.state[3] += c context.state[4] += d context.state[5] += e end k/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/sha2.jlเ macro R1_16(j, T) ww = (:a, :b, :c, :d, :e, :f, :g, :h) a = ww[((81 - j) % 8) + 1] b = ww[((82 - j) % 8) + 1] c = ww[((83 - j) % 8) + 1] d = ww[((84 - j) % 8) + 1] e = ww[((85 - j) % 8) + 1] f = ww[((86 - j) % 8) + 1] g = ww[((87 - j) % 8) + 1] h = ww[((88 - j) % 8) + 1] if T == 512 Sigma0 = :Sigma0_512 Sigma1 = :Sigma1_512 K = :K512 elseif T == 256 Sigma0 = :Sigma0_256 Sigma1 = :Sigma1_256 K = :K256 end return esc(quote # We byteswap every input byte v = bswap(unsafe_load(pbuf, $j)) unsafe_store!(pbuf, v, $j) # Apply the SHA-256 compression function to update a..h T1 = $h + $Sigma1($e) + Ch($e, $f, $g) + $K[$j] + v $h = $Sigma0($a) + Maj($a, $b, $c) $d += T1 $h += T1 end) end macro R17_80(j, T) ww = (:a, :b, :c, :d, :e, :f, :g, :h) a = ww[((81 - j) % 8) + 1] b = ww[((82 - j) % 8) + 1] c = ww[((83 - j) % 8) + 1] d = ww[((84 - j) % 8) + 1] e = ww[((85 - j) % 8) + 1] f = ww[((86 - j) % 8) + 1] g = ww[((87 - j) % 8) + 1] h = ww[((88 - j) % 8) + 1] if T == 512 Sigma0 = :Sigma0_512 Sigma1 = :Sigma1_512 sigma0 = :sigma0_512 sigma1 = :sigma1_512 K = :K512 elseif T == 256 Sigma0 = :Sigma0_256 Sigma1 = :Sigma1_256 sigma0 = :sigma0_256 sigma1 = :sigma1_256 K = :K256 end return esc(quote s0 = unsafe_load(pbuf, mod1($j + 1, 16)) s0 = $sigma0(s0) s1 = unsafe_load(pbuf, mod1($j + 14, 16)) s1 = $sigma1(s1) # Apply the SHA-256 compression function to update a..h v = unsafe_load(pbuf, mod1($j, 16)) + s1 + unsafe_load(pbuf, mod1($j + 9, 16)) + s0 unsafe_store!(pbuf, v, mod1($j, 16)) T1 = $h + $Sigma1($e) + Ch($e, $f, $g) + $K[$j] + v $h = $Sigma0($a) + Maj($a, $b, $c) $d += T1 $h += T1 end) end macro R_init(T) expr = :() for i in 1:16 expr = :($expr; @R1_16($i, $T)) end return esc(expr) end macro R_end(T) if T == 256 n_rounds = 64 elseif T == 512 n_rounds = 80 end expr = :() for i in 17:n_rounds expr = :($expr; @R17_80($i, $T)) end return esc(expr) end @generated function transform!(context::Union{SHA2_224_CTX, SHA2_256_CTX, SHA2_384_CTX, SHA2_512_CTX}) if context <: Union{SHA2_224_CTX,SHA2_256_CTX} T = 256 elseif context <: Union{SHA2_384_CTX,SHA2_512_CTX} T = 512 end return quote pbuf = buffer_pointer(context) # Initialize registers with the previous intermediate values (our state) a, b, c, d, e, f, g, h = context.state # Initial Rounds @R_init($T) # Other Rounds @R_end($T) # Compute the current intermediate hash value @inbounds begin context.state[1] += a context.state[2] += b context.state[3] += c context.state[4] += d context.state[5] += e context.state[6] += f context.state[7] += g context.state[8] += h end end end k/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/sha3.jlญ function transform!(context::T) where {T<:SHA3_CTX} # First, update state with buffer pbuf = Ptr{eltype(context.state)}(pointer(context.buffer)) for idx in 1:div(blocklen(T),8) context.state[idx] = context.state[idx] โŠป unsafe_load(pbuf, idx) end bc = context.bc state = context.state # We always assume 24 rounds @inbounds for round in 0:23 # Theta function for i in 1:5 bc[i] = state[i] โŠป state[i + 5] โŠป state[i + 10] โŠป state[i + 15] โŠป state[i + 20] end for i in 0:4 temp = bc[rem(i + 4, 5) + 1] โŠป L64(1, bc[rem(i + 1, 5) + 1]) j = 0 while j <= 20 state[Int(i + j + 1)] = state[i + j + 1] โŠป temp j += 5 end end # Rho Pi temp = state[2] for i in 1:24 j = SHA3_PILN[i] bc[1] = state[j] state[j] = L64(SHA3_ROTC[i], temp) temp = bc[1] end # Chi j = 0 while j <= 20 for i in 1:5 bc[i] = state[i + j] end for i in 0:4 state[j + i + 1] = state[j + i + 1] โŠป (~bc[rem(i + 1, 5) + 1] & bc[rem(i + 2, 5) + 1]) end j += 5 end # Iota state[1] = state[1] โŠป SHA3_ROUND_CONSTS[round+1] end return context.state end # Finalize data in the buffer, append total bitlength, and return our precious hash! function digest!(context::T) where {T<:SHA3_CTX} if !context.used usedspace = context.bytecount % blocklen(T) # If we have anything in the buffer still, pad and transform that data if usedspace < blocklen(T) - 1 # Begin padding with a 0x06 context.buffer[usedspace+1] = 0x06 # Fill with zeros up until the last byte context.buffer[usedspace+2:end-1] .= 0x00 # Finish it off with a 0x80 context.buffer[end] = 0x80 else # Otherwise, we have to add on a whole new buffer just for the zeros and 0x80 context.buffer[end] = 0x06 transform!(context) context.buffer[1:end-1] .= 0x0 context.buffer[end] = 0x80 end # Final transform: transform!(context) context.used = true end # Return the digest return reinterpret(UInt8, context.state)[1:digestlen(T)] end m/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/common.jl# Common update and digest functions which work across SHA1 and SHA2 # update! takes in variable-length data, buffering it into blocklen()-sized pieces, # calling transform!() when necessary to update the internal hash state. """ update!(context, data[, datalen]) Update the SHA context with the bytes in data. See also [`digest!`](@ref) for finalizing the hash. # Examples ```julia-repl julia> ctx = SHA1_CTX() SHA1 hash state julia> update!(ctx, b"data to to be hashed") ``` """ function update!(context::T, data::U, datalen=length(data)) where {T<:SHA_CTX, U<:AbstractBytes} context.used && error("Cannot update CTX after `digest!` has been called on it") # We need to do all our arithmetic in the proper bitwidth UIntXXX = typeof(context.bytecount) # Process as many complete blocks as possible 0 โ‰ค datalen โ‰ค length(data) || throw(BoundsError(data, firstindex(data)+datalen-1)) len = convert(UIntXXX, datalen) data_idx = convert(UIntXXX, firstindex(data)-1) usedspace = context.bytecount % blocklen(T) while len - data_idx + usedspace >= blocklen(T) # Fill up as much of the buffer as we can with the data given us copyto!(context.buffer, usedspace + 1, data, data_idx + 1, blocklen(T) - usedspace) transform!(context) context.bytecount += blocklen(T) - usedspace data_idx += blocklen(T) - usedspace usedspace = convert(UIntXXX, 0) end # There is less than a complete block left, but we need to save the leftovers into context.buffer: if len > data_idx copyto!(context.buffer, usedspace + 1, data, data_idx + 1, len - data_idx) context.bytecount += len - data_idx end end # Pad the remainder leaving space for the bitcount function pad_remainder!(context::T) where T<:SHA_CTX usedspace = context.bytecount % blocklen(T) # If we have anything in the buffer still, pad and transform that data if usedspace > 0 # Begin padding with a 1 bit: context.buffer[usedspace+1] = 0x80 usedspace += 1 # If we have room for the bitcount, then pad up to the short blocklen if usedspace <= short_blocklen(T) for i = 1:(short_blocklen(T) - usedspace) context.buffer[usedspace + i] = 0x0 end else # Otherwise, pad out this entire block, transform it, then pad up to short blocklen for i = 1:(blocklen(T) - usedspace) context.buffer[usedspace + i] = 0x0 end transform!(context) for i = 1:short_blocklen(T) context.buffer[i] = 0x0 end end else # If we don't have anything in the buffer, pad an entire shortbuffer context.buffer[1] = 0x80 for i = 2:short_blocklen(T) context.buffer[i] = 0x0 end end end # Clear out any saved data in the buffer, append total bitlength, and return our precious hash! # Note: SHA3_CTX has a more specialised method """ digest!(context) Finalize the SHA context and return the hash as array of bytes (Array{Uint8, 1}). Updating the context after calling `digest!` on it will error. # Examples ```julia-repl julia> ctx = SHA1_CTX() SHA1 hash state julia> update!(ctx, b"data to to be hashed") julia> digest!(ctx) 20-element Array{UInt8,1}: 0x83 0xe4 โ‹ฎ 0x89 0xf5 julia> update!(ctx, b"more data") ERROR: Cannot update CTX after `digest!` has been called on it [...] ``` """ function digest!(context::T) where T<:SHA_CTX if !context.used pad_remainder!(context) # Store the length of the input data (in bits) at the end of the padding bitcount_idx = div(short_blocklen(T), sizeof(context.bytecount)) + 1 pbuf = Ptr{typeof(context.bytecount)}(pointer(context.buffer)) unsafe_store!(pbuf, bswap(context.bytecount * 8), bitcount_idx) # Final transform: transform!(context) bswap!(context.state) context.used = true end # Return the digest return reinterpret(UInt8, context.state)[1:digestlen(T)] end k/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/SHA/src/hmac.jl}""" HMAC_CTX(ctx::CTX, key::Vector{UInt8}) where {CTX<:SHA_CTX} Construct an empty HMAC_CTX context. """ struct HMAC_CTX{CTX<:SHA_CTX} context::CTX outer::Vector{UInt8} function HMAC_CTX(ctx::CTX, key::Vector{UInt8}, blocksize::Integer=blocklen(CTX)) where CTX if length(key) > blocksize _ctx = CTX() update!(_ctx, key) key = digest!(_ctx) end pad = blocksize - length(key) if pad > 0 key = [key; fill(0x00, pad)] end update!(ctx, key .โŠป 0x36) new{CTX}(ctx, key .โŠป 0x5c) end end function update!(ctx::HMAC_CTX, data, datalen=length(data)) update!(ctx.context, data, datalen) end function digest!(ctx::HMAC_CTX{CTX}) where CTX digest = digest!(ctx.context) _ctx = CTX() update!(_ctx, ctx.outer) update!(_ctx, digest) digest!(_ctx) end ~/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Serialization/src/Serialization.jlPศ# This file is a part of Julia. License is MIT: https://julialang.org/license """ Provide serialization of Julia objects via the functions * [`serialize`](@ref) * [`deserialize`](@ref) """ module Serialization import Base: GMP, Bottom, unsafe_convert, uncompressed_ast import Core: svec, SimpleVector using Base: unaliascopy, unwrap_unionall, require_one_based_indexing, ntupleany using Core.IR export serialize, deserialize, AbstractSerializer, Serializer abstract type AbstractSerializer end mutable struct Serializer{I<:IO} <: AbstractSerializer io::I counter::Int table::IdDict{Any,Any} pending_refs::Vector{Int} known_object_data::Dict{UInt64,Any} version::Int Serializer{I}(io::I) where I<:IO = new(io, 0, IdDict(), Int[], Dict{UInt64,Any}(), ser_version) end Serializer(io::IO) = Serializer{typeof(io)}(io) ## serializing values ## const n_int_literals = 33 const n_reserved_slots = 24 const n_reserved_tags = 8 const TAGS = Any[ Symbol, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float16, Float32, Float64, Char, DataType, Union, UnionAll, Core.TypeName, Tuple, Array, Expr, LineNumberNode, :__LabelNode__, GotoNode, QuoteNode, CodeInfo, TypeVar, Core.Box, Core.MethodInstance, Module, Task, String, SimpleVector, Method, GlobalRef, SlotNumber, Const, NewvarNode, SSAValue, # dummy entries for tags that don't correspond directly to types Symbol, # UNDEFREF_TAG Symbol, # BACKREF_TAG Symbol, # LONGBACKREF_TAG Symbol, # SHORTBACKREF_TAG Symbol, # LONGTUPLE_TAG Symbol, # LONGSYMBOL_TAG Symbol, # LONGEXPR_TAG Symbol, # LONGSTRING_TAG Symbol, # SHORTINT64_TAG Symbol, # FULL_DATATYPE_TAG Symbol, # WRAPPER_DATATYPE_TAG Symbol, # OBJECT_TAG Symbol, # REF_OBJECT_TAG Symbol, # FULL_GLOBALREF_TAG Symbol, # HEADER_TAG Symbol, # IDDICT_TAG Symbol, # SHARED_REF_TAG ReturnNode, GotoIfNot, fill(Symbol, n_reserved_tags)..., (), Bool, Any, Bottom, Core.TypeofBottom, Type, svec(), Tuple{}, false, true, nothing, :Any, :Array, :TypeVar, :Box, :Tuple, :Ptr, :return, :call, Symbol("::"), :Function, :(=), :(==), :(===), :gotoifnot, :A, :B, :C, :M, :N, :T, :S, :X, :Y, :a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z, :add_int, :sub_int, :mul_int, :add_float, :sub_float, :new, :mul_float, :bitcast, :start, :done, :next, :indexed_iterate, :getfield, :meta, :eq_int, :slt_int, :sle_int, :ne_int, :push_loc, :pop_loc, :pop, :arrayset, :arrayref, :apply_type, :inbounds, :getindex, :setindex!, :Core, :!, :+, :Base, :static_parameter, :convert, :colon, Symbol("#self#"), Symbol("#temp#"), :tuple, Symbol(""), fill(:_reserved_, n_reserved_slots)..., (Int32(0):Int32(n_int_literals-1))..., (Int64(0):Int64(n_int_literals-1))... ] const NTAGS = length(TAGS) @assert NTAGS == 255 const ser_version = 24 # do not make changes without bumping the version #! format_version(::AbstractSerializer) = ser_version format_version(s::Serializer) = s.version function sertag(@nospecialize(v)) # NOTE: we use jl_value_ptr directly since we know at least one of the arguments # in the comparison below is a singleton. ptr = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), v) ptags = convert(Ptr{Ptr{Cvoid}}, pointer(TAGS)) # note: constant ints & reserved slots never returned here @inbounds for i in 1:(NTAGS-(n_reserved_slots+2*n_int_literals)) ptr == unsafe_load(ptags,i) && return i%Int32 end return Int32(-1) end desertag(i::Int32) = @inbounds(TAGS[i]) # tags >= this just represent themselves, their whole representation is 1 byte const VALUE_TAGS = sertag(()) const ZERO32_TAG = Int32(NTAGS-(2*n_int_literals-1)) const ZERO64_TAG = Int64(NTAGS-(n_int_literals-1)) const TRUE_TAG = sertag(true) const FALSE_TAG = sertag(false) const EMPTYTUPLE_TAG = sertag(()) const TUPLE_TAG = sertag(Tuple) const SIMPLEVECTOR_TAG = sertag(SimpleVector) const SYMBOL_TAG = sertag(Symbol) const INT8_TAG = sertag(Int8) const ARRAY_TAG = findfirst(==(Array), TAGS)%Int32 const EXPR_TAG = sertag(Expr) const MODULE_TAG = sertag(Module) const METHODINSTANCE_TAG = sertag(Core.MethodInstance) const METHOD_TAG = sertag(Method) const TASK_TAG = sertag(Task) const DATATYPE_TAG = sertag(DataType) const TYPENAME_TAG = sertag(Core.TypeName) const INT32_TAG = sertag(Int32) const INT64_TAG = sertag(Int64) const GLOBALREF_TAG = sertag(GlobalRef) const BOTTOM_TAG = sertag(Bottom) const UNIONALL_TAG = sertag(UnionAll) const STRING_TAG = sertag(String) const o0 = sertag(SSAValue) const UNDEFREF_TAG = Int32(o0+1) const BACKREF_TAG = Int32(o0+2) const LONGBACKREF_TAG = Int32(o0+3) const SHORTBACKREF_TAG = Int32(o0+4) const LONGTUPLE_TAG = Int32(o0+5) const LONGSYMBOL_TAG = Int32(o0+6) const LONGEXPR_TAG = Int32(o0+7) const LONGSTRING_TAG = Int32(o0+8) const SHORTINT64_TAG = Int32(o0+9) const FULL_DATATYPE_TAG = Int32(o0+10) const WRAPPER_DATATYPE_TAG = Int32(o0+11) const OBJECT_TAG = Int32(o0+12) const REF_OBJECT_TAG = Int32(o0+13) const FULL_GLOBALREF_TAG = Int32(o0+14) const HEADER_TAG = Int32(o0+15) const IDDICT_TAG = Int32(o0+16) const SHARED_REF_TAG = Int32(o0+17) writetag(s::IO, tag) = (write(s, UInt8(tag)); nothing) function write_as_tag(s::IO, tag) tag < VALUE_TAGS && write(s, UInt8(0)) write(s, UInt8(tag)) nothing end # cycle handling function serialize_cycle(s::AbstractSerializer, @nospecialize(x)) offs = get(s.table, x, -1)::Int if offs != -1 if offs <= typemax(UInt16) writetag(s.io, SHORTBACKREF_TAG) write(s.io, UInt16(offs)) elseif offs <= typemax(Int32) writetag(s.io, BACKREF_TAG) write(s.io, Int32(offs)) else writetag(s.io, LONGBACKREF_TAG) write(s.io, Int64(offs)) end return true end s.table[x] = s.counter s.counter += 1 return false end function serialize_cycle_header(s::AbstractSerializer, @nospecialize(x)) serialize_cycle(s, x) && return true serialize_type(s, typeof(x), true) return false end function reset_state(s::AbstractSerializer) s.counter = 0 empty!(s.table) empty!(s.pending_refs) s end serialize(s::AbstractSerializer, x::Bool) = x ? writetag(s.io, TRUE_TAG) : writetag(s.io, FALSE_TAG) serialize(s::AbstractSerializer, p::Ptr) = serialize_any(s, oftype(p, C_NULL)) serialize(s::AbstractSerializer, ::Tuple{}) = writetag(s.io, EMPTYTUPLE_TAG) function serialize(s::AbstractSerializer, t::Tuple) l = length(t) if l <= NTAGS writetag(s.io, TUPLE_TAG) write(s.io, UInt8(l)) else writetag(s.io, LONGTUPLE_TAG) write(s.io, Int32(l)) end for x in t serialize(s, x) end end function serialize(s::AbstractSerializer, v::SimpleVector) writetag(s.io, SIMPLEVECTOR_TAG) write(s.io, Int32(length(v))) for x in v serialize(s, x) end end function serialize(s::AbstractSerializer, x::Symbol) tag = sertag(x) if tag > 0 return write_as_tag(s.io, tag) end pname = unsafe_convert(Ptr{UInt8}, x) len = Int(ccall(:strlen, Csize_t, (Cstring,), pname)) if len > 7 serialize_cycle(s, x) && return end if len <= NTAGS writetag(s.io, SYMBOL_TAG) write(s.io, UInt8(len)) else writetag(s.io, LONGSYMBOL_TAG) write(s.io, Int32(len)) end unsafe_write(s.io, pname, len) nothing end function serialize_array_data(s::IO, a) require_one_based_indexing(a) isempty(a) && return 0 if eltype(a) === Bool last = a[1]::Bool count = 1 for i = 2:length(a) if a[i]::Bool != last || count == 127 write(s, UInt8((UInt8(last) << 7) | count)) last = a[i]::Bool count = 1 else count += 1 end end write(s, UInt8((UInt8(last) << 7) | count)) else write(s, a) end end function serialize(s::AbstractSerializer, a::Array) serialize_cycle(s, a) && return elty = eltype(a) writetag(s.io, ARRAY_TAG) if elty !== UInt8 serialize(s, elty) end if ndims(a) != 1 serialize(s, size(a)) else serialize(s, length(a)) end if isbitstype(elty) serialize_array_data(s.io, a) else sizehint!(s.table, div(length(a),4)) # prepare for lots of pointers @inbounds for i in eachindex(a) if isassigned(a, i) serialize(s, a[i]) else writetag(s.io, UNDEFREF_TAG) end end end end function serialize(s::AbstractSerializer, a::SubArray{T,N,A}) where {T,N,A<:Array} # SubArray's copy only selects the relevant data (and reduces the size) but does not # preserve the type of the argument. This internal function does both: b = unaliascopy(a) serialize_any(s, b) end function serialize(s::AbstractSerializer, ss::String) len = sizeof(ss) if len > 7 serialize_cycle(s, ss) && return writetag(s.io, SHARED_REF_TAG) end if len <= NTAGS writetag(s.io, STRING_TAG) write(s.io, UInt8(len)) else writetag(s.io, LONGSTRING_TAG) write(s.io, Int64(len)) end write(s.io, ss) nothing end function serialize(s::AbstractSerializer, ss::SubString{String}) # avoid saving a copy of the parent string, keeping the type of ss serialize_any(s, SubString(String(ss))) end # Don't serialize the pointers function serialize(s::AbstractSerializer, r::Regex) serialize_type(s, typeof(r)) serialize(s, r.pattern) serialize(s, r.compile_options) serialize(s, r.match_options) end function serialize(s::AbstractSerializer, n::BigInt) serialize_type(s, BigInt) serialize(s, string(n, base = 62)) end function serialize(s::AbstractSerializer, ex::Expr) serialize_cycle(s, ex) && return l = length(ex.args) if l <= NTAGS writetag(s.io, EXPR_TAG) write(s.io, UInt8(l)) else writetag(s.io, LONGEXPR_TAG) write(s.io, Int32(l)) end serialize(s, ex.head) for a in ex.args serialize(s, a) end end function serialize_dict_data(s::AbstractSerializer, d::AbstractDict) write(s.io, Int32(length(d))) for (k,v) in d serialize(s, k) serialize(s, v) end end function serialize(s::AbstractSerializer, d::Dict) serialize_cycle_header(s, d) && return serialize_dict_data(s, d) end function serialize(s::AbstractSerializer, d::IdDict) serialize_cycle(s, d) && return writetag(s.io, IDDICT_TAG) serialize_type_data(s, typeof(d)) serialize_dict_data(s, d) end function serialize_mod_names(s::AbstractSerializer, m::Module) p = parentmodule(m) if p === m || m === Base key = Base.root_module_key(m) uuid = key.uuid serialize(s, uuid === nothing ? nothing : uuid.value) serialize(s, Symbol(key.name)) else serialize_mod_names(s, p) serialize(s, nameof(m)) end end function serialize(s::AbstractSerializer, m::Module) writetag(s.io, MODULE_TAG) serialize_mod_names(s, m) writetag(s.io, EMPTYTUPLE_TAG) end # TODO: make this bidirectional, so objects can be sent back via the same key const object_numbers = WeakKeyDict() const obj_number_salt = Ref{UInt64}(0) function object_number(s::AbstractSerializer, @nospecialize(l)) global obj_number_salt, object_numbers if haskey(object_numbers, l) return object_numbers[l] end ln = obj_number_salt[] object_numbers[l] = ln obj_number_salt[] += 1 return ln::UInt64 end lookup_object_number(s::AbstractSerializer, n::UInt64) = nothing remember_object(s::AbstractSerializer, @nospecialize(o), n::UInt64) = nothing function lookup_object_number(s::Serializer, n::UInt64) return get(s.known_object_data, n, nothing) end function remember_object(s::Serializer, @nospecialize(o), n::UInt64) s.known_object_data[n] = o return nothing end function serialize(s::AbstractSerializer, meth::Method) serialize_cycle(s, meth) && return writetag(s.io, METHOD_TAG) write(s.io, object_number(s, meth)) serialize(s, meth.module) serialize(s, meth.name) serialize(s, meth.file) serialize(s, meth.line) serialize(s, meth.sig) serialize(s, meth.slot_syms) serialize(s, meth.nargs) serialize(s, meth.isva) serialize(s, meth.is_for_opaque_closure) serialize(s, meth.nospecializeinfer) serialize(s, meth.constprop) serialize(s, meth.purity) if isdefined(meth, :source) serialize(s, Base._uncompressed_ast(meth, meth.source)) else serialize(s, nothing) end if isdefined(meth, :generator) serialize(s, meth.generator) else serialize(s, nothing) end if isdefined(meth, :recursion_relation) serialize(s, method.recursion_relation) else serialize(s, nothing) end if isdefined(meth, :external_mt) error("cannot serialize Method objects with external method tables") end nothing end function serialize(s::AbstractSerializer, linfo::Core.MethodInstance) serialize_cycle(s, linfo) && return writetag(s.io, METHODINSTANCE_TAG) if isdefined(linfo, :uninferred) serialize(s, linfo.uninferred) else writetag(s.io, UNDEFREF_TAG) end serialize(s, nothing) # for backwards compat serialize(s, linfo.sparam_vals) serialize(s, Any) # for backwards compat serialize(s, linfo.specTypes) serialize(s, linfo.def) nothing end function serialize(s::AbstractSerializer, t::Task) serialize_cycle(s, t) && return if istaskstarted(t) && !istaskdone(t) error("cannot serialize a running Task") end writetag(s.io, TASK_TAG) serialize(s, t.code) serialize(s, t.storage) serialize(s, t.state) if t._isexception && (stk = Base.current_exceptions(t); !isempty(stk)) # the exception stack field is hidden inside the task, so if there # is any information there make a CapturedException from it instead. # TODO: Handle full exception chain, not just the first one. serialize(s, CapturedException(stk[1].exception, stk[1].backtrace)) else serialize(s, t.result) end serialize(s, t._isexception) end function serialize(s::AbstractSerializer, g::GlobalRef) if (g.mod === __deserialized_types__ ) || (g.mod === Main && isdefined(g.mod, g.name) && isconst(g.mod, g.name)) v = getglobal(g.mod, g.name) unw = unwrap_unionall(v) if isa(unw,DataType) && v === unw.name.wrapper && should_send_whole_type(s, unw) # handle references to types in Main by sending the whole type. # needed to be able to send nested functions (#15451). writetag(s.io, FULL_GLOBALREF_TAG) serialize(s, v) return end end writetag(s.io, GLOBALREF_TAG) serialize(s, g.mod) serialize(s, g.name) end function serialize(s::AbstractSerializer, t::Core.TypeName) serialize_cycle(s, t) && return writetag(s.io, TYPENAME_TAG) write(s.io, object_number(s, t)) serialize_typename(s, t) end function serialize_typename(s::AbstractSerializer, t::Core.TypeName) serialize(s, t.name) serialize(s, t.names) primary = unwrap_unionall(t.wrapper) serialize(s, primary.super) serialize(s, primary.parameters) serialize(s, primary.types) serialize(s, isdefined(primary, :instance)) serialize(s, t.flags & 0x1 == 0x1) # .abstract serialize(s, t.flags & 0x2 == 0x2) # .mutable serialize(s, Int32(length(primary.types) - t.n_uninitialized)) serialize(s, t.max_methods) if isdefined(t, :mt) && t.mt !== Symbol.name.mt serialize(s, t.mt.name) serialize(s, collect(Base.MethodList(t.mt))) serialize(s, t.mt.max_args) kws = collect(methods(Core.kwcall, (Any, t.wrapper, Vararg))) if isempty(kws) writetag(s.io, UNDEFREF_TAG) else serialize(s, kws) end else writetag(s.io, UNDEFREF_TAG) end nothing end # decide whether to send all data for a type (instead of just its name) function should_send_whole_type(s, t::DataType) tn = t.name if isdefined(tn, :mt) # TODO improve somehow # send whole type for anonymous functions in Main name = tn.mt.name mod = tn.module isanonfunction = mod === Main && # only Main t.super === Function && # only Functions unsafe_load(unsafe_convert(Ptr{UInt8}, tn.name)) == UInt8('#') && # hidden type (!isdefined(mod, name) || t != typeof(getglobal(mod, name))) # XXX: 95% accurate test for this being an inner function # TODO: more accurate test? (tn.name !== "#" name) #TODO: iskw = startswith(tn.name, "#kw#") && ??? #TODO: iskw && return send-as-kwftype return mod === __deserialized_types__ || isanonfunction end return false end function serialize_type_data(s, @nospecialize(t::DataType)) whole = should_send_whole_type(s, t) iswrapper = (t === unwrap_unionall(t.name.wrapper)) if whole && iswrapper writetag(s.io, WRAPPER_DATATYPE_TAG) serialize(s, t.name) return end serialize_cycle(s, t) && return if whole writetag(s.io, FULL_DATATYPE_TAG) serialize(s, t.name) else writetag(s.io, DATATYPE_TAG) serialize(s, nameof(t)) serialize(s, parentmodule(t)) end if !isempty(t.parameters) if iswrapper write(s.io, Int32(0)) else write(s.io, Int32(length(t.parameters))) for p in t.parameters serialize(s, p) end end end nothing end function serialize(s::AbstractSerializer, t::DataType) tag = sertag(t) tag > 0 && return write_as_tag(s.io, tag) if t === Tuple # `sertag` is not able to find types === to `Tuple` because they # will not have been hash-consed. Plus `serialize_type_data` does not # handle this case correctly, since Tuple{} != Tuple. `Tuple` is the # only type with this property. issue #15849 return write_as_tag(s.io, TUPLE_TAG) end serialize_type_data(s, t) end function serialize_type(s::AbstractSerializer, @nospecialize(t::DataType), ref::Bool = false) tag = sertag(t) tag > 0 && return writetag(s.io, tag) writetag(s.io, ref ? REF_OBJECT_TAG : OBJECT_TAG) serialize_type_data(s, t) end function serialize(s::AbstractSerializer, n::Int32) if 0 <= n <= (n_int_literals-1) write(s.io, UInt8(ZERO32_TAG+n)) else writetag(s.io, INT32_TAG) write(s.io, n) end nothing end function serialize(s::AbstractSerializer, n::Int64) if 0 <= n <= (n_int_literals-1) write(s.io, UInt8(ZERO64_TAG+n)) elseif typemin(Int32) <= n <= typemax(Int32) writetag(s.io, SHORTINT64_TAG) write(s.io, Int32(n)) else writetag(s.io, INT64_TAG) write(s.io, n) end nothing end for i in 0:13 tag = Int32(INT8_TAG + i) ty = TAGS[tag] (ty === Int32 || ty === Int64) && continue @eval serialize(s::AbstractSerializer, n::$ty) = (writetag(s.io, $tag); write(s.io, n); nothing) end serialize(s::AbstractSerializer, ::Type{Bottom}) = write_as_tag(s.io, BOTTOM_TAG) function serialize(s::AbstractSerializer, u::UnionAll) writetag(s.io, UNIONALL_TAG) n = 0; t = u while isa(t, UnionAll) t = t.body n += 1 end if isa(t, DataType) && t === unwrap_unionall(t.name.wrapper) write(s.io, UInt8(1)) write(s.io, Int16(n)) serialize(s, t) else write(s.io, UInt8(0)) serialize(s, u.var) serialize(s, u.body) end end serialize(s::AbstractSerializer, @nospecialize(x)) = serialize_any(s, x) function serialize_any(s::AbstractSerializer, @nospecialize(x)) tag = sertag(x) if tag > 0 return write_as_tag(s.io, tag) end t = typeof(x)::DataType if isprimitivetype(t) serialize_type(s, t) write(s.io, x) else if ismutable(x) serialize_cycle(s, x) && return serialize_type(s, t, true) else serialize_type(s, t, false) end nf = nfields(x) for i in 1:nf if isdefined(x, i) serialize(s, getfield(x, i)) else writetag(s.io, UNDEFREF_TAG) end end end nothing end """ Serialization.writeheader(s::AbstractSerializer) Write an identifying header to the specified serializer. The header consists of 8 bytes as follows: | Offset | Description | |:-------|:------------------------------------------------| | 0 | tag byte (0x37) | | 1-2 | signature bytes "JL" | | 3 | protocol version | | 4 | bits 0-1: endianness: 0 = little, 1 = big | | 4 | bits 2-3: platform: 0 = 32-bit, 1 = 64-bit | | 5-7 | reserved | """ function writeheader(s::AbstractSerializer) io = s.io writetag(io, HEADER_TAG) write(io, "JL") # magic bytes write(io, UInt8(ser_version)) endianness = (ENDIAN_BOM == 0x04030201 ? 0 : ENDIAN_BOM == 0x01020304 ? 1 : error("unsupported endianness in serializer")) machine = (sizeof(Int) == 4 ? 0 : sizeof(Int) == 8 ? 1 : error("unsupported word size in serializer")) write(io, UInt8(endianness) | (UInt8(machine) << 2)) write(io, [0x00,0x00,0x00]) # 3 reserved bytes nothing end function readheader(s::AbstractSerializer) # Tag already read io = s.io m1 = read(io, UInt8) m2 = read(io, UInt8) if m1 != UInt8('J') || m2 != UInt8('L') error("Unsupported serialization format (got header magic bytes $m1 $m2)") end version = read(io, UInt8) flags = read(io, UInt8) reserved1 = read(io, UInt8) reserved2 = read(io, UInt8) reserved3 = read(io, UInt8) endianflag = flags & 0x3 wordflag = (flags >> 2) & 0x3 wordsize = wordflag == 0 ? 4 : wordflag == 1 ? 8 : error("Unknown word size flag in header") endian_bom = endianflag == 0 ? 0x04030201 : endianflag == 1 ? 0x01020304 : error("Unknown endianness flag in header") # Check protocol compatibility. endian_bom == ENDIAN_BOM || error("Serialized byte order mismatch ($(repr(endian_bom)))") # We don't check wordsize == sizeof(Int) here, as Int is encoded concretely # as Int32 or Int64, which should be enough to correctly deserialize a range # of data structures between Julia versions. if version > ser_version error("""Cannot read stream serialized with a newer version of Julia. Got data version $version > current version $ser_version""") end s.version = version return end """ serialize(stream::IO, value) Write an arbitrary value to a stream in an opaque format, such that it can be read back by [`deserialize`](@ref). The read-back value will be as identical as possible to the original, but note that `Ptr` values are serialized as all-zero bit patterns (`NULL`). An 8-byte identifying header is written to the stream first. To avoid writing the header, construct a `Serializer` and use it as the first argument to `serialize` instead. See also [`Serialization.writeheader`](@ref). The data format can change in minor (1.x) Julia releases, but files written by prior 1.x versions will remain readable. The main exception to this is when the definition of a type in an external package changes. If that occurs, it may be necessary to specify an explicit compatible version of the affected package in your environment. Renaming functions, even private functions, inside packages can also put existing files out of sync. Anonymous functions require special care: because their names are automatically generated, minor code changes can cause them to be renamed. Serializing anonymous functions should be avoided in files intended for long-term storage. In some cases, the word size (32- or 64-bit) of the reading and writing machines must match. In rarer cases the OS or architecture must also match, for example when using packages that contain platform-dependent code. """ function serialize(s::IO, x) ss = Serializer(s) writeheader(ss) serialize(ss, x) end """ serialize(filename::AbstractString, value) Open a file and serialize the given value to it. !!! compat "Julia 1.1" This method is available as of Julia 1.1. """ serialize(filename::AbstractString, x) = open(io->serialize(io, x), filename, "w") ## deserializing values ## """ deserialize(stream) Read a value written by [`serialize`](@ref). `deserialize` assumes the binary data read from `stream` is correct and has been serialized by a compatible implementation of [`serialize`](@ref). `deserialize` is designed for simplicity and performance, and so does not validate the data read. Malformed data can result in process termination. The caller must ensure the integrity and correctness of data read from `stream`. """ deserialize(s::IO) = deserialize(Serializer(s)) """ deserialize(filename::AbstractString) Open a file and deserialize its contents. !!! compat "Julia 1.1" This method is available as of Julia 1.1. """ deserialize(filename::AbstractString) = open(deserialize, filename) function deserialize(s::AbstractSerializer) handle_deserialize(s, Int32(read(s.io, UInt8)::UInt8)) end function deserialize_cycle(s::AbstractSerializer, @nospecialize(x)) slot = pop!(s.pending_refs) s.table[slot] = x nothing end # optimized version of: # slot = s.counter; s.counter += 1 # push!(s.pending_refs, slot) # slot = pop!(s.pending_refs) # s.table[slot] = x function resolve_ref_immediately(s::AbstractSerializer, @nospecialize(x)) s.table[s.counter] = x s.counter += 1 nothing end function gettable(s::AbstractSerializer, id::Int) get(s.table, id) do errmsg = """Inconsistent Serializer state when deserializing. Attempt to access internal table with key $id failed. This might occur if the Serializer contexts when serializing and deserializing are inconsistent. In particular, if multiple serialize calls use the same Serializer object then the corresponding deserialize calls should also use the same Serializer object. """ error(errmsg) end end # deserialize_ is an internal function to dispatch on the tag # describing the serialized representation. the number of # representations is fixed, so deserialize_ does not get extended. function handle_deserialize(s::AbstractSerializer, b::Int32) if b == 0 return desertag(Int32(read(s.io, UInt8)::UInt8)) end if b >= VALUE_TAGS return desertag(b) elseif b == TUPLE_TAG return deserialize_tuple(s, Int(read(s.io, UInt8)::UInt8)) elseif b == SHORTBACKREF_TAG id = read(s.io, UInt16)::UInt16 return gettable(s, Int(id)) elseif b == BACKREF_TAG id = read(s.io, Int32)::Int32 return gettable(s, Int(id)) elseif b == ARRAY_TAG return deserialize_array(s) elseif b == DATATYPE_TAG return deserialize_datatype(s, false) elseif b == FULL_DATATYPE_TAG return deserialize_datatype(s, true) elseif b == WRAPPER_DATATYPE_TAG tname = deserialize(s)::Core.TypeName return unwrap_unionall(tname.wrapper) elseif b == OBJECT_TAG t = deserialize(s) if t === Missing return missing end return deserialize(s, t) elseif b == REF_OBJECT_TAG slot = s.counter; s.counter += 1 push!(s.pending_refs, slot) t = deserialize(s) return deserialize(s, t) elseif b == SHARED_REF_TAG slot = s.counter; s.counter += 1 obj = deserialize(s) s.table[slot] = obj return obj elseif b == SYMBOL_TAG return deserialize_symbol(s, Int(read(s.io, UInt8)::UInt8)) elseif b == SHORTINT64_TAG return Int64(read(s.io, Int32)::Int32) elseif b == EXPR_TAG return deserialize_expr(s, Int(read(s.io, UInt8)::UInt8)) elseif b == MODULE_TAG return deserialize_module(s) elseif b == STRING_TAG return deserialize_string(s, Int(read(s.io, UInt8)::UInt8)) elseif b == LONGSTRING_TAG return deserialize_string(s, Int(read(s.io, Int64)::Int64)) elseif b == SIMPLEVECTOR_TAG return deserialize_svec(s) elseif b == GLOBALREF_TAG return GlobalRef(deserialize(s)::Module, deserialize(s)::Symbol) elseif b == FULL_GLOBALREF_TAG ty = deserialize(s) tn = unwrap_unionall(ty).name return GlobalRef(tn.module, tn.name) elseif b == LONGTUPLE_TAG return deserialize_tuple(s, Int(read(s.io, Int32)::Int32)) elseif b == LONGEXPR_TAG return deserialize_expr(s, Int(read(s.io, Int32)::Int32)) elseif b == LONGBACKREF_TAG id = read(s.io, Int64)::Int64 return gettable(s, Int(id)) elseif b == LONGSYMBOL_TAG return deserialize_symbol(s, Int(read(s.io, Int32)::Int32)) elseif b == HEADER_TAG readheader(s) return deserialize(s) elseif b == INT8_TAG return read(s.io, Int8) elseif b == INT8_TAG+1 return read(s.io, UInt8) elseif b == INT8_TAG+2 return read(s.io, Int16) elseif b == INT8_TAG+3 return read(s.io, UInt16) elseif b == INT32_TAG return read(s.io, Int32) elseif b == INT8_TAG+5 return read(s.io, UInt32) elseif b == INT64_TAG return read(s.io, Int64) elseif b == INT8_TAG+7 return read(s.io, UInt64) elseif b == INT8_TAG+8 return read(s.io, Int128) elseif b == INT8_TAG+9 return read(s.io, UInt128) elseif b == INT8_TAG+10 return read(s.io, Float16) elseif b == INT8_TAG+11 return read(s.io, Float32) elseif b == INT8_TAG+12 return read(s.io, Float64) elseif b == INT8_TAG+13 return read(s.io, Char) elseif b == IDDICT_TAG slot = s.counter; s.counter += 1 push!(s.pending_refs, slot) t = deserialize(s) return deserialize_dict(s, t) end t = desertag(b)::DataType if ismutabletype(t) && length(t.types) > 0 # manual specialization of fieldcount slot = s.counter; s.counter += 1 push!(s.pending_refs, slot) end return deserialize(s, t) end function deserialize_symbol(s::AbstractSerializer, len::Int) str = Base._string_n(len) unsafe_read(s.io, pointer(str), len) sym = Symbol(str) if len > 7 resolve_ref_immediately(s, sym) end return sym end deserialize_tuple(s::AbstractSerializer, len) = ntupleany(i->deserialize(s), len) function deserialize_svec(s::AbstractSerializer) n = read(s.io, Int32) svec(Any[ deserialize(s) for i=1:n ]...) end function deserialize_module(s::AbstractSerializer) mkey = deserialize(s) if isa(mkey, Tuple) # old version, TODO: remove if mkey === () return Main end m = Base.root_module(mkey[1]) for i = 2:length(mkey) m = getglobal(m, mkey[i])::Module end else name = String(deserialize(s)::Symbol) pkg = (mkey === nothing) ? Base.PkgId(name) : Base.PkgId(Base.UUID(mkey), name) m = Base.root_module(pkg) mname = deserialize(s) while mname !== () m = getglobal(m, mname)::Module mname = deserialize(s) end end return m end function deserialize(s::AbstractSerializer, ::Type{Method}) lnumber = read(s.io, UInt64) meth = lookup_object_number(s, lnumber) if meth !== nothing meth = meth::Method makenew = false else meth = ccall(:jl_new_method_uninit, Ref{Method}, (Any,), Main) makenew = true end deserialize_cycle(s, meth) mod = deserialize(s)::Module name = deserialize(s)::Symbol file = deserialize(s)::Symbol line = deserialize(s)::Int32 sig = deserialize(s)::Type syms = deserialize(s) if syms isa SimpleVector # < v1.2 _ambig = deserialize(s) else slot_syms = syms::String end nargs = deserialize(s)::Int32 isva = deserialize(s)::Bool is_for_opaque_closure = false nospecializeinfer = false constprop = purity = 0x00 template_or_is_opaque = deserialize(s) if isa(template_or_is_opaque, Bool) is_for_opaque_closure = template_or_is_opaque if format_version(s) >= 24 nospecializeinfer = deserialize(s)::Bool end if format_version(s) >= 14 constprop = deserialize(s)::UInt8 end if format_version(s) >= 17 purity = deserialize(s)::UInt8 end template = deserialize(s) else template = template_or_is_opaque end generator = deserialize(s) recursion_relation = nothing if format_version(s) >= 15 recursion_relation = deserialize(s) end if makenew meth.module = mod meth.name = name meth.file = file meth.line = line meth.sig = sig meth.nargs = nargs meth.isva = isva meth.is_for_opaque_closure = is_for_opaque_closure meth.nospecializeinfer = nospecializeinfer meth.constprop = constprop meth.purity = purity if template !== nothing # TODO: compress template meth.source = template::CodeInfo if !@isdefined(slot_syms) slot_syms = ccall(:jl_compress_argnames, Ref{String}, (Any,), meth.source.slotnames) end end meth.slot_syms = slot_syms if generator !== nothing meth.generator = generator end if recursion_relation !== nothing meth.recursion_relation = recursion_relation end if !is_for_opaque_closure mt = ccall(:jl_method_table_for, Any, (Any,), sig) if mt !== nothing && nothing === ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), mt, sig, typemax(UInt)) ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), mt, meth, C_NULL) end end remember_object(s, meth, lnumber) end return meth end function deserialize(s::AbstractSerializer, ::Type{Core.MethodInstance}) linfo = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, (Ptr{Cvoid},), C_NULL) deserialize_cycle(s, linfo) tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG setfield!(linfo, :uninferred, handle_deserialize(s, tag)::CodeInfo, :monotonic) end tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG # for reading files prior to v1.2 handle_deserialize(s, tag) end linfo.sparam_vals = deserialize(s)::SimpleVector _rettype = deserialize(s) # for backwards compat linfo.specTypes = deserialize(s) linfo.def = deserialize(s) return linfo end function deserialize(s::AbstractSerializer, ::Type{Core.LineInfoNode}) mod = deserialize(s) if mod isa Module method = deserialize(s) else # files post v1.2 and pre v1.6 are broken method = mod mod = Main end return Core.LineInfoNode(mod, method, deserialize(s)::Symbol, Int32(deserialize(s)::Union{Int32, Int}), Int32(deserialize(s)::Union{Int32, Int})) end function deserialize(s::AbstractSerializer, ::Type{PhiNode}) edges = deserialize(s) if edges isa Vector{Any} edges = Vector{Int32}(edges) end values = deserialize(s)::Vector{Any} return PhiNode(edges, values) end function deserialize(s::AbstractSerializer, ::Type{CodeInfo}) ci = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) deserialize_cycle(s, ci) code = deserialize(s)::Vector{Any} ci.code = code # allow older-style IR with return and gotoifnot Exprs for i in 1:length(code) stmt = code[i] if isa(stmt, Expr) ex = stmt::Expr if ex.head === :return code[i] = ReturnNode(isempty(ex.args) ? nothing : ex.args[1]) elseif ex.head === :gotoifnot code[i] = GotoIfNot(ex.args[1], ex.args[2]) end end end ci.codelocs = deserialize(s)::Vector{Int32} _x = deserialize(s) if _x isa Array || _x isa Int pre_12 = false ci.ssavaluetypes = _x else pre_12 = true # < v1.2 ci.method_for_inference_limit_heuristics = _x ci.ssavaluetypes = deserialize(s) ci.linetable = deserialize(s) end ssaflags = deserialize(s) if length(ssaflags) โ‰  length(code) # make sure the length of `ssaflags` matches that of `code` # so that the latest inference doesn't throw on IRs serialized from old versions ssaflags = UInt8[0x00 for _ in 1:length(code)] end ci.ssaflags = ssaflags if pre_12 ci.slotflags = deserialize(s) else ci.method_for_inference_limit_heuristics = deserialize(s) ci.linetable = deserialize(s) end ci.slotnames = deserialize(s) if !pre_12 ci.slotflags = deserialize(s) ci.slottypes = deserialize(s) ci.rettype = deserialize(s) ci.parent = deserialize(s) world_or_edges = deserialize(s) pre_13 = isa(world_or_edges, Integer) if pre_13 ci.min_world = world_or_edges else ci.edges = world_or_edges ci.min_world = reinterpret(UInt, deserialize(s)) ci.max_world = reinterpret(UInt, deserialize(s)) end end ci.inferred = deserialize(s) if format_version(s) < 22 inlining_cost = deserialize(s) if isa(inlining_cost, Bool) Core.Compiler.set_inlineable!(ci, inlining_cost) else ci.inlining_cost = inlining_cost end end ci.propagate_inbounds = deserialize(s) if format_version(s) < 23 deserialize(s) # `pure` field has been removed end if format_version(s) >= 20 ci.has_fcall = deserialize(s) end if format_version(s) >= 24 ci.nospecializeinfer = deserialize(s)::Bool end if format_version(s) >= 21 ci.inlining = deserialize(s)::UInt8 end if format_version(s) >= 14 ci.constprop = deserialize(s)::UInt8 end if format_version(s) >= 17 ci.purity = deserialize(s)::UInt8 end if format_version(s) >= 22 ci.inlining_cost = deserialize(s)::UInt16 end return ci end if Int === Int64 const OtherInt = Int32 else const OtherInt = Int64 end function deserialize_array(s::AbstractSerializer) slot = s.counter; s.counter += 1 d1 = deserialize(s) if isa(d1, Type) elty = d1 d1 = deserialize(s) else elty = UInt8 end if isa(d1, Int32) || isa(d1, Int64) if elty !== Bool && isbitstype(elty) a = Vector{elty}(undef, d1) s.table[slot] = a return read!(s.io, a) end dims = (Int(d1),) elseif d1 isa Dims dims = d1::Dims else dims = convert(Dims, d1::Tuple{Vararg{OtherInt}})::Dims end if isbitstype(elty) n = prod(dims)::Int if elty === Bool && n > 0 A = Array{Bool, length(dims)}(undef, dims) i = 1 while i <= n b = read(s.io, UInt8)::UInt8 v = (b >> 7) != 0 count = b & 0x7f nxt = i + count while i < nxt A[i] = v i += 1 end end else A = read!(s.io, Array{elty}(undef, dims)) end s.table[slot] = A return A end A = Array{elty, length(dims)}(undef, dims) s.table[slot] = A sizehint!(s.table, s.counter + div(length(A)::Int,4)) deserialize_fillarray!(A, s) return A end function deserialize_fillarray!(A::Array{T}, s::AbstractSerializer) where {T} for i = eachindex(A) tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG @inbounds A[i] = handle_deserialize(s, tag) end end return A end function deserialize_expr(s::AbstractSerializer, len) e = Expr(:temp) resolve_ref_immediately(s, e) e.head = deserialize(s)::Symbol e.args = Any[ deserialize(s) for i = 1:len ] e end module __deserialized_types__ end function deserialize(s::AbstractSerializer, ::Type{Core.TypeName}) number = read(s.io, UInt64) return deserialize_typename(s, number) end function deserialize_typename(s::AbstractSerializer, number) name = deserialize(s)::Symbol tn = lookup_object_number(s, number) if tn !== nothing makenew = false else # reuse the same name for the type, if possible, for nicer debugging tn_name = isdefined(__deserialized_types__, name) ? gensym() : name tn = ccall(:jl_new_typename_in, Any, (Any, Any, Cint, Cint), tn_name, __deserialized_types__, false, false) makenew = true end tn = tn::Core.TypeName remember_object(s, tn, number) deserialize_cycle(s, tn) names = deserialize(s)::SimpleVector super = deserialize(s)::Type parameters = deserialize(s)::SimpleVector types = deserialize(s)::SimpleVector attrs = Core.svec() has_instance = deserialize(s)::Bool abstr = deserialize(s)::Bool mutabl = deserialize(s)::Bool ninitialized = deserialize(s)::Int32 maxm = format_version(s) >= 18 ? deserialize(s)::UInt8 : UInt8(0) if makenew # TODO: there's an unhanded cycle in the dependency graph at this point: # while deserializing super and/or types, we may have encountered # tn.wrapper and throw UndefRefException before we get to this point ndt = ccall(:jl_new_datatype, Any, (Any, Any, Any, Any, Any, Any, Any, Cint, Cint, Cint), tn, tn.module, super, parameters, names, types, attrs, abstr, mutabl, ninitialized) @assert tn == ndt.name ccall(:jl_set_const, Cvoid, (Any, Any, Any), tn.module, tn.name, tn.wrapper) ty = tn.wrapper tn.max_methods = maxm if has_instance ty = ty::DataType if !Base.issingletontype(ty) singleton = ccall(:jl_new_struct, Any, (Any, Any...), ty) # use setfield! directly to avoid `fieldtype` lowering expecting to see a Singleton object already on ty ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), ty, Base.fieldindex(DataType, :instance)-1, singleton) end end end tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG mtname = handle_deserialize(s, tag) defs = deserialize(s) maxa = deserialize(s)::Int if makenew mt = ccall(:jl_new_method_table, Any, (Any, Any), name, tn.module) if !isempty(parameters) mt.offs = 0 end mt.name = mtname setfield!(mt, :max_args, maxa, :monotonic) ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), tn, Base.fieldindex(Core.TypeName, :mt)-1, mt) for def in defs if isdefined(def, :sig) ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), mt, def, C_NULL) end end end tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG kws = handle_deserialize(s, tag) if makenew if kws isa Vector{Method} for def in kws kwmt = typeof(Core.kwcall).name.mt ccall(:jl_method_table_insert, Cvoid, (Any, Any, Ptr{Cvoid}), mt, def, C_NULL) end else # old object format -- try to forward from old to new @eval Core.kwcall(kwargs::NamedTuple, f::$ty, args...) = $kws(kwargs, f, args...) end end end elseif makenew mt = Symbol.name.mt ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), tn, Base.fieldindex(Core.TypeName, :mt)-1, mt) end return tn end function deserialize_datatype(s::AbstractSerializer, full::Bool) slot = s.counter; s.counter += 1 if full tname = deserialize(s)::Core.TypeName ty = tname.wrapper else name = deserialize(s)::Symbol mod = deserialize(s)::Module ty = getglobal(mod, name) end if isa(ty,DataType) && isempty(ty.parameters) t = ty else np = Int(read(s.io, Int32)::Int32) if np == 0 t = unwrap_unionall(ty) elseif ty === Tuple # note np==0 has its own tag if np == 1 t = Tuple{deserialize(s)} elseif np == 2 t = Tuple{deserialize(s), deserialize(s)} elseif np == 3 t = Tuple{deserialize(s), deserialize(s), deserialize(s)} elseif np == 4 t = Tuple{deserialize(s), deserialize(s), deserialize(s), deserialize(s)} else t = Tuple{Any[ deserialize(s) for i=1:np ]...} end else t = ty for i = 1:np t = t{deserialize(s)} end end end s.table[slot] = t return t end function deserialize(s::AbstractSerializer, ::Type{UnionAll}) form = read(s.io, UInt8) if form == 0 var = deserialize(s) body = deserialize(s) return UnionAll(var, body) else n = read(s.io, Int16) t = deserialize(s)::DataType w = t.name.wrapper k = 0 while isa(w, UnionAll) w = w.body k += 1 end w = t.name.wrapper k -= n while k > 0 w = w.body k -= 1 end return w end end function deserialize(s::AbstractSerializer, ::Type{Task}) t = Task(()->nothing) deserialize_cycle(s, t) t.code = deserialize(s) t.storage = deserialize(s) state = deserialize(s) if state === :runnable t._state = Base.task_state_runnable elseif state === :done t._state = Base.task_state_done elseif state === :failed t._state = Base.task_state_failed else @assert false end t.result = deserialize(s) exc = deserialize(s) if exc === nothing t._isexception = false elseif exc isa Bool t._isexception = exc else t._isexception = true t.result = exc end t end function deserialize_string(s::AbstractSerializer, len::Int) out = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), len) unsafe_read(s.io, pointer(out), len) return out end # default DataType deserializer function deserialize(s::AbstractSerializer, t::DataType) nf = length(t.types) if isprimitivetype(t) return read(s.io, t) elseif ismutabletype(t) x = ccall(:jl_new_struct_uninit, Any, (Any,), t) deserialize_cycle(s, x) for i in 1:nf tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), x, i-1, handle_deserialize(s, tag)) end end return x elseif nf == 0 return ccall(:jl_new_struct_uninit, Any, (Any,), t) else na = nf vflds = Vector{Any}(undef, nf) for i in 1:nf tag = Int32(read(s.io, UInt8)::UInt8) if tag != UNDEFREF_TAG f = handle_deserialize(s, tag) na >= i && (vflds[i] = f) else na >= i && (na = i - 1) # rest of tail must be undefined values end end return ccall(:jl_new_structv, Any, (Any, Ptr{Any}, UInt32), t, vflds, na) end end function deserialize_dict(s::AbstractSerializer, T::Type{<:AbstractDict}) n = read(s.io, Int32) t = T(); sizehint!(t, n) deserialize_cycle(s, t) for i = 1:n k = deserialize(s) v = deserialize(s) t[k] = v end return t end function deserialize(s::AbstractSerializer, T::Type{Dict{K,V}}) where {K,V} return deserialize_dict(s, T) end deserialize(s::AbstractSerializer, ::Type{BigInt}) = parse(BigInt, deserialize(s), base = 62) function deserialize(s::AbstractSerializer, t::Type{Regex}) pattern = deserialize(s) compile_options = deserialize(s) match_options = deserialize(s) return Regex(pattern, compile_options, match_options) end ## StackTraces # provide a custom serializer that skips attempting to serialize the `outer_linfo` # which is likely to contain complex references, types, and module references # that may not exist on the receiver end function serialize(s::AbstractSerializer, frame::Base.StackTraces.StackFrame) serialize_type(s, typeof(frame)) serialize(s, frame.func) serialize(s, frame.file) write(s.io, frame.line) write(s.io, frame.from_c) write(s.io, frame.inlined) write(s.io, frame.pointer) nothing end function deserialize(s::AbstractSerializer, ::Type{Base.StackTraces.StackFrame}) func = deserialize(s) file = deserialize(s) line = read(s.io, Int) from_c = read(s.io, Bool) inlined = read(s.io, Bool) pointer = read(s.io, UInt64) return Base.StackTraces.StackFrame(func, file, line, nothing, from_c, inlined, pointer) end function serialize(s::AbstractSerializer, lock::Base.AbstractLock) # assert_havelock(lock) serialize_cycle_header(s, lock) nothing end function deserialize(s::AbstractSerializer, ::Type{T}) where T<:Base.AbstractLock lock = T() deserialize_cycle(s, lock) return lock end function serialize(s::AbstractSerializer, cond::Base.GenericCondition) serialize_cycle_header(s, cond) && return serialize(s, cond.lock) nothing end function deserialize(s::AbstractSerializer, ::Type{T}) where T<:Base.GenericCondition lock = deserialize(s) cond = T(lock) deserialize_cycle(s, cond) return cond end serialize(s::AbstractSerializer, l::LazyString) = invoke(serialize, Tuple{AbstractSerializer,Any}, s, Base._LazyString((), string(l))) end r/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Sockets/src/Sockets.jlƒp# This file is a part of Julia. License is MIT: https://julialang.org/license """ Support for sockets. Provides [`IPAddr`](@ref) and subtypes, [`TCPSocket`](@ref), and [`UDPSocket`](@ref). """ module Sockets export accept, bind, connect, getaddrinfo, getalladdrinfo, getnameinfo, getipaddr, getipaddrs, islinklocaladdr, getpeername, getsockname, listen, listenany, recv, recvfrom, send, join_multicast_group, leave_multicast_group, TCPSocket, UDPSocket, @ip_str, IPAddr, IPv4, IPv6 import Base: isless, show, print, parse, bind, convert, isreadable, iswritable, alloc_buf_hook, _uv_hook_close using Base: LibuvStream, LibuvServer, PipeEndpoint, @handle_as, uv_error, associate_julia_struct, uvfinalize, notify_error, uv_req_data, uv_req_set_data, preserve_handle, unpreserve_handle, _UVError, IOError, eventloop, StatusUninit, StatusInit, StatusConnecting, StatusOpen, StatusClosing, StatusClosed, StatusActive, preserve_handle, unpreserve_handle, iolock_begin, iolock_end, uv_status_string, check_open, OS_HANDLE, RawFD, UV_EINVAL, UV_ENOMEM, UV_ENOBUFS, UV_EAGAIN, UV_ECONNABORTED, UV_EADDRINUSE, UV_EACCES, UV_EADDRNOTAVAIL, UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS, UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL, UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME, UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE, UV_EAI_SOCKTYPE, UV_EAI_MEMORY include("IPAddr.jl") include("addrinfo.jl") """ TCPSocket(; delay=true) Open a TCP socket using libuv. If `delay` is true, libuv delays creation of the socket's file descriptor till the first [`bind`](@ref) call. `TCPSocket` has various fields to denote the state of the socket as well as its send/receive buffers. """ mutable struct TCPSocket <: LibuvStream handle::Ptr{Cvoid} status::Int buffer::IOBuffer cond::Base.ThreadSynchronizer readerror::Any sendbuf::Union{IOBuffer, Nothing} lock::ReentrantLock # advisory lock throttle::Int function TCPSocket(handle::Ptr{Cvoid}, status) tcp = new( handle, status, PipeBuffer(), Base.ThreadSynchronizer(), nothing, nothing, ReentrantLock(), Base.DEFAULT_READ_BUFFER_SZ) associate_julia_struct(tcp.handle, tcp) finalizer(uvfinalize, tcp) return tcp end end # kw arg "delay": if true, libuv delays creation of the socket fd till the first bind call function TCPSocket(; delay=true) tcp = TCPSocket(Libc.malloc(Base._sizeof_uv_tcp), StatusUninit) af_spec = delay ? 0 : 2 # AF_UNSPEC is 0, AF_INET is 2 iolock_begin() err = ccall(:uv_tcp_init_ex, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cuint), eventloop(), tcp.handle, af_spec) uv_error("failed to create tcp socket", err) tcp.status = StatusInit iolock_end() return tcp end function TCPSocket(fd::OS_HANDLE) tcp = TCPSocket() iolock_begin() err = ccall(:uv_tcp_open, Int32, (Ptr{Cvoid}, OS_HANDLE), tcp.handle, fd) uv_error("tcp_open", err) tcp.status = StatusOpen iolock_end() return tcp end if OS_HANDLE != RawFD TCPSocket(fd::RawFD) = TCPSocket(Libc._get_osfhandle(fd)) end mutable struct TCPServer <: LibuvServer handle::Ptr{Cvoid} status::Int cond::Base.ThreadSynchronizer function TCPServer(handle::Ptr{Cvoid}, status) tcp = new( handle, status, Base.ThreadSynchronizer()) associate_julia_struct(tcp.handle, tcp) finalizer(uvfinalize, tcp) return tcp end end # Keyword arg "delay": if true, libuv delays creation of socket fd till bind. # It can be set to false if there is a need to set socket options before # further calls to `bind` and `listen`, e.g. `SO_REUSEPORT`. function TCPServer(; delay=true) tcp = TCPServer(Libc.malloc(Base._sizeof_uv_tcp), StatusUninit) af_spec = delay ? 0 : 2 # AF_UNSPEC is 0, AF_INET is 2 iolock_begin() err = ccall(:uv_tcp_init_ex, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cuint), eventloop(), tcp.handle, af_spec) uv_error("failed to create tcp server", err) tcp.status = StatusInit iolock_end() return tcp end """ accept(server[, client]) Accepts a connection on the given server and returns a connection to the client. An uninitialized client stream may be provided, in which case it will be used instead of creating a new stream. """ accept(server::TCPServer) = accept(server, TCPSocket()) function accept(callback, server::LibuvServer) task = @async try while true client = accept(server) callback(client) end catch ex # accept below may explicitly throw UV_ECONNABORTED: # filter that out since we expect that error if !(ex isa IOError && ex.code == UV_ECONNABORTED) || isopen(server) rethrow() end end return task # caller is responsible for checking for errors end # UDP """ UDPSocket() Open a UDP socket using libuv. `UDPSocket` has various fields to denote the state of the socket. """ mutable struct UDPSocket <: LibuvStream handle::Ptr{Cvoid} status::Int recvnotify::Base.ThreadSynchronizer cond::Base.ThreadSynchronizer function UDPSocket(handle::Ptr{Cvoid}, status) cond = Base.ThreadSynchronizer() udp = new(handle, status, Base.ThreadSynchronizer(cond.lock), cond) associate_julia_struct(udp.handle, udp) finalizer(uvfinalize, udp) return udp end end function UDPSocket() this = UDPSocket(Libc.malloc(Base._sizeof_uv_udp), StatusUninit) iolock_begin() err = ccall(:uv_udp_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), this.handle) uv_error("failed to create udp socket", err) this.status = StatusInit iolock_end() return this end show(io::IO, stream::UDPSocket) = print(io, typeof(stream), "(", uv_status_string(stream), ")") function _uv_hook_close(sock::UDPSocket) lock(sock.cond) try sock.status = StatusClosed notify(sock.cond) notify_error(sock.recvnotify, EOFError()) finally unlock(sock.cond) end nothing end # Disables dual stack mode. const UV_TCP_IPV6ONLY = 1 # Disables dual stack mode. Only available when using ipv6 binf const UV_UDP_IPV6ONLY = 1 # Indicates message was truncated because read buffer was too small. The # remainder was discarded by the OS. const UV_UDP_PARTIAL = 2 # Indicates if SO_REUSEADDR will be set when binding the handle in uv_udp_bind. This sets # the SO_REUSEPORT socket flag on the BSDs and OS X. On other Unix platforms, it sets the # SO_REUSEADDR flag. What that means is that multiple threads or processes can bind to the # same address without error (provided they all set the flag) but only the last one to bind # will receive any traffic, in effect "stealing" the port from the previous listener. const UV_UDP_REUSEADDR = 4 ## function _bind(sock::Union{TCPServer, TCPSocket}, host::Union{IPv4, IPv6}, port::UInt16, flags::UInt32=UInt32(0)) host_in = Ref(hton(host.host)) return ccall(:jl_tcp_bind, Int32, (Ptr{Cvoid}, UInt16, Ptr{Cvoid}, Cuint, Cint), sock, hton(port), host_in, flags, host isa IPv6) end function _bind(sock::UDPSocket, host::Union{IPv4, IPv6}, port::UInt16, flags::UInt32=UInt32(0)) host_in = Ref(hton(host.host)) return ccall(:jl_udp_bind, Int32, (Ptr{Cvoid}, UInt16, Ptr{Cvoid}, Cuint, Cint), sock, hton(port), host_in, flags, host isa IPv6) end """ bind(socket::Union{TCPServer, UDPSocket, TCPSocket}, host::IPAddr, port::Integer; ipv6only=false, reuseaddr=false, kws...) Bind `socket` to the given `host:port`. Note that `0.0.0.0` will listen on all devices. * The `ipv6only` parameter disables dual stack mode. If `ipv6only=true`, only an IPv6 stack is created. * If `reuseaddr=true`, multiple threads or processes can bind to the same address without error if they all set `reuseaddr=true`, but only the last to bind will receive any traffic. """ function bind(sock::Union{TCPServer, UDPSocket, TCPSocket}, host::IPAddr, port::Integer; ipv6only = false, reuseaddr = false, kws...) if sock.status != StatusInit error("$(typeof(sock)) is not in initialization state") end flags = 0 if isa(host, IPv6) && ipv6only flags |= isa(sock, UDPSocket) ? UV_UDP_IPV6ONLY : UV_TCP_IPV6ONLY end if isa(sock, UDPSocket) && reuseaddr flags |= UV_UDP_REUSEADDR end iolock_begin() err = _bind(sock, host, UInt16(port), UInt32(flags)) if err < 0 iolock_end() if err != UV_EADDRINUSE && err != UV_EACCES && err != UV_EADDRNOTAVAIL #TODO: this codepath is not currently tested throw(_UVError("bind", err)) else return false end end if isa(sock, TCPServer) || isa(sock, UDPSocket) sock.status = StatusOpen end isa(sock, UDPSocket) && setopt(sock; kws...) iolock_end() return true end bind(sock::TCPServer, addr::InetAddr) = bind(sock, addr.host, addr.port) """ setopt(sock::UDPSocket; multicast_loop=nothing, multicast_ttl=nothing, enable_broadcast=nothing, ttl=nothing) Set UDP socket options. * `multicast_loop`: loopback for multicast packets (default: `true`). * `multicast_ttl`: TTL for multicast packets (default: `nothing`). * `enable_broadcast`: flag must be set to `true` if socket will be used for broadcast messages, or else the UDP system will return an access error (default: `false`). * `ttl`: Time-to-live of packets sent on the socket (default: `nothing`). """ function setopt(sock::UDPSocket; multicast_loop=nothing, multicast_ttl=nothing, enable_broadcast=nothing, ttl=nothing) iolock_begin() if sock.status == StatusUninit error("Cannot set options on uninitialized socket") end if multicast_loop !== nothing uv_error("multicast_loop", ccall(:uv_udp_set_multicast_loop, Cint, (Ptr{Cvoid}, Cint), sock.handle, multicast_loop) < 0) end if multicast_ttl !== nothing uv_error("multicast_ttl", ccall(:uv_udp_set_multicast_ttl, Cint, (Ptr{Cvoid}, Cint), sock.handle, multicast_ttl)) end if enable_broadcast !== nothing uv_error("enable_broadcast", ccall(:uv_udp_set_broadcast, Cint, (Ptr{Cvoid}, Cint), sock.handle, enable_broadcast)) end if ttl !== nothing uv_error("ttl", ccall(:uv_udp_set_ttl, Cint, (Ptr{Cvoid}, Cint), sock.handle, ttl)) end iolock_end() nothing end """ recv(socket::UDPSocket) Read a UDP packet from the specified socket, and return the bytes received. This call blocks. """ function recv(sock::UDPSocket) addr, data = recvfrom(sock) return data end function uv_recvcb end """ recvfrom(socket::UDPSocket) -> (host_port, data) Read a UDP packet from the specified socket, returning a tuple of `(host_port, data)`, where `host_port` will be an InetAddr{IPv4} or InetAddr{IPv6}, as appropriate. !!! compat "Julia 1.3" Prior to Julia version 1.3, the first returned value was an address (`IPAddr`). In version 1.3 it was changed to an `InetAddr`. """ function recvfrom(sock::UDPSocket) iolock_begin() # If the socket has not been bound, it will be bound implicitly to ::0 and a random port if sock.status != StatusInit && sock.status != StatusOpen && sock.status != StatusActive error("UDPSocket is not initialized and open") end if ccall(:uv_is_active, Cint, (Ptr{Cvoid},), sock.handle) == 0 err = ccall(:uv_udp_recv_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), sock, @cfunction(Base.uv_alloc_buf, Cvoid, (Ptr{Cvoid}, Csize_t, Ptr{Cvoid})), @cfunction(uv_recvcb, Cvoid, (Ptr{Cvoid}, Cssize_t, Ptr{Cvoid}, Ptr{Cvoid}, Cuint))) uv_error("recv_start", err) end sock.status = StatusActive lock(sock.recvnotify) iolock_end() try From = Union{InetAddr{IPv4}, InetAddr{IPv6}} Data = Vector{UInt8} from, data = wait(sock.recvnotify)::Tuple{From, Data} return (from, data) finally unlock(sock.recvnotify) end end alloc_buf_hook(sock::UDPSocket, size::UInt) = (Libc.malloc(size), Int(size)) # size is always 64k from libuv function uv_recvcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid}, addr::Ptr{Cvoid}, flags::Cuint) sock = @handle_as handle UDPSocket lock(sock.recvnotify) try buf_addr = ccall(:jl_uv_buf_base, Ptr{UInt8}, (Ptr{Cvoid},), buf) if nread == 0 && addr == C_NULL Libc.free(buf_addr) elseif nread < 0 Libc.free(buf_addr) notify_error(sock.recvnotify, _UVError("recv", nread)) elseif flags & UV_UDP_PARTIAL > 0 Libc.free(buf_addr) notify_error(sock.recvnotify, "Partial message received") else buf_size = Int(ccall(:jl_uv_buf_len, Csize_t, (Ptr{Cvoid},), buf)) if buf_size - nread < 16384 # waste at most 16k (note: buf_size is currently always 64k) buf = unsafe_wrap(Array, buf_addr, nread, own=true) else buf = Vector{UInt8}(undef, nread) GC.@preserve buf unsafe_copyto!(pointer(buf), buf_addr, nread) Libc.free(buf_addr) end # need to check the address type in order to convert to a Julia IPAddr host = IPv4(0) port = UInt16(0) if ccall(:jl_sockaddr_is_ip4, Cint, (Ptr{Cvoid},), addr) == 1 host = IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Cvoid},), addr))) port = ntoh(ccall(:jl_sockaddr_port4, UInt16, (Ptr{Cvoid},), addr)) elseif ccall(:jl_sockaddr_is_ip6, Cint, (Ptr{Cvoid},), addr) == 1 tmp = Ref{UInt128}(0) scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Cvoid}, Ptr{UInt128}), addr, tmp) host = IPv6(ntoh(tmp[])) port = ntoh(ccall(:jl_sockaddr_port6, UInt16, (Ptr{Cvoid},), addr)) end from = InetAddr(host, port) notify(sock.recvnotify, (from, buf), all=false) end if sock.status == StatusActive && isempty(sock.recvnotify) sock.status = StatusOpen ccall(:uv_udp_recv_stop, Cint, (Ptr{Cvoid},), sock) end finally unlock(sock.recvnotify) end nothing end function _send_async(sock::UDPSocket, ipaddr::Union{IPv4, IPv6}, port::UInt16, buf) req = Libc.malloc(Base._sizeof_uv_udp_send) uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call host_in = Ref(hton(ipaddr.host)) err = ccall(:jl_udp_send, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt16, Ptr{Cvoid}, Ptr{UInt8}, Csize_t, Ptr{Cvoid}, Cint), req, sock, hton(port), host_in, buf, sizeof(buf), @cfunction(Base.uv_writecb_task, Cvoid, (Ptr{Cvoid}, Cint)), ipaddr isa IPv6) if err < 0 Libc.free(req) uv_error("send", err) end return req end """ send(socket::UDPSocket, host::IPAddr, port::Integer, msg) Send `msg` over `socket` to `host:port`. """ function send(sock::UDPSocket, ipaddr::IPAddr, port::Integer, msg) # If the socket has not been bound, it will be bound implicitly to ::0 and a random port iolock_begin() if sock.status != StatusInit && sock.status != StatusOpen && sock.status != StatusActive error("UDPSocket is not initialized and open") end uvw = _send_async(sock, ipaddr, UInt16(port), msg) ct = current_task() preserve_handle(ct) Base.sigatomic_begin() uv_req_set_data(uvw, ct) iolock_end() status = try Base.sigatomic_end() wait()::Cint finally Base.sigatomic_end() iolock_begin() ct.queue === nothing || list_deletefirst!(ct.queue, ct) if uv_req_data(uvw) != C_NULL # uvw is still alive, # so make sure we won't get spurious notifications later uv_req_set_data(uvw, C_NULL) else # done with uvw Libc.free(uvw) end iolock_end() unpreserve_handle(ct) end uv_error("send", status) nothing end #from `connect` function uv_connectcb(conn::Ptr{Cvoid}, status::Cint) hand = ccall(:jl_uv_connect_handle, Ptr{Cvoid}, (Ptr{Cvoid},), conn) sock = @handle_as hand LibuvStream lock(sock.cond) try if status >= 0 # success if !(sock.status == StatusClosed || sock.status == StatusClosing) sock.status = StatusOpen end else sock.readerror = _UVError("connect", status) # TODO: perhaps we should not reuse readerror for this if !(sock.status == StatusClosed || sock.status == StatusClosing) ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), hand) sock.status = StatusClosing end end notify(sock.cond) finally unlock(sock.cond) end Libc.free(conn) nothing end function connect!(sock::TCPSocket, host::Union{IPv4, IPv6}, port::Integer) iolock_begin() if sock.status != StatusInit error("TCPSocket is not in initialization state") end if !(0 <= port <= typemax(UInt16)) throw(ArgumentError("port out of range, must be 0 โ‰ค port โ‰ค 65535, got $port")) end host_in = Ref(hton(host.host)) uv_error("connect", ccall(:jl_tcp_connect, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, UInt16, Ptr{Cvoid}, Cint), sock, host_in, hton(UInt16(port)), @cfunction(uv_connectcb, Cvoid, (Ptr{Cvoid}, Cint)), host isa IPv6)) sock.status = StatusConnecting iolock_end() nothing end connect!(sock::TCPSocket, addr::InetAddr) = connect!(sock, addr.host, addr.port) function wait_connected(x::LibuvStream) iolock_begin() check_open(x) isopen(x) || x.readerror === nothing || throw(x.readerror) preserve_handle(x) lock(x.cond) try while x.status == StatusConnecting iolock_end() wait(x.cond) unlock(x.cond) iolock_begin() lock(x.cond) end isopen(x) || x.readerror === nothing || throw(x.readerror) finally unlock(x.cond) unpreserve_handle(x) end iolock_end() nothing end # Default Host to localhost """ connect([host], port::Integer) -> TCPSocket Connect to the host `host` on port `port`. """ connect(sock::TCPSocket, port::Integer) = connect(sock, localhost, port) connect(port::Integer) = connect(localhost, port) # Valid connect signatures for TCP connect(host::AbstractString, port::Integer) = connect(TCPSocket(), host, port) connect(addr::IPAddr, port::Integer) = connect(TCPSocket(), addr, port) connect(addr::InetAddr) = connect(TCPSocket(), addr) function connect!(sock::TCPSocket, host::AbstractString, port::Integer) if sock.status != StatusInit error("TCPSocket is not in initialization state") end ipaddr = getaddrinfo(host) connect!(sock, ipaddr, port) return sock end function connect(sock::LibuvStream, args...) connect!(sock, args...) wait_connected(sock) return sock end """ nagle(socket::Union{TCPServer, TCPSocket}, enable::Bool) Enables or disables Nagle's algorithm on a given TCP server or socket. !!! compat "Julia 1.3" This function requires Julia 1.3 or later. """ function nagle(sock::Union{TCPServer, TCPSocket}, enable::Bool) # disable or enable Nagle's algorithm on all OSes iolock_begin() check_open(sock) err = ccall(:uv_tcp_nodelay, Cint, (Ptr{Cvoid}, Cint), sock.handle, Cint(!enable)) # TODO: check err iolock_end() return err end """ quickack(socket::Union{TCPServer, TCPSocket}, enable::Bool) On Linux systems, the TCP_QUICKACK is disabled or enabled on `socket`. """ function quickack(sock::Union{TCPServer, TCPSocket}, enable::Bool) iolock_begin() check_open(sock) @static if Sys.islinux() # tcp_quickack is a linux only option if ccall(:jl_tcp_quickack, Cint, (Ptr{Cvoid}, Cint), sock.handle, Cint(enable)) < 0 @warn "Networking unoptimized ( Error enabling TCP_QUICKACK : $(Libc.strerror(Libc.errno())) )" maxlog=1 end end iolock_end() nothing end ## const BACKLOG_DEFAULT = 511 """ listen([addr, ]port::Integer; backlog::Integer=BACKLOG_DEFAULT) -> TCPServer Listen on port on the address specified by `addr`. By default this listens on `localhost` only. To listen on all interfaces pass `IPv4(0)` or `IPv6(0)` as appropriate. `backlog` determines how many connections can be pending (not having called [`accept`](@ref)) before the server will begin to reject them. The default value of `backlog` is 511. """ function listen(addr; backlog::Integer=BACKLOG_DEFAULT) sock = TCPServer() bind(sock, addr) || error("cannot bind to port; may already be in use or access denied") listen(sock; backlog=backlog) return sock end listen(port::Integer; backlog::Integer=BACKLOG_DEFAULT) = listen(localhost, port; backlog=backlog) listen(host::IPAddr, port::Integer; backlog::Integer=BACKLOG_DEFAULT) = listen(InetAddr(host, port); backlog=backlog) function listen(sock::LibuvServer; backlog::Integer=BACKLOG_DEFAULT) uv_error("listen", trylisten(sock; backlog=backlog)) return sock end # from `listen` function uv_connectioncb(stream::Ptr{Cvoid}, status::Cint) sock = @handle_as stream LibuvServer lock(sock.cond) try if status >= 0 notify(sock.cond) else notify_error(sock.cond, _UVError("connection", status)) end finally unlock(sock.cond) end nothing end function trylisten(sock::LibuvServer; backlog::Integer=BACKLOG_DEFAULT) iolock_begin() check_open(sock) err = ccall(:uv_listen, Cint, (Ptr{Cvoid}, Cint, Ptr{Cvoid}), sock, backlog, @cfunction(uv_connectioncb, Cvoid, (Ptr{Cvoid}, Cint))) sock.status = StatusActive iolock_end() return err end ## function accept_nonblock(server::TCPServer, client::TCPSocket) iolock_begin() if client.status != StatusInit error("client TCPSocket is not in initialization state") end err = ccall(:uv_accept, Int32, (Ptr{Cvoid}, Ptr{Cvoid}), server.handle, client.handle) if err == 0 client.status = StatusOpen end iolock_end() return err end function accept_nonblock(server::TCPServer) client = TCPSocket() uv_error("accept", accept_nonblock(server, client)) return client end function accept(server::LibuvServer, client::LibuvStream) iolock_begin() if server.status != StatusActive && server.status != StatusClosing && server.status != StatusClosed throw(ArgumentError("server not connected, make sure \"listen\" has been called")) end while isopen(server) err = accept_nonblock(server, client) if err == 0 iolock_end() return client elseif err != UV_EAGAIN uv_error("accept", err) end preserve_handle(server) lock(server.cond) iolock_end() try wait(server.cond) finally unlock(server.cond) unpreserve_handle(server) end iolock_begin() end uv_error("accept", UV_ECONNABORTED) nothing end ## Utility functions const localhost = ip"127.0.0.1" """ listenany([host::IPAddr,] port_hint; backlog::Integer=BACKLOG_DEFAULT) -> (UInt16, TCPServer) Create a `TCPServer` on any port, using hint as a starting point. Returns a tuple of the actual port that the server was created on and the server itself. The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. """ function listenany(host::IPAddr, default_port; backlog::Integer=BACKLOG_DEFAULT) addr = InetAddr(host, default_port) while true sock = TCPServer() if bind(sock, addr) && trylisten(sock; backlog) == 0 if default_port == 0 _addr, port = getsockname(sock) return (port, sock) end return (addr.port, sock) end close(sock) addr = InetAddr(addr.host, addr.port + UInt16(1)) if addr.port == default_port error("no ports available") end end end listenany(default_port; backlog::Integer=BACKLOG_DEFAULT) = listenany(localhost, default_port; backlog) function udp_set_membership(sock::UDPSocket, group_addr::String, interface_addr::Union{Nothing, String}, operation) if interface_addr === nothing interface_addr = C_NULL end r = ccall(:uv_udp_set_membership, Cint, (Ptr{Cvoid}, Cstring, Cstring, Cint), sock.handle, group_addr, interface_addr, operation) uv_error("uv_udp_set_membership", r) return end """ join_multicast_group(sock::UDPSocket, group_addr, interface_addr = nothing) Join a socket to a particular multicast group defined by `group_addr`. If `interface_addr` is given, specifies a particular interface for multi-homed systems. Use `leave_multicast_group()` to disable reception of a group. """ function join_multicast_group(sock::UDPSocket, group_addr::String, interface_addr::Union{Nothing, String} = nothing) return udp_set_membership(sock, group_addr, interface_addr, 1) end function join_multicast_group(sock::UDPSocket, group_addr::IPAddr, interface_addr::Union{Nothing, IPAddr} = nothing) if interface_addr !== nothing interface_addr = string(interface_addr) end return join_multicast_group(sock, string(group_addr), interface_addr) end """ leave_multicast_group(sock::UDPSocket, group_addr, interface_addr = nothing) Remove a socket from a particular multicast group defined by `group_addr`. If `interface_addr` is given, specifies a particular interface for multi-homed systems. Use `join_multicast_group()` to enable reception of a group. """ function leave_multicast_group(sock::UDPSocket, group_addr::String, interface_addr::Union{Nothing, String} = nothing) return udp_set_membership(sock, group_addr, interface_addr, 0) end function leave_multicast_group(sock::UDPSocket, group_addr::IPAddr, interface_addr::Union{Nothing, IPAddr} = nothing) if interface_addr !== nothing interface_addr = string(interface_addr) end return leave_multicast_group(sock, string(group_addr), interface_addr) end """ getsockname(sock::Union{TCPServer, TCPSocket}) -> (IPAddr, UInt16) Get the IP address and port that the given socket is bound to. """ getsockname(sock::Union{TCPSocket, TCPServer}) = _sockname(sock, true) """ getpeername(sock::TCPSocket) -> (IPAddr, UInt16) Get the IP address and port of the remote endpoint that the given socket is connected to. Valid only for connected TCP sockets. """ getpeername(sock::TCPSocket) = _sockname(sock, false) function _sockname(sock, self=true) sock.status == StatusInit || check_open(sock) rport = Ref{Cushort}(0) raddress = zeros(UInt8, 16) rfamily = Ref{Cuint}(0) iolock_begin() if self r = ccall(:jl_tcp_getsockname, Int32, (Ptr{Cvoid}, Ref{Cushort}, Ptr{Cvoid}, Ref{Cuint}), sock.handle, rport, raddress, rfamily) else r = ccall(:jl_tcp_getpeername, Int32, (Ptr{Cvoid}, Ref{Cushort}, Ptr{Cvoid}, Ref{Cuint}), sock.handle, rport, raddress, rfamily) end iolock_end() uv_error("cannot obtain socket name", r) port = ntoh(rport[]) af_inet6 = @static if Sys.iswindows() # AF_INET6 in 23 elseif Sys.isapple() 30 elseif Sys.KERNEL โˆˆ (:FreeBSD, :DragonFly) 28 elseif Sys.KERNEL โˆˆ (:NetBSD, :OpenBSD) 24 else 10 end if rfamily[] == 2 # AF_INET addrv4 = raddress[1:4] naddr = ntoh(unsafe_load(Ptr{Cuint}(pointer(addrv4)), 1)) addr = IPv4(naddr) elseif rfamily[] == af_inet6 naddr = ntoh(unsafe_load(Ptr{UInt128}(pointer(raddress)), 1)) addr = IPv6(naddr) else error(string("unsupported address family: ", rfamily[])) end return addr, port end # domain sockets include("PipeServer.jl") end q/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Sockets/src/IPAddr.jl^"# This file is a part of Julia. License is MIT: https://julialang.org/license """ IPAddr Abstract supertype for IP addresses. [`IPv4`](@ref) and [`IPv6`](@ref) are subtypes of this. """ abstract type IPAddr end Base.isless(a::T, b::T) where {T<:IPAddr} = isless(a.host, b.host) (dt::Type{<:Integer})(ip::IPAddr) = dt(ip.host)::dt # Allow IP addresses to broadcast as unwrapped scalars Base.Broadcast.broadcastable(ip::IPAddr) = Ref(ip) struct IPv4 <: IPAddr host::UInt32 IPv4(host::UInt32) = new(host) IPv4(a::UInt8,b::UInt8,c::UInt8,d::UInt8) = new(UInt32(a)<<24| UInt32(b)<<16| UInt32(c)<<8| d) function IPv4(a::Integer,b::Integer,c::Integer,d::Integer) if !(0<=a<=255 && 0<=b<=255 && 0<=c<=255 && 0<=d<=255) throw(ArgumentError("IPv4 field out of range (must be 0-255)")) end IPv4(UInt8(a),UInt8(b),UInt8(c),UInt8(d)) end end """ IPv4(host::Integer) -> IPv4 Return an IPv4 object from ip address `host` formatted as an [`Integer`](@ref). # Examples ```jldoctest julia> IPv4(3223256218) ip"192.30.252.154" ``` """ function IPv4(host::Integer) if host < 0 throw(ArgumentError("IPv4 address must be positive")) elseif typemax(typeof(host)) > typemax(UInt32) && host > typemax(UInt32) throw(ArgumentError("IPv4 address must fit within 32 bits")) else return IPv4(UInt32(host)) end end # constructor: ("1.2.3.4") IPv4(str::AbstractString) = parse(IPv4, str) show(io::IO,ip::IPv4) = print(io,"ip\"",ip,"\"") print(io::IO,ip::IPv4) = print(io,string((ip.host&(0xFF000000))>>24),".", string((ip.host&(0xFF0000))>>16),".", string((ip.host&(0xFF00))>>8),".", string(ip.host&0xFF)) struct IPv6 <: IPAddr host::UInt128 IPv6(host::UInt128) = new(host) IPv6(a::UInt16,b::UInt16,c::UInt16,d::UInt16, e::UInt16,f::UInt16,g::UInt16,h::UInt16) = new(UInt128(a)<<(7*16)| UInt128(b)<<(6*16)| UInt128(c)<<(5*16)| UInt128(d)<<(4*16)| UInt128(e)<<(3*16)| UInt128(f)<<(2*16)| UInt128(g)<<(1*16)| h) function IPv6(a::Integer,b::Integer,c::Integer,d::Integer, e::Integer,f::Integer,g::Integer,h::Integer) if !(0<=a<=0xFFFF && 0<=b<=0xFFFF && 0<=c<=0xFFFF && 0<=d<=0xFFFF && 0<=e<=0xFFFF && 0<=f<=0xFFFF && 0<=g<=0xFFFF && 0<=h<=0xFFFF) throw(ArgumentError("IPv6 field out of range (must be 0-65535)")) end IPv6(UInt16(a),UInt16(b),UInt16(c),UInt16(d), UInt16(e),UInt16(f),UInt16(g),UInt16(h)) end end """ IPv6(host::Integer) -> IPv6 Return an IPv6 object from ip address `host` formatted as an [`Integer`](@ref). # Examples ```jldoctest julia> IPv6(3223256218) ip"::c01e:fc9a" ``` """ function IPv6(host::Integer) if host < 0 throw(ArgumentError("IPv6 address must be positive")) # We allow passing bigger integer types, but need to be careful to avoid overflow # Let's hope promotion rules are sensible elseif typemax(typeof(host)) > typemax(UInt128) && host > typemax(UInt128) throw(ArgumentError("IPv6 address must fit within 128 bits")) else return IPv6(UInt128(host)) end end IPv6(str::AbstractString) = parse(IPv6, str) # Suppress leading '0's and "0x" print_ipv6_field(io,field::UInt16) = print(io,string(field, base = 16)) print_ipv6_field(io,ip,i) = print_ipv6_field(io,ipv6_field(ip,i)) function ipv6_field(ip::IPv6,i) if i < 0 || i > 7 throw(BoundsError()) end UInt16((ip.host&(UInt128(0xFFFF)<<(i*16))) >> (i*16)) end show(io::IO, ip::IPv6) = print(io,"ip\"",ip,"\"") # RFC 5952 compliant show function # http://tools.ietf.org/html/rfc5952 function print(io::IO,ip::IPv6) i = 8 m = 0 longest_sub_i = -1 while i!=0 i-=1 field = ipv6_field(ip,i) if field == 0 && longest_sub_i == -1 # Find longest subsequence of 0 longest_sub_i,j,m,c = i,i,1,1 while j != 0 j-=1 if ipv6_field(ip,j) == 0 c += 1 else c = 0 end if c > m if j+c != longest_sub_i+1 longest_sub_i = j+c-1 end m = c end end # Prevent single 0 from contracting to :: as required if m == 1 longest_sub_i = 9 end end if i == longest_sub_i print(io,":") i -= m-1 if i == 0 print(io,":") break end else if i != 7 print(io,":") end print_ipv6_field(io,field) end end end # Parsing const ipv4_leading_zero_error = """ Leading zeros in IPv4 addresses are disallowed due to ambiguity. If the address is in octal or hexadecimal, convert it to decimal, otherwise remove the leading zero. """ function parse(::Type{IPv4}, str::AbstractString) fields = split(str,'.') i = 1 ret = 0 for f in fields if isempty(f) throw(ArgumentError("empty field in IPv4 address")) end if length(f) > 1 && f[1] == '0' throw(ArgumentError(ipv4_leading_zero_error)) else r = parse(Int, f, base = 10) end if i != length(fields) if r < 0 || r > 255 throw(ArgumentError("IPv4 field out of range (must be 0-255)")) end ret |= UInt32(r) << ((4-i)*8) else if r > ((UInt64(1)<<((5-length(fields))*8))-1) throw(ArgumentError("IPv4 field too large")) end ret |= r end i+=1 end IPv4(ret) end function parseipv6fields(fields,num_fields) if length(fields) > num_fields throw(ArgumentError("too many fields in IPv6 address")) end cf = 7 ret = UInt128(0) for f in fields if isempty(f) # ::abc:... and ..:abc:: if cf != 7 && cf != 0 cf -= num_fields-length(fields) end cf -= 1 continue end ret |= UInt128(parse(Int, f, base = 16))<<(cf*16) cf -= 1 end ret end parseipv6fields(fields) = parseipv6fields(fields,8) function parse(::Type{IPv6}, str::AbstractString) fields = split(str,':') if length(fields) > 8 throw(ArgumentError("too many fields in IPv6 address")) elseif length(fields) == 8 return IPv6(parseipv6fields(fields)) elseif in('.',fields[end]) return IPv6((parseipv6fields(fields[1:(end-1)],6)) | parse(IPv4, fields[end]).host ) else return IPv6(parseipv6fields(fields)) end end # # This supports IP addresses in the common dot (IPv4) or colon (IPv6) # separated formats. Most other common formats use a standard integer encoding # of the appropriate size and should use the appropriate constructor # function parse(::Type{IPAddr}, str::AbstractString) if ':' in str return parse(IPv6, str) else return parse(IPv4, str) end end """ @ip_str str -> IPAddr Parse `str` as an IP address. # Examples ```jldoctest julia> ip"127.0.0.1" ip"127.0.0.1" julia> @ip_str "2001:db8:0:0:0:0:2:1" ip"2001:db8::2:1" ``` """ macro ip_str(str) return parse(IPAddr, str) end struct InetAddr{T<:IPAddr} host::T port::UInt16 end """ InetAddr(ip::IPAddr, port) -> InetAddr Return an `InetAddr` object from ip address `ip` and port number `port`. # Examples ```jldoctest julia> Sockets.InetAddr(ip"127.0.0.1", 8000) Sockets.InetAddr{IPv4}(ip"127.0.0.1", 8000) ``` """ InetAddr(ip::IPAddr, port) = InetAddr{typeof(ip)}(ip, port) """ InetAddr(str::AbstractString, port) -> InetAddr Return an `InetAddr` object from ip address `str` formatted as [`AbstractString`](@ref) and port number `port`. !!! compat "Julia 1.3" This constructor requires at least Julia 1.3. # Examples ```jldoctest julia> Sockets.InetAddr("127.0.0.1", 8000) Sockets.InetAddr{IPv4}(ip"127.0.0.1", 8000) ``` """ InetAddr(str::AbstractString, port) = InetAddr(parse(IPAddr, str), port) function show(io::IO, addr::InetAddr) show(io, typeof(addr)) print(io, "(") show(io, addr.host) print(io, ", ", Int(addr.port), ")") end s/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Sockets/src/addrinfo.jl๐.# This file is a part of Julia. License is MIT: https://julialang.org/license """ DNSError The type of exception thrown when an error occurs in DNS lookup. The `host` field indicates the host URL string. The `code` field indicates the error code based on libuv. """ struct DNSError <: Exception host::String code::Int32 end function show(io::IO, err::DNSError) print(io, "DNSError: ", err.host, ", ", Base.struverror(err.code), " (", Base.uverrorname(err.code), ")") end function uv_getaddrinfocb(req::Ptr{Cvoid}, status::Cint, addrinfo::Ptr{Cvoid}) data = uv_req_data(req) if data != C_NULL t = unsafe_pointer_to_objref(data)::Task uv_req_set_data(req, C_NULL) if status != 0 || addrinfo == C_NULL schedule(t, _UVError("getaddrinfo", status)) else freeaddrinfo = addrinfo addrs = IPAddr[] while addrinfo != C_NULL sockaddr = ccall(:jl_sockaddr_from_addrinfo, Ptr{Cvoid}, (Ptr{Cvoid},), addrinfo) if ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Cvoid},), sockaddr) == 1 ip4addr = ccall(:jl_sockaddr_host4, UInt32, (Ptr{Cvoid},), sockaddr) push!(addrs, IPv4(ntoh(ip4addr))) elseif ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Cvoid},), sockaddr) == 1 ip6addr = Ref{UInt128}() scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Cvoid}, Ptr{UInt128}), sockaddr, ip6addr) push!(addrs, IPv6(ntoh(ip6addr[]))) end addrinfo = ccall(:jl_next_from_addrinfo, Ptr{Cvoid}, (Ptr{Cvoid},), addrinfo) end ccall(:uv_freeaddrinfo, Cvoid, (Ptr{Cvoid},), freeaddrinfo) schedule(t, addrs) end else # no owner for this req, safe to just free it Libc.free(req) end nothing end """ getalladdrinfo(host::AbstractString) -> Vector{IPAddr} Gets all of the IP addresses of the `host`. Uses the operating system's underlying `getaddrinfo` implementation, which may do a DNS lookup. # Example ```julia-repl julia> getalladdrinfo("google.com") 2-element Array{IPAddr,1}: ip"172.217.6.174" ip"2607:f8b0:4000:804::200e" ``` """ function getalladdrinfo(host::String) req = Libc.malloc(Base._sizeof_uv_getaddrinfo) uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call iolock_begin() status = ccall(:jl_getaddrinfo, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Ptr{Cvoid}), eventloop(), req, host, #=service=#C_NULL, @cfunction(uv_getaddrinfocb, Cvoid, (Ptr{Cvoid}, Cint, Ptr{Cvoid}))) if status < 0 Libc.free(req) if status == UV_EINVAL throw(ArgumentError("Invalid getaddrinfo argument")) elseif status == UV_ENOMEM || status == UV_ENOBUFS throw(OutOfMemoryError()) end uv_error("getaddrinfo", status) end ct = current_task() preserve_handle(ct) Base.sigatomic_begin() uv_req_set_data(req, ct) iolock_end() r = try Base.sigatomic_end() wait() finally Base.sigatomic_end() iolock_begin() ct.queue === nothing || list_deletefirst!(ct.queue, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later uv_req_set_data(req, C_NULL) ccall(:uv_cancel, Int32, (Ptr{Cvoid},), req) # try to let libuv know we don't care anymore else # done with req Libc.free(req) end iolock_end() unpreserve_handle(ct) end if isa(r, IOError) code = r.code if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS, UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL, UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME, UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE, UV_EAI_SOCKTYPE) throw(DNSError(host, code)) elseif code == UV_EAI_MEMORY throw(OutOfMemoryError()) else throw(r) end end return r::Vector{IPAddr} end getalladdrinfo(host::AbstractString) = getalladdrinfo(String(host)) """ getaddrinfo(host::AbstractString, IPAddr=IPv4) -> IPAddr Gets the first IP address of the `host` of the specified `IPAddr` type. Uses the operating system's underlying getaddrinfo implementation, which may do a DNS lookup. """ function getaddrinfo(host::String, T::Type{<:IPAddr}) addrs = getalladdrinfo(host) for addr in addrs if addr isa T return addr end end throw(DNSError(host, UV_EAI_NONAME)) end getaddrinfo(host::AbstractString, T::Type{<:IPAddr}) = getaddrinfo(String(host), T) function getaddrinfo(host::AbstractString) addrs = getalladdrinfo(String(host)) if !isempty(addrs) return addrs[begin]::Union{IPv4,IPv6} end throw(DNSError(host, UV_EAI_NONAME)) end function uv_getnameinfocb(req::Ptr{Cvoid}, status::Cint, hostname::Cstring, service::Cstring) data = uv_req_data(req) if data != C_NULL t = unsafe_pointer_to_objref(data)::Task uv_req_set_data(req, C_NULL) if status != 0 schedule(t, _UVError("getnameinfo", status)) else schedule(t, unsafe_string(hostname)) end else # no owner for this req, safe to just free it Libc.free(req) end nothing end """ getnameinfo(host::IPAddr) -> String Performs a reverse-lookup for IP address to return a hostname and service using the operating system's underlying `getnameinfo` implementation. # Examples ```julia-repl julia> getnameinfo(IPv4("8.8.8.8")) "google-public-dns-a.google.com" ``` """ function getnameinfo(address::Union{IPv4, IPv6}) req = Libc.malloc(Base._sizeof_uv_getnameinfo) uv_req_set_data(req, C_NULL) # in case we get interrupted before arriving at the wait call port = hton(UInt16(0)) flags = 0 uvcb = @cfunction(uv_getnameinfocb, Cvoid, (Ptr{Cvoid}, Cint, Cstring, Cstring)) status = UV_EINVAL host_in = Ref(hton(address.host)) iolock_begin() status = ccall(:jl_getnameinfo, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, UInt16, Cint, Ptr{Cvoid}, Cint), eventloop(), req, host_in, port, flags, uvcb, address isa IPv6) if status < 0 Libc.free(req) if status == UV_EINVAL throw(ArgumentError("Invalid getnameinfo argument")) elseif status == UV_ENOMEM || status == UV_ENOBUFS throw(OutOfMemoryError()) end uv_error("getnameinfo", status) end ct = current_task() preserve_handle(ct) Base.sigatomic_begin() uv_req_set_data(req, ct) iolock_end() r = try Base.sigatomic_end() wait() finally Base.sigatomic_end() iolock_begin() ct.queue === nothing || list_deletefirst!(ct.queue, ct) if uv_req_data(req) != C_NULL # req is still alive, # so make sure we don't get spurious notifications later uv_req_set_data(req, C_NULL) ccall(:uv_cancel, Int32, (Ptr{Cvoid},), req) # try to let libuv know we don't care anymore else # done with req Libc.free(req) end iolock_end() unpreserve_handle(ct) end if isa(r, IOError) code = r.code if code in (UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS, UV_EAI_BADHINTS, UV_EAI_CANCELED, UV_EAI_FAIL, UV_EAI_FAMILY, UV_EAI_NODATA, UV_EAI_NONAME, UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, UV_EAI_SERVICE, UV_EAI_SOCKTYPE) throw(DNSError(repr(address), code)) elseif code == UV_EAI_MEMORY throw(OutOfMemoryError()) else throw(r) end end return r::String end const _sizeof_uv_interface_address = ccall(:jl_uv_sizeof_interface_address,Int32,()) """ getipaddr() -> IPAddr Get an IP address of the local machine, preferring IPv4 over IPv6. Throws if no addresses are available. getipaddr(addr_type::Type{T}) where T<:IPAddr -> T Get an IP address of the local machine of the specified type. Throws if no addresses of the specified type are available. This function is a backwards-compatibility wrapper around [`getipaddrs`](@ref). New applications should use [`getipaddrs`](@ref) instead. # Examples ```julia-repl julia> getipaddr() ip"192.168.1.28" julia> getipaddr(IPv6) ip"fe80::9731:35af:e1c5:6e49" ``` See also [`getipaddrs`](@ref). """ function getipaddr(addr_type::Type{T}) where T<:IPAddr addrs = getipaddrs(addr_type) if length(addrs) == 0 error("No networking interface available") end # Prefer the first IPv4 address i = something(findfirst(ip -> ip isa IPv4, addrs), 1) return addrs[i] end getipaddr() = getipaddr(IPv4) """ getipaddrs(addr_type::Type{T}=IPAddr; loopback::Bool=false) where T<:IPAddr -> Vector{T} Get the IP addresses of the local machine. Setting the optional `addr_type` parameter to `IPv4` or `IPv6` causes only addresses of that type to be returned. The `loopback` keyword argument dictates whether loopback addresses (e.g. `ip"127.0.0.1"`, `ip"::1"`) are included. !!! compat "Julia 1.2" This function is available as of Julia 1.2. # Examples ```julia-repl julia> getipaddrs() 5-element Array{IPAddr,1}: ip"198.51.100.17" ip"203.0.113.2" ip"2001:db8:8:4:445e:5fff:fe5d:5500" ip"2001:db8:8:4:c164:402e:7e3c:3668" ip"fe80::445e:5fff:fe5d:5500" julia> getipaddrs(IPv6) 3-element Array{IPv6,1}: ip"2001:db8:8:4:445e:5fff:fe5d:5500" ip"2001:db8:8:4:c164:402e:7e3c:3668" ip"fe80::445e:5fff:fe5d:5500" ``` See also [`islinklocaladdr`](@ref). """ function getipaddrs(addr_type::Type{T}=IPAddr; loopback::Bool=false) where T<:IPAddr addresses = T[] addr_ref = Ref{Ptr{UInt8}}(C_NULL) count_ref = Ref{Int32}(1) lo_present = false err = ccall(:jl_uv_interface_addresses, Int32, (Ref{Ptr{UInt8}}, Ref{Int32}), addr_ref, count_ref) uv_error("getlocalip", err) addr, count = addr_ref[], count_ref[] for i = 0:(count-1) current_addr = addr + i*_sizeof_uv_interface_address if 1 == ccall(:jl_uv_interface_address_is_internal, Int32, (Ptr{UInt8},), current_addr) lo_present = true if !loopback continue end end sockaddr = ccall(:jl_uv_interface_address_sockaddr, Ptr{Cvoid}, (Ptr{UInt8},), current_addr) if IPv4 <: T && ccall(:jl_sockaddr_is_ip4, Int32, (Ptr{Cvoid},), sockaddr) == 1 push!(addresses, IPv4(ntoh(ccall(:jl_sockaddr_host4, UInt32, (Ptr{Cvoid},), sockaddr)))) elseif IPv6 <: T && ccall(:jl_sockaddr_is_ip6, Int32, (Ptr{Cvoid},), sockaddr) == 1 addr6 = Ref{UInt128}() scope_id = ccall(:jl_sockaddr_host6, UInt32, (Ptr{Cvoid}, Ref{UInt128},), sockaddr, addr6) push!(addresses, IPv6(ntoh(addr6[]))) end end ccall(:uv_free_interface_addresses, Cvoid, (Ptr{UInt8}, Int32), addr, count) return addresses end """ islinklocaladdr(addr::IPAddr) Tests if an IP address is a link-local address. Link-local addresses are not guaranteed to be unique beyond their network segment, therefore routers do not forward them. Link-local addresses are from the address blocks `169.254.0.0/16` or `fe80::/10`. # Example ```julia filter(!islinklocaladdr, getipaddrs()) ``` """ function islinklocaladdr(addr::IPv4) # RFC 3927 return (addr.host & 0xFFFF0000) == 0xA9FE0000 end function islinklocaladdr(addr::IPv6) # RFC 4291 return (addr.host & 0xFFC00000000000000000000000000000) == 0xFE800000000000000000000000000000 end u/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Sockets/src/PipeServer.jlษ # This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct PipeServer <: LibuvServer handle::Ptr{Cvoid} status::Int cond::Base.ThreadSynchronizer function PipeServer(handle::Ptr{Cvoid}, status) p = new(handle, status, Base.ThreadSynchronizer()) associate_julia_struct(p.handle, p) finalizer(uvfinalize, p) return p end end function PipeServer() pipe = PipeServer(Libc.malloc(Base._sizeof_uv_named_pipe), StatusUninit) iolock_begin() err = ccall(:uv_pipe_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), eventloop(), pipe.handle, 0) uv_error("failed to create pipe server", err) pipe.status = StatusInit iolock_end() return pipe end ## server functions ## accept(server::PipeServer) = accept(server, PipeEndpoint()) function accept_nonblock(server::PipeServer, client::PipeEndpoint) iolock_begin() if client.status != StatusInit error("client is already in use or has been closed") end err = ccall(:uv_accept, Int32, (Ptr{Cvoid}, Ptr{Cvoid}), server.handle, client.handle) if err == 0 client.status = StatusOpen end iolock_end() return err end function accept_nonblock(server::PipeServer) client = PipeEndpoint() uv_error("accept", accept_nonblock(server, client) != 0) return client end function bind(server::PipeServer, name::AbstractString) iolock_begin() @assert server.status == StatusInit err = ccall(:uv_pipe_bind, Int32, (Ptr{Cvoid}, Cstring), server, name) if err != 0 iolock_end() if err != UV_EADDRINUSE && err != UV_EACCES #TODO: this codepath is currently not tested throw(_UVError("bind", err)) else return false end end server.status = StatusOpen iolock_end() return true end """ listen(path::AbstractString) -> PipeServer Create and listen on a named pipe / UNIX domain socket. !!! note Path length on Unix is limited to somewhere between 92 and 108 bytes (cf. `man unix`). """ function listen(path::AbstractString) sock = PipeServer() bind(sock, path) || throw(ArgumentError("could not listen on path $path")) return listen(sock) end function connect!(sock::PipeEndpoint, path::AbstractString) iolock_begin() @assert sock.status == StatusInit req = Libc.malloc(Base._sizeof_uv_connect) uv_req_set_data(req, C_NULL) ccall(:uv_pipe_connect, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), req, sock.handle, path, @cfunction(uv_connectcb, Cvoid, (Ptr{Cvoid}, Cint))) sock.status = StatusConnecting iolock_end() return sock end """ connect(path::AbstractString) -> PipeEndpoint Connect to the named pipe / UNIX domain socket at `path`. !!! note Path length on Unix is limited to somewhere between 92 and 108 bytes (cf. `man unix`). """ connect(path::AbstractString) = connect(PipeEndpoint(), path) r/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Unicode/src/Unicode.jlT5# This file is a part of Julia. License is MIT: https://julialang.org/license module Unicode export graphemes, isequal_normalized """ Unicode.julia_chartransform(c::Union{Char,Integer}) Map the Unicode character (`Char`) or codepoint (`Integer`) `c` to the corresponding "equivalent" character or codepoint, respectively, according to the custom equivalence used within the Julia parser (in addition to NFC normalization). For example, `'ยต'` (U+00B5 micro) is treated as equivalent to `'ฮผ'` (U+03BC mu) by Julia's parser, so `julia_chartransform` performs this transformation while leaving other characters unchanged: ```jldoctest julia> Unicode.julia_chartransform('\u00B5') 'ฮผ': Unicode U+03BC (category Ll: Letter, lowercase) julia> Unicode.julia_chartransform('x') 'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase) ``` `julia_chartransform` is mainly useful for passing to the [`Unicode.normalize`](@ref) function in order to mimic the normalization used by the Julia parser: ```jldoctest julia> s = "\u00B5o\u0308" "ยตoฬˆ" julia> s2 = Unicode.normalize(s, compose=true, stable=true, chartransform=Unicode.julia_chartransform) "ฮผรถ" julia> collect(s2) 2-element Vector{Char}: 'ฮผ': Unicode U+03BC (category Ll: Letter, lowercase) 'รถ': Unicode U+00F6 (category Ll: Letter, lowercase) julia> s2 == string(Meta.parse(s)) true ``` !!! compat "Julia 1.8" This function was introduced in Julia 1.8. """ function julia_chartransform end julia_chartransform(codepoint::UInt32) = get(Base.Unicode._julia_charmap, codepoint, codepoint) julia_chartransform(codepoint::Integer) = julia_chartransform(UInt32(codepoint)) julia_chartransform(char::Char) = Char(julia_chartransform(UInt32(char))) """ Unicode.normalize(s::AbstractString; keywords...) Unicode.normalize(s::AbstractString, normalform::Symbol) Normalize the string `s`. By default, canonical composition (`compose=true`) is performed without ensuring Unicode versioning stability (`compat=false`), which produces the shortest possible equivalent string but may introduce composition characters not present in earlier Unicode versions. Alternatively, one of the four "normal forms" of the Unicode standard can be specified: `normalform` can be `:NFC`, `:NFD`, `:NFKC`, or `:NFKD`. Normal forms C (canonical composition) and D (canonical decomposition) convert different visually identical representations of the same abstract string into a single canonical form, with form C being more compact. Normal forms KC and KD additionally canonicalize "compatibility equivalents": they convert characters that are abstractly similar but visually distinct into a single canonical choice (e.g. they expand ligatures into the individual characters), with form KC being more compact. Alternatively, finer control and additional transformations may be obtained by calling `Unicode.normalize(s; keywords...)`, where any number of the following boolean keywords options (which all default to `false` except for `compose`) are specified: * `compose=false`: do not perform canonical composition * `decompose=true`: do canonical decomposition instead of canonical composition (`compose=true` is ignored if present) * `compat=true`: compatibility equivalents are canonicalized * `casefold=true`: perform Unicode case folding, e.g. for case-insensitive string comparison * `newline2lf=true`, `newline2ls=true`, or `newline2ps=true`: convert various newline sequences (LF, CRLF, CR, NEL) into a linefeed (LF), line-separation (LS), or paragraph-separation (PS) character, respectively * `stripmark=true`: strip diacritical marks (e.g. accents) * `stripignore=true`: strip Unicode's "default ignorable" characters (e.g. the soft hyphen or the left-to-right marker) * `stripcc=true`: strip control characters; horizontal tabs and form feeds are converted to spaces; newlines are also converted to spaces unless a newline-conversion flag was specified * `rejectna=true`: throw an error if unassigned code points are found * `stable=true`: enforce Unicode versioning stability (never introduce characters missing from earlier Unicode versions) You can also use the `chartransform` keyword (which defaults to `identity`) to pass an arbitrary *function* mapping `Integer` codepoints to codepoints, which is is called on each character in `s` as it is processed, in order to perform arbitrary additional normalizations. For example, by passing `chartransform=Unicode.julia_chartransform`, you can apply a few Julia-specific character normalizations that are performed by Julia when parsing identifiers (in addition to NFC normalization: `compose=true, stable=true`). For example, NFKC corresponds to the options `compose=true, compat=true, stable=true`. # Examples ```jldoctest julia> "รฉ" == Unicode.normalize("eฬ") #LHS: Unicode U+00e9, RHS: U+0065 & U+0301 true julia> "ฮผ" == Unicode.normalize("ยต", compat=true) #LHS: Unicode U+03bc, RHS: Unicode U+00b5 true julia> Unicode.normalize("JuLiA", casefold=true) "julia" julia> Unicode.normalize("JรบLiA", stripmark=true) "JuLiA" ``` !!! compat "Julia 1.8" The `chartransform` keyword argument requires Julia 1.8. """ function normalize end normalize(s::AbstractString, nf::Symbol) = Base.Unicode.normalize(s, nf) normalize(s::AbstractString; kwargs...) = Base.Unicode.normalize(s; kwargs...) """ Unicode.isassigned(c) -> Bool Return `true` if the given char or integer is an assigned Unicode code point. # Examples ```jldoctest julia> Unicode.isassigned(101) true julia> Unicode.isassigned('\\x01') true ``` """ isassigned(c) = Base.Unicode.isassigned(c) """ graphemes(s::AbstractString) -> GraphemeIterator Return an iterator over substrings of `s` that correspond to the extended graphemes in the string, as defined by Unicode UAX #29. (Roughly, these are what users would perceive as single characters, even though they may contain more than one codepoint; for example a letter combined with an accent mark is a single grapheme.) """ graphemes(s::AbstractString) = Base.Unicode.GraphemeIterator{typeof(s)}(s) """ graphemes(s::AbstractString, m:n) -> SubString Returns a [`SubString`](@ref) of `s` consisting of the `m`-th through `n`-th graphemes of the string `s`, where the second argument `m:n` is an integer-valued [`AbstractUnitRange`](@ref). Loosely speaking, this corresponds to the `m:n`-th user-perceived "characters" in the string. For example: ```jldoctest julia> s = graphemes("exposeฬ", 3:6) "poseฬ" julia> collect(s) 5-element Vector{Char}: 'p': ASCII/Unicode U+0070 (category Ll: Letter, lowercase) 'o': ASCII/Unicode U+006F (category Ll: Letter, lowercase) 's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase) 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase) 'ฬ': Unicode U+0301 (category Mn: Mark, nonspacing) ``` This consists of the 3rd to *7th* codepoints ([`Char`](@ref)s) in `"exposeฬ"`, because the grapheme `"eฬ"` is actually *two* Unicode codepoints (an `'e'` followed by an acute-accent combining character U+0301). Because finding grapheme boundaries requires iteration over the string contents, the `graphemes(s, m:n)` function requires time proportional to the length of the string (number of codepoints) before the end of the substring. !!! compat "Julia 1.9" The `m:n` argument of `graphemes` requires Julia 1.9. """ function graphemes(s::AbstractString, r::AbstractUnitRange{<:Integer}) m, n = Int(first(r)), Int(last(r)) m > 0 || throw(ArgumentError("starting index $m is not โ‰ฅ 1")) n < m && return @view s[1:0] c0 = eltype(s)(0x00000000) state = Ref{Int32}(0) count = 0 i, iprev, ilast = 1, 1, lastindex(s) # find the start of the m-th grapheme while i โ‰ค ilast && count < m @inbounds c = s[i] count += Base.Unicode.isgraphemebreak!(state, c0, c) c0 = c i, iprev = nextind(s, i), i end start = iprev count < m && throw(BoundsError(s, i)) # find the end of the n-th grapheme while i โ‰ค ilast @inbounds c = s[i] count += Base.Unicode.isgraphemebreak!(state, c0, c) count > n && break c0 = c i, iprev = nextind(s, i), i end count < n && throw(BoundsError(s, i)) return @view s[start:iprev] end using Base.Unicode: utf8proc_error, UTF8PROC_DECOMPOSE, UTF8PROC_CASEFOLD, UTF8PROC_STRIPMARK function _decompose_char!(codepoint::Union{Integer,Char}, dest::Vector{UInt32}, offset::Integer, options::Integer) ret = GC.@preserve dest @ccall utf8proc_decompose_char(codepoint::UInt32, pointer(dest, 1+offset)::Ptr{UInt32}, (length(dest)-offset)::Int, options::Cint, C_NULL::Ptr{Cint})::Int ret < 0 && utf8proc_error(ret) return ret end # would be good to have higher-level accessor functions in utf8proc. alternatively, # we could mirror the whole utf8proc_property_t struct in Julia, but that is annoying # because of the bitfields. combining_class(uc::Integer) = 0x000301 โ‰ค uc โ‰ค 0x10ffff ? unsafe_load(ccall(:utf8proc_get_property, Ptr{UInt16}, (UInt32,), uc), 2) : 0x0000 combining_class(c::AbstractChar) = ismalformed(c) ? 0x0000 : combining_class(UInt32(c)) """ isequal_normalized(s1::AbstractString, s2::AbstractString; casefold=false, stripmark=false, chartransform=identity) Return whether `s1` and `s2` are canonically equivalent Unicode strings. If `casefold=true`, ignores case (performs Unicode case-folding); if `stripmark=true`, strips diacritical marks and other combining characters. As with [`Unicode.normalize`](@ref), you can also pass an arbitrary function via the `chartransform` keyword (mapping `Integer` codepoints to codepoints) to perform custom normalizations, such as [`Unicode.julia_chartransform`](@ref). !!! compat "Julia 1.8" The `isequal_normalized` function was added in Julia 1.8. # Examples For example, the string `"noรซl"` can be constructed in two canonically equivalent ways in Unicode, depending on whether `"รซ"` is formed from a single codepoint U+00EB or from the ASCII character `'e'` followed by the U+0308 combining-diaeresis character. ```jldoctest julia> s1 = "no\u00EBl" "noรซl" julia> s2 = "noe\u0308l" "noeฬˆl" julia> s1 == s2 false julia> isequal_normalized(s1, s2) true julia> isequal_normalized(s1, "noel", stripmark=true) true julia> isequal_normalized(s1, "NOร‹L", casefold=true) true ``` """ isequal_normalized(s1::AbstractString, s2::AbstractString; casefold::Bool=false, stripmark::Bool=false, chartransform=identity) = _isequal_normalized!(s1, s2, Vector{UInt32}(undef, 4), Vector{UInt32}(undef, 4), chartransform; casefold, stripmark) # like isequal_normalized, but takes pre-allocated codepoint buffers as arguments, and chartransform is a positional argument function _isequal_normalized!(s1::AbstractString, s2::AbstractString, d1::Vector{UInt32}, d2::Vector{UInt32}, chartransform::F=identity; casefold::Bool=false, stripmark::Bool=false) where {F} function decompose_next_chars!(state, d, options, s) local n offset = 0 @inbounds while true # read a char and decompose it to d c = chartransform(UInt32(state[1])) state = iterate(s, state[2]) if c < 0x80 # fast path for common ASCII case n = 1 + offset n > length(d) && resize!(d, 2n) d[n] = casefold ? (0x41 โ‰ค c โ‰ค 0x5A ? c+0x20 : c) : c break # ASCII characters are all zero combining class else while true n = _decompose_char!(c, d, offset, options) + offset if n > length(d) resize!(d, 2n) continue end break end end # decomposed chars must be sorted in ascending order of combining class, # which means we need to keep fetching chars until we get to non-combining (iszero(combining_class(d[n])) || isnothing(state)) && break # non-combining offset = n end # sort by combining class if n < 32 # almost always true for j1 = 2:n # insertion sort cc = combining_class(d[j1]) iszero(cc) && continue # don't re-order non-combiners for j2 = j1:-1:2 combining_class(d[j2-1]) โ‰ค cc && break d[j2-1], d[j2] = d[j2], d[j2-1] end end else # avoid n^2 complexity in crazy large-n case j = 1 @views while j < n jโ‚€ = j + something(findnext(iszero โˆ˜ combining_class, d[j+1:n], 1), n+1-j) sort!(d[j:jโ‚€-1], by=combining_class) j = jโ‚€ end end # split return statement to help type inference: return state === nothing ? (1, n, nothing) : (1, n, state) end options = UTF8PROC_DECOMPOSE casefold && (options |= UTF8PROC_CASEFOLD) stripmark && (options |= UTF8PROC_STRIPMARK) i1,i2 = iterate(s1),iterate(s2) n1 = n2 = 0 # lengths of codepoint buffers j1 = j2 = 1 # indices in d1, d2 while true if j1 > n1 i1 === nothing && return i2 === nothing && j2 > n2 j1, n1, i1 = decompose_next_chars!(i1, d1, options, s1) end if j2 > n2 i2 === nothing && return false j2, n2, i2 = decompose_next_chars!(i2, d2, options, s2) end d1[j1] == d2[j2] || return false j1 += 1; j2 += 1 end end end ~/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/LinearAlgebra.jlญP# This file is a part of Julia. License is MIT: https://julialang.org/license """ Linear algebra module. Provides array arithmetic, matrix factorizations and other linear algebra related functionality. """ module LinearAlgebra import Base: \, /, *, ^, +, -, == import Base: USE_BLAS64, abs, acos, acosh, acot, acoth, acsc, acsch, adjoint, asec, asech, asin, asinh, atan, atanh, axes, big, broadcast, ceil, cis, collect, conj, convert, copy, copyto!, copymutable, cos, cosh, cot, coth, csc, csch, eltype, exp, fill!, floor, getindex, hcat, getproperty, imag, inv, isapprox, isequal, isone, iszero, IndexStyle, kron, kron!, length, log, map, ndims, one, oneunit, parent, permutedims, power_by_squaring, promote_rule, real, sec, sech, setindex!, show, similar, sin, sincos, sinh, size, sqrt, strides, stride, tan, tanh, transpose, trunc, typed_hcat, vec, view, zero using Base: IndexLinear, promote_eltype, promote_op, promote_typeof, print_matrix, @propagate_inbounds, reduce, typed_hvcat, typed_vcat, require_one_based_indexing, splat using Base.Broadcast: Broadcasted, broadcasted using Base.PermutedDimsArrays: CommutativeOps using OpenBLAS_jll using libblastrampoline_jll import Libdl export # Modules LAPACK, BLAS, # Types Adjoint, Transpose, SymTridiagonal, Tridiagonal, Bidiagonal, Factorization, BunchKaufman, Cholesky, CholeskyPivoted, ColumnNorm, Eigen, GeneralizedEigen, GeneralizedSVD, GeneralizedSchur, Hessenberg, LU, LDLt, NoPivot, RowNonZero, QR, QRPivoted, LQ, Schur, SVD, Hermitian, RowMaximum, Symmetric, LowerTriangular, UpperTriangular, UnitLowerTriangular, UnitUpperTriangular, UpperHessenberg, Diagonal, UniformScaling, # Functions axpy!, axpby!, bunchkaufman, bunchkaufman!, cholesky, cholesky!, cond, condskeel, copyto!, copy_transpose!, cross, adjoint, adjoint!, det, diag, diagind, diagm, dot, eigen, eigen!, eigmax, eigmin, eigvals, eigvals!, eigvecs, factorize, givens, hermitianpart, hermitianpart!, hessenberg, hessenberg!, isdiag, ishermitian, isposdef, isposdef!, issuccess, issymmetric, istril, istriu, kron, kron!, ldiv!, ldlt!, ldlt, logabsdet, logdet, lowrankdowndate, lowrankdowndate!, lowrankupdate, lowrankupdate!, lu, lu!, lyap, mul!, lmul!, rmul!, norm, normalize, normalize!, nullspace, ordschur!, ordschur, pinv, qr, qr!, lq, lq!, opnorm, rank, rdiv!, reflect!, rotate!, schur, schur!, svd, svd!, svdvals!, svdvals, sylvester, tr, transpose, transpose!, tril, triu, tril!, triu!, # Operators \, /, # Constants I const BlasFloat = Union{Float64,Float32,ComplexF64,ComplexF32} const BlasReal = Union{Float64,Float32} const BlasComplex = Union{ComplexF64,ComplexF32} if USE_BLAS64 const BlasInt = Int64 else const BlasInt = Int32 end abstract type Algorithm end struct DivideAndConquer <: Algorithm end struct QRIteration <: Algorithm end abstract type PivotingStrategy end struct NoPivot <: PivotingStrategy end struct RowNonZero <: PivotingStrategy end struct RowMaximum <: PivotingStrategy end struct ColumnNorm <: PivotingStrategy end # Check that stride of matrix/vector is 1 # Writing like this to avoid splatting penalty when called with multiple arguments, # see PR 16416 """ stride1(A) -> Int Return the distance between successive array elements in dimension 1 in units of element size. # Examples ```jldoctest julia> A = [1,2,3,4] 4-element Vector{Int64}: 1 2 3 4 julia> LinearAlgebra.stride1(A) 1 julia> B = view(A, 2:2:4) 2-element view(::Vector{Int64}, 2:2:4) with eltype Int64: 2 4 julia> LinearAlgebra.stride1(B) 2 ``` """ stride1(x) = stride(x,1) stride1(x::Array) = 1 stride1(x::DenseArray) = stride(x, 1)::Int @inline chkstride1(A...) = _chkstride1(true, A...) @noinline _chkstride1(ok::Bool) = ok || error("matrix does not have contiguous columns") @inline _chkstride1(ok::Bool, A, B...) = _chkstride1(ok & (stride1(A) == 1), B...) """ LinearAlgebra.checksquare(A) Check that a matrix is square, then return its common dimension. For multiple arguments, return a vector. # Examples ```jldoctest julia> A = fill(1, (4,4)); B = fill(1, (5,5)); julia> LinearAlgebra.checksquare(A, B) 2-element Vector{Int64}: 4 5 ``` """ function checksquare(A) m,n = size(A) m == n || throw(DimensionMismatch("matrix is not square: dimensions are $(size(A))")) m end function checksquare(A...) sizes = Int[] for a in A size(a,1)==size(a,2) || throw(DimensionMismatch("matrix is not square: dimensions are $(size(a))")) push!(sizes, size(a,1)) end return sizes end function char_uplo(uplo::Symbol) if uplo === :U return 'U' elseif uplo === :L return 'L' else throw_uplo() end end function sym_uplo(uplo::Char) if uplo == 'U' return :U elseif uplo == 'L' return :L else throw_uplo() end end @noinline throw_uplo() = throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)")) """ ldiv!(Y, A, B) -> Y Compute `A \\ B` in-place and store the result in `Y`, returning the result. The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). The reason for this is that factorization itself is both expensive and typically allocates memory (although it can also be done in-place via, e.g., [`lu!`](@ref)), and performance-critical situations requiring `ldiv!` usually also require fine-grained control over the factorization of `A`. !!! note Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as these are already in a factorized form # Examples ```jldoctest julia> A = [1 2.2 4; 3.1 0.2 3; 4 1 2]; julia> X = [1; 2.5; 3]; julia> Y = zero(X); julia> ldiv!(Y, qr(A), X); julia> Y 3-element Vector{Float64}: 0.7128099173553719 -0.051652892561983674 0.10020661157024757 julia> A\\X 3-element Vector{Float64}: 0.7128099173553719 -0.05165289256198333 0.10020661157024785 ``` """ ldiv!(Y, A, B) """ ldiv!(A, B) Compute `A \\ B` in-place and overwriting `B` to store the result. The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). The reason for this is that factorization itself is both expensive and typically allocates memory (although it can also be done in-place via, e.g., [`lu!`](@ref)), and performance-critical situations requiring `ldiv!` usually also require fine-grained control over the factorization of `A`. !!! note Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as these are already in a factorized form # Examples ```jldoctest julia> A = [1 2.2 4; 3.1 0.2 3; 4 1 2]; julia> X = [1; 2.5; 3]; julia> Y = copy(X); julia> ldiv!(qr(A), X); julia> X 3-element Vector{Float64}: 0.7128099173553719 -0.051652892561983674 0.10020661157024757 julia> A\\Y 3-element Vector{Float64}: 0.7128099173553719 -0.05165289256198333 0.10020661157024785 ``` """ ldiv!(A, B) """ rdiv!(A, B) Compute `A / B` in-place and overwriting `A` to store the result. The argument `B` should *not* be a matrix. Rather, instead of matrices it should be a factorization object (e.g. produced by [`factorize`](@ref) or [`cholesky`](@ref)). The reason for this is that factorization itself is both expensive and typically allocates memory (although it can also be done in-place via, e.g., [`lu!`](@ref)), and performance-critical situations requiring `rdiv!` usually also require fine-grained control over the factorization of `B`. !!! note Certain structured matrix types, such as `Diagonal` and `UpperTriangular`, are permitted, as these are already in a factorized form """ rdiv!(A, B) """ copy_oftype(A, T) Creates a copy of `A` with eltype `T`. No assertions about mutability of the result are made. When `eltype(A) == T`, then this calls `copy(A)` which may be overloaded for custom array types. Otherwise, this calls `convert(AbstractArray{T}, A)`. """ copy_oftype(A::AbstractArray{T}, ::Type{T}) where {T} = copy(A) copy_oftype(A::AbstractArray{T,N}, ::Type{S}) where {T,N,S} = convert(AbstractArray{S,N}, A) """ copymutable_oftype(A, T) Copy `A` to a mutable array with eltype `T` based on `similar(A, T)`. The resulting matrix typically has similar algebraic structure as `A`. For example, supplying a tridiagonal matrix results in another tridiagonal matrix. In general, the type of the output corresponds to that of `similar(A, T)`. In LinearAlgebra, mutable copies (of some desired eltype) are created to be passed to in-place algorithms (such as `ldiv!`, `rdiv!`, `lu!` and so on). If the specific algorithm is known to preserve the algebraic structure, use `copymutable_oftype`. If the algorithm is known to return a dense matrix (or some wrapper backed by a dense matrix), then use `copy_similar`. See also: `Base.copymutable`, `copy_similar`. """ copymutable_oftype(A::AbstractArray, ::Type{S}) where {S} = copyto!(similar(A, S), A) """ copy_similar(A, T) Copy `A` to a mutable array with eltype `T` based on `similar(A, T, size(A))`. Compared to `copymutable_oftype`, the result can be more flexible. In general, the type of the output corresponds to that of the three-argument method `similar(A, T, size(A))`. See also: `copymutable_oftype`. """ copy_similar(A::AbstractArray, ::Type{T}) where {T} = copyto!(similar(A, T, size(A)), A) include("adjtrans.jl") include("transpose.jl") include("exceptions.jl") include("generic.jl") include("blas.jl") include("matmul.jl") include("lapack.jl") include("dense.jl") include("tridiag.jl") include("triangular.jl") include("factorization.jl") include("eigen.jl") include("svd.jl") include("symmetric.jl") include("cholesky.jl") include("lu.jl") include("bunchkaufman.jl") include("diagonal.jl") include("symmetriceigen.jl") include("bidiag.jl") include("uniformscaling.jl") include("qr.jl") include("lq.jl") include("hessenberg.jl") include("abstractq.jl") include("givens.jl") include("special.jl") include("bitarray.jl") include("ldlt.jl") include("schur.jl") include("structuredbroadcast.jl") include("deprecated.jl") const โ‹… = dot const ร— = cross export โ‹…, ร— wrapper_char(::AbstractArray) = 'N' wrapper_char(::Adjoint) = 'C' wrapper_char(::Adjoint{<:Real}) = 'T' wrapper_char(::Transpose) = 'T' wrapper_char(A::Hermitian) = A.uplo == 'U' ? 'H' : 'h' wrapper_char(A::Hermitian{<:Real}) = A.uplo == 'U' ? 'S' : 's' wrapper_char(A::Symmetric) = A.uplo == 'U' ? 'S' : 's' Base.@constprop :aggressive function wrap(A::AbstractVecOrMat, tA::AbstractChar) if tA == 'N' return A elseif tA == 'T' return transpose(A) elseif tA == 'C' return adjoint(A) elseif tA == 'H' return Hermitian(A, :U) elseif tA == 'h' return Hermitian(A, :L) elseif tA == 'S' return Symmetric(A, :U) else # tA == 's' return Symmetric(A, :L) end end _unwrap(A::AbstractVecOrMat) = A ## convenience methods ## return only the solution of a least squares problem while avoiding promoting ## vectors to matrices. _cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x _cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X # SymTridiagonal ev can be the same length as dv, but the last element is # ignored. However, some methods can fail if they read the entire ev # rather than just the meaningful elements. This is a helper function # for getting only the meaningful elements of ev. See #41089 _evview(S::SymTridiagonal) = @view S.ev[begin:begin + length(S.dv) - 2] ## append right hand side with zeros if necessary _zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n)) _zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2)) # convert to Vector, if necessary _makevector(x::Vector) = x _makevector(x::AbstractVector) = Vector(x) # append a zero element / drop the last element _pushzero(A) = (B = similar(A, length(A)+1); @inbounds B[begin:end-1] .= A; @inbounds B[end] = zero(eltype(B)); B) _droplast!(A) = deleteat!(A, lastindex(A)) # some trait like this would be cool # onedefined(::Type{T}) where {T} = hasmethod(one, (T,)) # but we are actually asking for oneunit(T), that is, however, defined for generic T as # `T(one(T))`, so the question is equivalent for whether one(T) is defined onedefined(::Type) = false onedefined(::Type{<:Number}) = true # initialize return array for op(A, B) _init_eltype(::typeof(*), ::Type{TA}, ::Type{TB}) where {TA,TB} = (onedefined(TA) && onedefined(TB)) ? typeof(matprod(oneunit(TA), oneunit(TB))) : promote_op(matprod, TA, TB) _init_eltype(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = (onedefined(TA) && onedefined(TB)) ? typeof(op(oneunit(TA), oneunit(TB))) : promote_op(op, TA, TB) _initarray(op, ::Type{TA}, ::Type{TB}, C) where {TA,TB} = similar(C, _init_eltype(op, TA, TB), size(C)) # General fallback definition for handling under- and overdetermined system as well as square problems # While this definition is pretty general, it does e.g. promote to common element type of lhs and rhs # which is required by LAPACK but not SuiteSparse which allows real-complex solves in some cases. Hence, # we restrict this method to only the LAPACK factorizations in LinearAlgebra. # The definition is put here since it explicitly references all the Factorization structs so it has # to be located after all the files that define the structs. const LAPACKFactorizations{T,S} = Union{ BunchKaufman{T,S}, Cholesky{T,S}, LQ{T,S}, LU{T,S}, QR{T,S}, QRCompactWY{T,S}, QRPivoted{T,S}, SVD{T,<:Real,S}} (\)(F::LAPACKFactorizations, B::AbstractVecOrMat) = ldiv(F, B) (\)(F::AdjointFactorization{<:Any,<:LAPACKFactorizations}, B::AbstractVecOrMat) = ldiv(F, B) (\)(F::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) = ldiv(F, B) function ldiv(F::Factorization, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(F) if m != size(B, 1) throw(DimensionMismatch("arguments must have the same number of rows")) end TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) FF = Factorization{TFB}(F) # For wide problem we (often) compute a minimum norm solution. The solution # is larger than the right hand side so we use size(F, 2). BB = _zeros(TFB, B, n) if n > size(B, 1) # Underdetermined copyto!(view(BB, 1:m, :), B) else copyto!(BB, B) end ldiv!(FF, BB) # For tall problems, we compute a least squares solution so only part # of the rhs should be returned from \ while ldiv! uses (and returns) # the complete rhs return _cut_B(BB, 1:n) end # disambiguate (\)(F::LAPACKFactorizations{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = @invoke \(F::Factorization{T}, B::VecOrMat{Complex{T}}) (\)(F::AdjointFactorization{T,<:LAPACKFactorizations}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = ldiv(F, B) (\)(F::TransposeFactorization{T,<:LU}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = ldiv(F, B) """ LinearAlgebra.peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) `peakflops` computes the peak flop rate of the computer by using double precision [`gemm!`](@ref LinearAlgebra.BLAS.gemm!). By default, if no arguments are specified, it multiplies two `Float64` matrices of size `n x n`, where `n = 4096`. If the underlying BLAS is using multiple threads, higher flop rates are realized. The number of BLAS threads can be set with [`BLAS.set_num_threads(n)`](@ref). If the keyword argument `eltype` is provided, `peakflops` will construct matrices with elements of type `eltype` for calculating the peak flop rate. By default, `peakflops` will use the best timing from 3 trials. If the `ntrials` keyword argument is provided, `peakflops` will use those many trials for picking the best timing. If the keyword argument `parallel` is set to `true`, `peakflops` is run in parallel on all the worker processors. The flop rate of the entire parallel computer is returned. When running in parallel, only 1 BLAS thread is used. The argument `n` still refers to the size of the problem that is solved on each processor. !!! compat "Julia 1.1" This function requires at least Julia 1.1. In Julia 1.0 it is available from the standard library `InteractiveUtils`. """ function peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) t = zeros(Float64, ntrials) for i=1:ntrials a = ones(eltype,n,n) t[i] = @elapsed a2 = a*a @assert a2[1,1] == n end if parallel let Distributed = Base.require(Base.PkgId( Base.UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) nworkers = @invokelatest Distributed.nworkers() results = @invokelatest Distributed.pmap(peakflops, fill(n, nworkers)) return sum(results) end else return 2*Float64(n)^3 / minimum(t) end end function versioninfo(io::IO=stdout) indent = " " config = BLAS.get_config() build_flags = join(string.(config.build_flags), ", ") println(io, "BLAS: ", BLAS.libblastrampoline, " (", build_flags, ")") for lib in config.loaded_libs interface = uppercase(string(lib.interface)) println(io, indent, "--> ", lib.libname, " (", interface, ")") end println(io, "Threading:") println(io, indent, "Threads.threadpoolsize() = ", Threads.threadpoolsize()) println(io, indent, "Threads.maxthreadid() = ", Base.Threads.maxthreadid()) println(io, indent, "LinearAlgebra.BLAS.get_num_threads() = ", BLAS.get_num_threads()) println(io, "Relevant environment variables:") env_var_names = [ "JULIA_NUM_THREADS", "MKL_DYNAMIC", "MKL_NUM_THREADS", # OpenBLAS has a hierarchy of environment variables for setting the # number of threads, see # https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables ("OPENBLAS_NUM_THREADS", "GOTO_NUM_THREADS", "OMP_NUM_THREADS"), ] printed_at_least_one_env_var = false print_var(io, indent, name) = println(io, indent, name, " = ", ENV[name]) for name in env_var_names if name isa Tuple # If `name` is a Tuple, then find the first environment which is # defined, and disregard the following ones. for nm in name if haskey(ENV, nm) print_var(io, indent, nm) printed_at_least_one_env_var = true break end end else if haskey(ENV, name) print_var(io, indent, name) printed_at_least_one_env_var = true end end end if !printed_at_least_one_env_var println(io, indent, "[none]") end return nothing end function __init__() try BLAS.lbt_forward(OpenBLAS_jll.libopenblas_path; clear=true) BLAS.check() catch ex Base.showerror_nostdio(ex, "WARNING: Error during initialization of module LinearAlgebra") end # register a hook to disable BLAS threading Base.at_disable_library_threading(() -> BLAS.set_num_threads(1)) # https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "GOTO_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS") @static if Sys.isapple() && Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "aarch64" BLAS.set_num_threads(max(1, Sys.CPU_THREADS)) else BLAS.set_num_threads(max(1, Sys.CPU_THREADS รท 2)) end end end end # module LinearAlgebra |/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/OpenBLAS_jll/src/OpenBLAS_jll.jl™ # This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/OpenBLAS_jll.jl baremodule OpenBLAS_jll using Base, Libdl, Base.BinaryPlatforms # We are explicitly NOT loading this at runtime, as it contains `libgomp` # which conflicts with `libiomp5`, breaking things like MKL. In the future, # we hope to transition to a JLL interface that provides a more granular # interface than eagerly dlopen'ing all libraries provided in the JLL # which will eliminate issues like this, where we avoid loading a JLL # because we don't want to load a library that we don't even use yet. # using CompilerSupportLibraries_jll # Because of this however, we have to manually load the libraries we # _do_ care about, namely libgfortran Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libopenblas # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libopenblas_handle::Ptr{Cvoid} = C_NULL libopenblas_path::String = "" if Base.USE_BLAS64 const libsuffix = "64_" else const libsuffix = "" end if Sys.iswindows() const libopenblas = "libopenblas$(libsuffix).dll" const _libgfortran = string("libgfortran-", libgfortran_version(HostPlatform()).major, ".dll") elseif Sys.isapple() const libopenblas = "@rpath/libopenblas$(libsuffix).dylib" const _libgfortran = string("@rpath/", "libgfortran.", libgfortran_version(HostPlatform()).major, ".dylib") else const libopenblas = "libopenblas$(libsuffix).so" const _libgfortran = string("libgfortran.so.", libgfortran_version(HostPlatform()).major) end function __init__() # make sure OpenBLAS does not set CPU affinity (#1070, #9639) if !haskey(ENV, "OPENBLAS_MAIN_FREE") ENV["OPENBLAS_MAIN_FREE"] = "1" end # Ensure that OpenBLAS does not grab a huge amount of memory at first, # since it instantly allocates scratch buffer space for the number of # threads it thinks it needs to use. # X-ref: https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables # X-ref: https://github.com/JuliaLang/julia/issues/45434 if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "GOTO_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS") # We set this to `1` here, and then LinearAlgebra will update # to the true value in its `__init__()` function. ENV["OPENBLAS_DEFAULT_NUM_THREADS"] = "1" end # As mentioned above, we are sneaking this in here so that we don't have to # depend on CSL_jll and load _all_ of its libraries. dlopen(_libgfortran) global libopenblas_handle = dlopen(libopenblas) global libopenblas_path = dlpath(libopenblas_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libopenblas_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libopenblas_path() = libopenblas_path end # module OpenBLAS_jll Ž/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/libblastrampoline_jll/src/libblastrampoline_jll.jl# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/libblastrampoline_jll.jl baremodule libblastrampoline_jll using Base, Libdl Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libblastrampoline # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libblastrampoline_handle::Ptr{Cvoid} = C_NULL libblastrampoline_path::String = "" # NOTE: keep in sync with `Base.libblas_name` and `Base.liblapack_name`. const libblastrampoline = if Sys.iswindows() "libblastrampoline-5.dll" elseif Sys.isapple() "@rpath/libblastrampoline.5.dylib" else "libblastrampoline.so.5" end function __init__() global libblastrampoline_handle = dlopen(libblastrampoline) global libblastrampoline_path = dlpath(libblastrampoline_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libblastrampoline_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libblastrampoline_path() = libblastrampoline_path end # module libblastrampoline_jll y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/adjtrans.jlO# This file is a part of Julia. License is MIT: https://julialang.org/license ### basic definitions (types, aliases, constructors, abstractarray interface, sundry similar) # note that Adjoint and Transpose must be able to wrap not only vectors and matrices # but also factorizations, rotations, and other linear algebra objects, including # user-defined such objects. so do not restrict the wrapped type. """ Adjoint Lazy wrapper type for an adjoint view of the underlying linear algebra object, usually an `AbstractVector`/`AbstractMatrix`. Usually, the `Adjoint` constructor should not be called directly, use [`adjoint`](@ref) instead. To materialize the view use [`copy`](@ref). This type is intended for linear algebra usage - for general data manipulation see [`permutedims`](@ref Base.permutedims). # Examples ```jldoctest julia> A = [3+2im 9+2im; 0 0] 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 0+0im 0+0im julia> Adjoint(A) 2ร—2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: 3-2im 0+0im 9-2im 0+0im ``` """ struct Adjoint{T,S} <: AbstractMatrix{T} parent::S end """ Transpose Lazy wrapper type for a transpose view of the underlying linear algebra object, usually an `AbstractVector`/`AbstractMatrix`. Usually, the `Transpose` constructor should not be called directly, use [`transpose`](@ref) instead. To materialize the view use [`copy`](@ref). This type is intended for linear algebra usage - for general data manipulation see [`permutedims`](@ref Base.permutedims). # Examples ```jldoctest julia> A = [2 3; 0 0] 2ร—2 Matrix{Int64}: 2 3 0 0 julia> Transpose(A) 2ร—2 transpose(::Matrix{Int64}) with eltype Int64: 2 0 3 0 ``` """ struct Transpose{T,S} <: AbstractMatrix{T} parent::S end # basic outer constructors Adjoint(A) = Adjoint{Base.promote_op(adjoint,eltype(A)),typeof(A)}(A) Transpose(A) = Transpose{Base.promote_op(transpose,eltype(A)),typeof(A)}(A) """ inplace_adj_or_trans(::AbstractArray) -> adjoint!|transpose!|copyto! inplace_adj_or_trans(::Type{<:AbstractArray}) -> adjoint!|transpose!|copyto! Return [`adjoint!`](@ref) from an `Adjoint` type or object and [`transpose!`](@ref) from a `Transpose` type or object. Otherwise, return [`copyto!`](@ref). Note that `Adjoint` and `Transpose` have to be the outer-most wrapper object for a non-`identity` function to be returned. """ inplace_adj_or_trans(::T) where {T <: AbstractArray} = inplace_adj_or_trans(T) inplace_adj_or_trans(::Type{<:AbstractArray}) = copyto! inplace_adj_or_trans(::Type{<:Adjoint}) = adjoint! inplace_adj_or_trans(::Type{<:Transpose}) = transpose! # unwraps Adjoint, Transpose, Symmetric, Hermitian _unwrap(A::Adjoint) = parent(A) _unwrap(A::Transpose) = parent(A) # unwraps Adjoint and Transpose only _unwrap_at(A) = A _unwrap_at(A::Adjoint) = parent(A) _unwrap_at(A::Transpose) = parent(A) Base.dataids(A::Union{Adjoint, Transpose}) = Base.dataids(A.parent) Base.unaliascopy(A::Union{Adjoint,Transpose}) = typeof(A)(Base.unaliascopy(A.parent)) # wrapping lowercase quasi-constructors """ A' adjoint(A) Lazy adjoint (conjugate transposition). Note that `adjoint` is applied recursively to elements. For number types, `adjoint` returns the complex conjugate, and therefore it is equivalent to the identity function for real numbers. This operation is intended for linear algebra usage - for general data manipulation see [`permutedims`](@ref Base.permutedims). # Examples ```jldoctest julia> A = [3+2im 9+2im; 0 0] 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 0+0im 0+0im julia> B = A' # equivalently adjoint(A) 2ร—2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: 3-2im 0+0im 9-2im 0+0im julia> B isa Adjoint true julia> adjoint(B) === A # the adjoint of an adjoint unwraps the parent true julia> Adjoint(B) # however, the constructor always wraps its argument 2ร—2 adjoint(adjoint(::Matrix{Complex{Int64}})) with eltype Complex{Int64}: 3+2im 9+2im 0+0im 0+0im julia> B[1,2] = 4 + 5im; # modifying B will modify A automatically julia> A 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 4-5im 0+0im ``` For real matrices, the `adjoint` operation is equivalent to a `transpose`. ```jldoctest julia> A = reshape([x for x in 1:4], 2, 2) 2ร—2 Matrix{Int64}: 1 3 2 4 julia> A' 2ร—2 adjoint(::Matrix{Int64}) with eltype Int64: 1 2 3 4 julia> adjoint(A) == transpose(A) true ``` The adjoint of an `AbstractVector` is a row-vector: ```jldoctest julia> x = [3, 4im] 2-element Vector{Complex{Int64}}: 3 + 0im 0 + 4im julia> x' 1ร—2 adjoint(::Vector{Complex{Int64}}) with eltype Complex{Int64}: 3+0im 0-4im julia> x'x # compute the dot product, equivalently x' * x 25 + 0im ``` For a matrix of matrices, the individual blocks are recursively operated on: ```jldoctest julia> A = reshape([x + im*x for x in 1:4], 2, 2) 2ร—2 Matrix{Complex{Int64}}: 1+1im 3+3im 2+2im 4+4im julia> C = reshape([A, 2A, 3A, 4A], 2, 2) 2ร—2 Matrix{Matrix{Complex{Int64}}}: [1+1im 3+3im; 2+2im 4+4im] [3+3im 9+9im; 6+6im 12+12im] [2+2im 6+6im; 4+4im 8+8im] [4+4im 12+12im; 8+8im 16+16im] julia> C' 2ร—2 adjoint(::Matrix{Matrix{Complex{Int64}}}) with eltype Adjoint{Complex{Int64}, Matrix{Complex{Int64}}}: [1-1im 2-2im; 3-3im 4-4im] [2-2im 4-4im; 6-6im 8-8im] [3-3im 6-6im; 9-9im 12-12im] [4-4im 8-8im; 12-12im 16-16im] ``` """ adjoint(A::AbstractVecOrMat) = Adjoint(A) """ transpose(A) Lazy transpose. Mutating the returned object should appropriately mutate `A`. Often, but not always, yields `Transpose(A)`, where `Transpose` is a lazy transpose wrapper. Note that this operation is recursive. This operation is intended for linear algebra usage - for general data manipulation see [`permutedims`](@ref Base.permutedims), which is non-recursive. # Examples ```jldoctest julia> A = [3 2; 0 0] 2ร—2 Matrix{Int64}: 3 2 0 0 julia> B = transpose(A) 2ร—2 transpose(::Matrix{Int64}) with eltype Int64: 3 0 2 0 julia> B isa Transpose true julia> transpose(B) === A # the transpose of a transpose unwraps the parent true julia> Transpose(B) # however, the constructor always wraps its argument 2ร—2 transpose(transpose(::Matrix{Int64})) with eltype Int64: 3 2 0 0 julia> B[1,2] = 4; # modifying B will modify A automatically julia> A 2ร—2 Matrix{Int64}: 3 2 4 0 ``` For complex matrices, the `adjoint` operation is equivalent to a conjugate-transpose. ```jldoctest julia> A = reshape([Complex(x, x) for x in 1:4], 2, 2) 2ร—2 Matrix{Complex{Int64}}: 1+1im 3+3im 2+2im 4+4im julia> adjoint(A) == conj(transpose(A)) true ``` The `transpose` of an `AbstractVector` is a row-vector: ```jldoctest julia> v = [1,2,3] 3-element Vector{Int64}: 1 2 3 julia> transpose(v) # returns a row-vector 1ร—3 transpose(::Vector{Int64}) with eltype Int64: 1 2 3 julia> transpose(v) * v # compute the dot product 14 ``` For a matrix of matrices, the individual blocks are recursively operated on: ```jldoctest julia> C = [1 3; 2 4] 2ร—2 Matrix{Int64}: 1 3 2 4 julia> D = reshape([C, 2C, 3C, 4C], 2, 2) # construct a block matrix 2ร—2 Matrix{Matrix{Int64}}: [1 3; 2 4] [3 9; 6 12] [2 6; 4 8] [4 12; 8 16] julia> transpose(D) # blocks are recursively transposed 2ร—2 transpose(::Matrix{Matrix{Int64}}) with eltype Transpose{Int64, Matrix{Int64}}: [1 2; 3 4] [2 4; 6 8] [3 6; 9 12] [4 8; 12 16] ``` """ transpose(A::AbstractVecOrMat) = Transpose(A) # unwrapping lowercase quasi-constructors adjoint(A::Adjoint) = A.parent transpose(A::Transpose) = A.parent adjoint(A::Transpose{<:Real}) = A.parent transpose(A::Adjoint{<:Real}) = A.parent # printing function Base.showarg(io::IO, v::Adjoint, toplevel) print(io, "adjoint(") Base.showarg(io, parent(v), false) print(io, ')') toplevel && print(io, " with eltype ", eltype(v)) return nothing end function Base.showarg(io::IO, v::Transpose, toplevel) print(io, "transpose(") Base.showarg(io, parent(v), false) print(io, ')') toplevel && print(io, " with eltype ", eltype(v)) return nothing end # some aliases for internal convenience use const AdjOrTrans{T,S} = Union{Adjoint{T,S},Transpose{T,S}} where {T,S} const AdjointAbsVec{T} = Adjoint{T,<:AbstractVector} const AdjointAbsMat{T} = Adjoint{T,<:AbstractMatrix} const TransposeAbsVec{T} = Transpose{T,<:AbstractVector} const TransposeAbsMat{T} = Transpose{T,<:AbstractMatrix} const AdjOrTransAbsVec{T} = AdjOrTrans{T,<:AbstractVector} const AdjOrTransAbsMat{T} = AdjOrTrans{T,<:AbstractMatrix} # for internal use below wrapperop(_) = identity wrapperop(::Adjoint) = adjoint wrapperop(::Transpose) = transpose # the following fallbacks can be removed if Adjoint/Transpose are restricted to AbstractVecOrMat size(A::AdjOrTrans) = reverse(size(A.parent)) axes(A::AdjOrTrans) = reverse(axes(A.parent)) # AbstractArray interface, basic definitions length(A::AdjOrTrans) = length(A.parent) size(v::AdjOrTransAbsVec) = (1, length(v.parent)) size(A::AdjOrTransAbsMat) = reverse(size(A.parent)) axes(v::AdjOrTransAbsVec) = (Base.OneTo(1), axes(v.parent)...) axes(A::AdjOrTransAbsMat) = reverse(axes(A.parent)) IndexStyle(::Type{<:AdjOrTransAbsVec}) = IndexLinear() IndexStyle(::Type{<:AdjOrTransAbsMat}) = IndexCartesian() @propagate_inbounds Base.isassigned(v::AdjOrTransAbsVec, i::Int) = isassigned(v.parent, i-1+first(axes(v.parent)[1])) @propagate_inbounds Base.isassigned(v::AdjOrTransAbsMat, i::Int, j::Int) = isassigned(v.parent, j, i) @propagate_inbounds getindex(v::AdjOrTransAbsVec{T}, i::Int) where {T} = wrapperop(v)(v.parent[i-1+first(axes(v.parent)[1])])::T @propagate_inbounds getindex(A::AdjOrTransAbsMat{T}, i::Int, j::Int) where {T} = wrapperop(A)(A.parent[j, i])::T @propagate_inbounds setindex!(v::AdjOrTransAbsVec, x, i::Int) = (setindex!(v.parent, wrapperop(v)(x), i-1+first(axes(v.parent)[1])); v) @propagate_inbounds setindex!(A::AdjOrTransAbsMat, x, i::Int, j::Int) = (setindex!(A.parent, wrapperop(A)(x), j, i); A) # AbstractArray interface, additional definitions to retain wrapper over vectors where appropriate @propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, is::AbstractArray{Int}) = wrapperop(v)(v.parent[is]) @propagate_inbounds getindex(v::AdjOrTransAbsVec, ::Colon, ::Colon) = wrapperop(v)(v.parent[:]) # conversion of underlying storage convert(::Type{Adjoint{T,S}}, A::Adjoint) where {T,S} = Adjoint{T,S}(convert(S, A.parent))::Adjoint{T,S} convert(::Type{Transpose{T,S}}, A::Transpose) where {T,S} = Transpose{T,S}(convert(S, A.parent))::Transpose{T,S} # Strides and pointer for transposed strided arrays โ€” but only if the elements are actually stored in memory Base.strides(A::Adjoint{<:Real, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1)) Base.strides(A::Transpose{<:Any, <:AbstractVector}) = (stride(A.parent, 2), stride(A.parent, 1)) # For matrices it's slightly faster to use reverse and avoid calling stride twice Base.strides(A::Adjoint{<:Real, <:AbstractMatrix}) = reverse(strides(A.parent)) Base.strides(A::Transpose{<:Any, <:AbstractMatrix}) = reverse(strides(A.parent)) Base.unsafe_convert(::Type{Ptr{T}}, A::Adjoint{<:Real, <:AbstractVecOrMat}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) Base.unsafe_convert(::Type{Ptr{T}}, A::Transpose{<:Any, <:AbstractVecOrMat}) where {T} = Base.unsafe_convert(Ptr{T}, A.parent) Base.elsize(::Type{<:Adjoint{<:Real, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P) Base.elsize(::Type{<:Transpose{<:Any, P}}) where {P<:AbstractVecOrMat} = Base.elsize(P) # for vectors, the semantics of the wrapped and unwrapped types differ # so attempt to maintain both the parent and wrapper type insofar as possible similar(A::AdjOrTransAbsVec) = wrapperop(A)(similar(A.parent)) similar(A::AdjOrTransAbsVec, ::Type{T}) where {T} = wrapperop(A)(similar(A.parent, Base.promote_op(wrapperop(A), T))) # for matrices, the semantics of the wrapped and unwrapped types are generally the same # and as you are allocating with similar anyway, you might as well get something unwrapped similar(A::AdjOrTrans) = similar(A.parent, eltype(A), axes(A)) similar(A::AdjOrTrans, ::Type{T}) where {T} = similar(A.parent, T, axes(A)) similar(A::AdjOrTrans, ::Type{T}, dims::Dims{N}) where {T,N} = similar(A.parent, T, dims) # AbstractMatrix{T} constructor for adjtrans vector: preserve wrapped type AbstractMatrix{T}(A::AdjOrTransAbsVec) where {T} = wrapperop(A)(AbstractVector{T}(A.parent)) # sundry basic definitions parent(A::AdjOrTrans) = A.parent vec(v::TransposeAbsVec{<:Number}) = parent(v) vec(v::AdjointAbsVec{<:Real}) = parent(v) ### concatenation # preserve Adjoint/Transpose wrapper around vectors # to retain the associated semantics post-concatenation hcat(avs::Union{Number,AdjointAbsVec}...) = _adjoint_hcat(avs...) hcat(tvs::Union{Number,TransposeAbsVec}...) = _transpose_hcat(tvs...) _adjoint_hcat(avs::Union{Number,AdjointAbsVec}...) = adjoint(vcat(map(adjoint, avs)...)) _transpose_hcat(tvs::Union{Number,TransposeAbsVec}...) = transpose(vcat(map(transpose, tvs)...)) typed_hcat(::Type{T}, avs::Union{Number,AdjointAbsVec}...) where {T} = adjoint(typed_vcat(T, map(adjoint, avs)...)) typed_hcat(::Type{T}, tvs::Union{Number,TransposeAbsVec}...) where {T} = transpose(typed_vcat(T, map(transpose, tvs)...)) # otherwise-redundant definitions necessary to prevent hitting the concat methods in LinearAlgebra/special.jl hcat(avs::Adjoint{<:Any,<:Vector}...) = _adjoint_hcat(avs...) hcat(tvs::Transpose{<:Any,<:Vector}...) = _transpose_hcat(tvs...) hcat(avs::Adjoint{T,Vector{T}}...) where {T} = _adjoint_hcat(avs...) hcat(tvs::Transpose{T,Vector{T}}...) where {T} = _transpose_hcat(tvs...) # TODO unify and allow mixed combinations ### higher order functions # preserve Adjoint/Transpose wrapper around vectors # to retain the associated semantics post-map/broadcast # # note that the caller's operation f operates in the domain of the wrapped vectors' entries. # hence the adjoint->f->adjoint shenanigans applied to the parent vectors' entries. map(f, avs::AdjointAbsVec...) = adjoint(map((xs...) -> adjoint(f(adjoint.(xs)...)), parent.(avs)...)) map(f, tvs::TransposeAbsVec...) = transpose(map((xs...) -> transpose(f(transpose.(xs)...)), parent.(tvs)...)) quasiparentt(x) = parent(x); quasiparentt(x::Number) = x # to handle numbers in the defs below quasiparenta(x) = parent(x); quasiparenta(x::Number) = conj(x) # to handle numbers in the defs below broadcast(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) broadcast(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) # Hack to preserve behavior after #32122; this needs to be done with a broadcast style instead to support dotted fusion Broadcast.broadcast_preserving_zero_d(f, avs::Union{Number,AdjointAbsVec}...) = adjoint(broadcast((xs...) -> adjoint(f(adjoint.(xs)...)), quasiparenta.(avs)...)) Broadcast.broadcast_preserving_zero_d(f, tvs::Union{Number,TransposeAbsVec}...) = transpose(broadcast((xs...) -> transpose(f(transpose.(xs)...)), quasiparentt.(tvs)...)) # TODO unify and allow mixed combinations with a broadcast style ### reductions # faster to sum the Array than to work through the wrapper (but only in commutative reduction ops as in Base/permuteddimsarray.jl) Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Transpose, dims::Colon) = Base._mapreduce_dim(fโˆ˜transpose, op, init, parent(A), dims) Base._mapreduce_dim(f, op::CommutativeOps, init::Base._InitialValue, A::Adjoint, dims::Colon) = Base._mapreduce_dim(fโˆ˜adjoint, op, init, parent(A), dims) # in prod, use fast path only in the commutative case to avoid surprises Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Transpose{<:Union{Real,Complex}}, dims::Colon) = Base._mapreduce_dim(fโˆ˜transpose, op, init, parent(A), dims) Base._mapreduce_dim(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, init::Base._InitialValue, A::Adjoint{<:Union{Real,Complex}}, dims::Colon) = Base._mapreduce_dim(fโˆ˜adjoint, op, init, parent(A), dims) # count allows for optimization only if the parent array has Bool eltype Base._count(::typeof(identity), A::Transpose{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init) Base._count(::typeof(identity), A::Adjoint{Bool}, ::Colon, init) = Base._count(identity, parent(A), :, init) Base._any(f, A::Transpose, ::Colon) = Base._any(fโˆ˜transpose, parent(A), :) Base._any(f, A::Adjoint, ::Colon) = Base._any(fโˆ˜adjoint, parent(A), :) Base._all(f, A::Transpose, ::Colon) = Base._all(fโˆ˜transpose, parent(A), :) Base._all(f, A::Adjoint, ::Colon) = Base._all(fโˆ˜adjoint, parent(A), :) # sum(A'; dims) Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::TransposeAbsMat) = (Base.mapreducedim!(fโˆ˜transpose, op, switch_dim12(B), parent(A)); B) Base.mapreducedim!(f, op::CommutativeOps, B::AbstractArray, A::AdjointAbsMat) = (Base.mapreducedim!(fโˆ˜adjoint, op, switch_dim12(B), parent(A)); B) Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::TransposeAbsMat{<:Union{Real,Complex}}) = (Base.mapreducedim!(fโˆ˜transpose, op, switch_dim12(B), parent(A)); B) Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*),typeof(Base.mul_prod)}, B::AbstractArray, A::AdjointAbsMat{<:Union{Real,Complex}}) = (Base.mapreducedim!(fโˆ˜adjoint, op, switch_dim12(B), parent(A)); B) switch_dim12(B::AbstractVector) = permutedims(B) switch_dim12(B::AbstractVector{<:Number}) = transpose(B) # avoid allocs due to permutedims switch_dim12(B::AbstractArray{<:Any,0}) = B switch_dim12(B::AbstractArray) = PermutedDimsArray(B, (2, 1, ntuple(Base.Fix1(+,2), ndims(B) - 2)...)) ### linear algebra (-)(A::Adjoint) = Adjoint( -A.parent) (-)(A::Transpose) = Transpose(-A.parent) tr(A::Adjoint) = adjoint(tr(parent(A))) tr(A::Transpose) = transpose(tr(parent(A))) ## multiplication * function _dot_nonrecursive(u, v) lu = length(u) if lu != length(v) throw(DimensionMismatch("first array has length $(lu) which does not match the length of the second, $(length(v)).")) end if lu == 0 zero(eltype(u)) * zero(eltype(v)) else sum(uu*vv for (uu, vv) in zip(u, v)) end end # Adjoint/Transpose-vector * vector *(u::AdjointAbsVec{<:Number}, v::AbstractVector{<:Number}) = dot(u.parent, v) *(u::TransposeAbsVec{T}, v::AbstractVector{T}) where {T<:Real} = dot(u.parent, v) *(u::AdjOrTransAbsVec, v::AbstractVector) = _dot_nonrecursive(u, v) # vector * Adjoint/Transpose-vector *(u::AbstractVector, v::AdjOrTransAbsVec) = broadcast(*, u, v) # Adjoint/Transpose-vector * Adjoint/Transpose-vector # (necessary for disambiguation with fallback methods in linalg/matmul) *(u::AdjointAbsVec, v::AdjointAbsVec) = throw(MethodError(*, (u, v))) *(u::TransposeAbsVec, v::TransposeAbsVec) = throw(MethodError(*, (u, v))) # AdjOrTransAbsVec{<:Any,<:AdjOrTransAbsVec} is a lazy conj vectors # We need to expand the combinations to avoid ambiguities (*)(u::TransposeAbsVec, v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v) (*)(u::AdjointAbsVec, v::AdjointAbsVec{<:Any,<:TransposeAbsVec}) = _dot_nonrecursive(u, v) (*)(u::TransposeAbsVec, v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v) (*)(u::AdjointAbsVec, v::TransposeAbsVec{<:Any,<:AdjointAbsVec}) = _dot_nonrecursive(u, v) ## pseudoinversion pinv(v::AdjointAbsVec, tol::Real = 0) = pinv(v.parent, tol).parent pinv(v::TransposeAbsVec, tol::Real = 0) = pinv(conj(v.parent)).parent ## left-division \ \(u::AdjOrTransAbsVec, v::AdjOrTransAbsVec) = pinv(u) * v ## right-division / /(u::AdjointAbsVec, A::AbstractMatrix) = adjoint(adjoint(A) \ u.parent) /(u::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A) \ u.parent) /(u::AdjointAbsVec, A::TransposeAbsMat) = adjoint(conj(A.parent) \ u.parent) # technically should be adjoint(copy(adjoint(copy(A))) \ u.parent) /(u::TransposeAbsVec, A::AdjointAbsMat) = transpose(conj(A.parent) \ u.parent) # technically should be transpose(copy(transpose(copy(A))) \ u.parent) ## complex conjugate conj(A::Transpose) = adjoint(A.parent) conj(A::Adjoint) = transpose(A.parent) ## structured matrix methods ## function Base.replace_in_print_matrix(A::AdjOrTrans,i::Integer,j::Integer,s::AbstractString) Base.replace_in_print_matrix(parent(A), j, i, s) end z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/transpose.jl # This file is a part of Julia. License is MIT: https://julialang.org/license adjoint(a::AbstractArray) = error("adjoint not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") transpose(a::AbstractArray) = error("transpose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") ## Matrix transposition ## """ transpose!(dest,src) Transpose array `src` and store the result in the preallocated array `dest`, which should have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is supported and unexpected results will happen if `src` and `dest` have overlapping memory regions. # Examples ```jldoctest julia> A = [3+2im 9+2im; 8+7im 4+6im] 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 8+7im 4+6im julia> B = zeros(Complex{Int64}, 2, 2) 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+0im 0+0im 0+0im julia> transpose!(B, A); julia> B 2ร—2 Matrix{Complex{Int64}}: 3+2im 8+7im 9+2im 4+6im julia> A 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 8+7im 4+6im ``` """ transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) """ adjoint!(dest,src) Conjugate transpose array `src` and store the result in the preallocated array `dest`, which should have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is supported and unexpected results will happen if `src` and `dest` have overlapping memory regions. # Examples ```jldoctest julia> A = [3+2im 9+2im; 8+7im 4+6im] 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 8+7im 4+6im julia> B = zeros(Complex{Int64}, 2, 2) 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+0im 0+0im 0+0im julia> adjoint!(B, A); julia> B 2ร—2 Matrix{Complex{Int64}}: 3-2im 8-7im 9-2im 4-6im julia> A 2ร—2 Matrix{Complex{Int64}}: 3+2im 9+2im 8+7im 4+6im ``` """ adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) function transpose!(B::AbstractVector, A::AbstractMatrix) axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) copyto!(B, A) end function transpose!(B::AbstractMatrix, A::AbstractVector) axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) copyto!(B, A) end function adjoint!(B::AbstractVector, A::AbstractMatrix) axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end function adjoint!(B::AbstractMatrix, A::AbstractVector) axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end const transposebaselength=64 function transpose_f!(f, B::AbstractMatrix, A::AbstractMatrix) inds = axes(A) axes(B,1) == inds[2] && axes(B,2) == inds[1] || throw(DimensionMismatch(string(f))) m, n = length(inds[1]), length(inds[2]) if m*n<=4*transposebaselength @inbounds begin for j = inds[2] for i = inds[1] B[j,i] = f(A[i,j]) end end end else transposeblock!(f,B,A,m,n,first(inds[1])-1,first(inds[2])-1) end return B end function transposeblock!(f, B::AbstractMatrix, A::AbstractMatrix, m::Int, n::Int, offseti::Int, offsetj::Int) if m*n<=transposebaselength @inbounds begin for j = offsetj .+ (1:n) for i = offseti .+ (1:m) B[j,i] = f(A[i,j]) end end end elseif m>n newm=m>>1 transposeblock!(f,B,A,newm,n,offseti,offsetj) transposeblock!(f,B,A,m-newm,n,offseti+newm,offsetj) else newn=n>>1 transposeblock!(f,B,A,m,newn,offseti,offsetj) transposeblock!(f,B,A,m,n-newn,offseti,offsetj+newn) end return B end function ccopy!(B, A) RB, RA = eachindex(B), eachindex(A) if RB == RA for i = RB B[i] = adjoint(A[i]) end else for (i,j) = zip(RB, RA) B[i] = adjoint(A[j]) end end return B end """ copy(A::Transpose) copy(A::Adjoint) Eagerly evaluate the lazy matrix transpose/adjoint. Note that the transposition is applied recursively to elements. This operation is intended for linear algebra usage - for general data manipulation see [`permutedims`](@ref Base.permutedims), which is non-recursive. # Examples ```jldoctest julia> A = [1 2im; -3im 4] 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+2im 0-3im 4+0im julia> T = transpose(A) 2ร—2 transpose(::Matrix{Complex{Int64}}) with eltype Complex{Int64}: 1+0im 0-3im 0+2im 4+0im julia> copy(T) 2ร—2 Matrix{Complex{Int64}}: 1+0im 0-3im 0+2im 4+0im ``` """ copy(::Union{Transpose,Adjoint}) Base.copy(A::TransposeAbsMat) = transpose!(similar(A.parent, reverse(axes(A.parent))), A.parent) Base.copy(A::AdjointAbsMat) = adjoint!(similar(A.parent, reverse(axes(A.parent))), A.parent) function copy_transpose!(B::AbstractVecOrMat, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, A::AbstractVecOrMat, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) if length(ir_dest) != length(jr_src) throw(ArgumentError(LazyString("source and destination must have same size (got ", length(jr_src)," and ",length(ir_dest),")"))) end if length(jr_dest) != length(ir_src) throw(ArgumentError(LazyString("source and destination must have same size (got ", length(ir_src)," and ",length(jr_dest),")"))) end @boundscheck checkbounds(B, ir_dest, jr_dest) @boundscheck checkbounds(A, ir_src, jr_src) idest = first(ir_dest) for jsrc in jr_src jdest = first(jr_dest) for isrc in ir_src B[idest,jdest] = A[isrc,jsrc] jdest += step(jr_dest) end idest += step(ir_dest) end return B end function copy_similar(A::AdjointAbsMat, ::Type{T}) where {T} C = similar(A, T, size(A)) adjoint!(C, parent(A)) end function copy_similar(A::TransposeAbsMat, ::Type{T}) where {T} C = similar(A, T, size(A)) transpose!(C, parent(A)) end {/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/exceptions.jl๑# This file is a part of Julia. License is MIT: https://julialang.org/license export LAPACKException, SingularException, PosDefException, RankDeficientException, ZeroPivotException struct LAPACKException <: Exception info::BlasInt end """ SingularException Exception thrown when the input matrix has one or more zero-valued eigenvalues, and is not invertible. A linear solve involving such a matrix cannot be computed. The `info` field indicates the location of (one of) the singular value(s). """ struct SingularException <: Exception info::BlasInt end """ PosDefException Exception thrown when the input matrix was not [positive definite](https://en.wikipedia.org/wiki/Definiteness_of_a_matrix). Some linear algebra functions and factorizations are only applicable to positive definite matrices. The `info` field indicates the location of (one of) the eigenvalue(s) which is (are) less than/equal to 0. """ struct PosDefException <: Exception info::BlasInt end function Base.showerror(io::IO, ex::PosDefException) print(io, "PosDefException: matrix is not ") if ex.info == -1 print(io, "Hermitian") else print(io, "positive definite") end print(io, "; Cholesky factorization failed.") end struct RankDeficientException <: Exception info::BlasInt end """ ZeroPivotException <: Exception Exception thrown when a matrix factorization/solve encounters a zero in a pivot (diagonal) position and cannot proceed. This may *not* mean that the matrix is singular: it may be fruitful to switch to a different factorization such as pivoted LU that can re-order variables to eliminate spurious zero pivots. The `info` field indicates the location of (one of) the zero pivot(s). """ struct ZeroPivotException <: Exception info::BlasInt end function Base.showerror(io::IO, ex::ZeroPivotException) print(io, "ZeroPivotException: factorization encountered one or more zero pivots. Consider switching to a pivoted LU factorization.") end x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/generic.jlJณ# This file is a part of Julia. License is MIT: https://julialang.org/license ## linalg.jl: Some generic Linear Algebra definitions # Elements of `out` may not be defined (e.g., for `BigFloat`). To make # `mul!(out, A, B)` work for such cases, `out .*โ‚› beta` short-circuits # `out * beta`. Using `broadcasted` to avoid the multiplication # inside this function. function *โ‚› end Broadcast.broadcasted(::typeof(*โ‚›), out, beta) = iszero(beta::Number) ? false : broadcasted(*, out, beta) """ MulAddMul(alpha, beta) A callable for operating short-circuiting version of `x * alpha + y * beta`. # Examples ```jldoctest julia> using LinearAlgebra: MulAddMul julia> _add = MulAddMul(1, 0); julia> _add(123, nothing) 123 julia> MulAddMul(12, 34)(56, 78) == 56 * 12 + 78 * 34 true ``` """ struct MulAddMul{ais1, bis0, TA, TB} alpha::TA beta::TB end @inline function MulAddMul(alpha::TA, beta::TB) where {TA,TB} if isone(alpha) if iszero(beta) return MulAddMul{true,true,TA,TB}(alpha, beta) else return MulAddMul{true,false,TA,TB}(alpha, beta) end else if iszero(beta) return MulAddMul{false,true,TA,TB}(alpha, beta) else return MulAddMul{false,false,TA,TB}(alpha, beta) end end end MulAddMul() = MulAddMul{true,true,Bool,Bool}(true, false) @inline (::MulAddMul{true})(x) = x @inline (p::MulAddMul{false})(x) = x * p.alpha @inline (::MulAddMul{true, true})(x, _) = x @inline (p::MulAddMul{false, true})(x, _) = x * p.alpha @inline (p::MulAddMul{true, false})(x, y) = x + y * p.beta @inline (p::MulAddMul{false, false})(x, y) = x * p.alpha + y * p.beta """ _modify!(_add::MulAddMul, x, C, idx) Short-circuiting version of `C[idx] = _add(x, C[idx])`. Short-circuiting the indexing `C[idx]` is necessary for avoiding `UndefRefError` when mutating an array of non-primitive numbers such as `BigFloat`. # Examples ```jldoctest julia> using LinearAlgebra: MulAddMul, _modify! julia> _add = MulAddMul(1, 0); C = Vector{BigFloat}(undef, 1); julia> _modify!(_add, 123, C, 1) julia> C 1-element Vector{BigFloat}: 123.0 ``` """ @inline @propagate_inbounds function _modify!(p::MulAddMul{ais1, bis0}, x, C, idxโ€ฒ) where {ais1, bis0} # `idxโ€ฒ` may be an integer, a tuple of integer, or a `CartesianIndex`. # Let `CartesianIndex` constructor normalize them so that it can be # used uniformly. It also acts as a workaround for performance penalty # of splatting a number (#29114): idx = CartesianIndex(idxโ€ฒ) if bis0 C[idx] = p(x) else C[idx] = p(x, C[idx]) end return end @inline function _rmul_or_fill!(C::AbstractArray, beta::Number) if isempty(C) return C end if iszero(beta) fill!(C, zero(eltype(C))) else rmul!(C, beta) end return C end function generic_mul!(C::AbstractArray, X::AbstractArray, s::Number, _add::MulAddMul) if length(C) != length(X) throw(DimensionMismatch("first array has length $(length(C)) which does not match the length of the second, $(length(X)).")) end for (IC, IX) in zip(eachindex(C), eachindex(X)) @inbounds _modify!(_add, X[IX] * s, C, IC) end C end function generic_mul!(C::AbstractArray, s::Number, X::AbstractArray, _add::MulAddMul) if length(C) != length(X) throw(DimensionMismatch("first array has length $(length(C)) which does not match the length of the second, $(length(X)).")) end for (IC, IX) in zip(eachindex(C), eachindex(X)) @inbounds _modify!(_add, s * X[IX], C, IC) end C end @inline function mul!(C::AbstractArray, s::Number, X::AbstractArray, alpha::Number, beta::Number) if axes(C) == axes(X) C .= (s .* X) .*โ‚› alpha .+ C .*โ‚› beta else generic_mul!(C, s, X, MulAddMul(alpha, beta)) end return C end @inline function mul!(C::AbstractArray, X::AbstractArray, s::Number, alpha::Number, beta::Number) if axes(C) == axes(X) C .= (X .* s) .*โ‚› alpha .+ C .*โ‚› beta else generic_mul!(C, X, s, MulAddMul(alpha, beta)) end return C end # For better performance when input and output are the same array # See https://github.com/JuliaLang/julia/issues/8415#issuecomment-56608729 """ rmul!(A::AbstractArray, b::Number) Scale an array `A` by a scalar `b` overwriting `A` in-place. Use [`lmul!`](@ref) to multiply scalar from left. The scaling operation respects the semantics of the multiplication [`*`](@ref) between an element of `A` and `b`. In particular, this also applies to multiplication involving non-finite numbers such as `NaN` and `ยฑInf`. !!! compat "Julia 1.1" Prior to Julia 1.1, `NaN` and `ยฑInf` entries in `A` were treated inconsistently. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> rmul!(A, 2) 2ร—2 Matrix{Int64}: 2 4 6 8 julia> rmul!([NaN], 0.0) 1-element Vector{Float64}: NaN ``` """ function rmul!(X::AbstractArray, s::Number) @simd for I in eachindex(X) @inbounds X[I] *= s end X end """ lmul!(a::Number, B::AbstractArray) Scale an array `B` by a scalar `a` overwriting `B` in-place. Use [`rmul!`](@ref) to multiply scalar from right. The scaling operation respects the semantics of the multiplication [`*`](@ref) between `a` and an element of `B`. In particular, this also applies to multiplication involving non-finite numbers such as `NaN` and `ยฑInf`. !!! compat "Julia 1.1" Prior to Julia 1.1, `NaN` and `ยฑInf` entries in `B` were treated inconsistently. # Examples ```jldoctest julia> B = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> lmul!(2, B) 2ร—2 Matrix{Int64}: 2 4 6 8 julia> lmul!(0.0, [Inf]) 1-element Vector{Float64}: NaN ``` """ function lmul!(s::Number, X::AbstractArray) @simd for I in eachindex(X) @inbounds X[I] = s*X[I] end X end """ rdiv!(A::AbstractArray, b::Number) Divide each entry in an array `A` by a scalar `b` overwriting `A` in-place. Use [`ldiv!`](@ref) to divide scalar from left. # Examples ```jldoctest julia> A = [1.0 2.0; 3.0 4.0] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> rdiv!(A, 2.0) 2ร—2 Matrix{Float64}: 0.5 1.0 1.5 2.0 ``` """ function rdiv!(X::AbstractArray, s::Number) @simd for I in eachindex(X) @inbounds X[I] /= s end X end """ ldiv!(a::Number, B::AbstractArray) Divide each entry in an array `B` by a scalar `a` overwriting `B` in-place. Use [`rdiv!`](@ref) to divide scalar from right. # Examples ```jldoctest julia> B = [1.0 2.0; 3.0 4.0] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> ldiv!(2.0, B) 2ร—2 Matrix{Float64}: 0.5 1.0 1.5 2.0 ``` """ function ldiv!(s::Number, X::AbstractArray) @simd for I in eachindex(X) @inbounds X[I] = s\X[I] end X end ldiv!(Y::AbstractArray, s::Number, X::AbstractArray) = Y .= s .\ X # Generic fallback. This assumes that B and Y have the same sizes. ldiv!(Y::AbstractArray, A::AbstractMatrix, B::AbstractArray) = ldiv!(A, copyto!(Y, B)) """ cross(x, y) ร—(x,y) Compute the cross product of two 3-vectors. # Examples ```jldoctest julia> a = [0;1;0] 3-element Vector{Int64}: 0 1 0 julia> b = [0;0;1] 3-element Vector{Int64}: 0 0 1 julia> cross(a,b) 3-element Vector{Int64}: 1 0 0 ``` """ function cross(a::AbstractVector, b::AbstractVector) if !(length(a) == length(b) == 3) throw(DimensionMismatch("cross product is only defined for vectors of length 3")) end a1, a2, a3 = a b1, b2, b3 = b [a2*b3-a3*b2, a3*b1-a1*b3, a1*b2-a2*b1] end """ triu(M) Upper triangle of a matrix. # Examples ```jldoctest julia> a = fill(1.0, (4,4)) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 julia> triu(a) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 1.0 ``` """ triu(M::AbstractMatrix) = triu!(copy(M)) """ tril(M) Lower triangle of a matrix. # Examples ```jldoctest julia> a = fill(1.0, (4,4)) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 julia> tril(a) 4ร—4 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 1.0 1.0 1.0 0.0 1.0 1.0 1.0 1.0 ``` """ tril(M::AbstractMatrix) = tril!(copy(M)) """ triu(M, k::Integer) Return the upper triangle of `M` starting from the `k`th superdiagonal. # Examples ```jldoctest julia> a = fill(1.0, (4,4)) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 julia> triu(a,3) 4ร—4 Matrix{Float64}: 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 julia> triu(a,-3) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 ``` """ triu(M::AbstractMatrix,k::Integer) = triu!(copy(M),k) """ tril(M, k::Integer) Return the lower triangle of `M` starting from the `k`th superdiagonal. # Examples ```jldoctest julia> a = fill(1.0, (4,4)) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 julia> tril(a,3) 4ร—4 Matrix{Float64}: 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 julia> tril(a,-3) 4ร—4 Matrix{Float64}: 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 ``` """ tril(M::AbstractMatrix,k::Integer) = tril!(copy(M),k) """ triu!(M) Upper triangle of a matrix, overwriting `M` in the process. See also [`triu`](@ref). """ triu!(M::AbstractMatrix) = triu!(M,0) """ tril!(M) Lower triangle of a matrix, overwriting `M` in the process. See also [`tril`](@ref). """ tril!(M::AbstractMatrix) = tril!(M,0) diag(A::AbstractVector) = throw(ArgumentError("use diagm instead of diag to construct a diagonal matrix")) ########################################################################################### # Dot products and norms # special cases of norm; note that they don't need to handle isempty(x) generic_normMinusInf(x) = float(mapreduce(norm, min, x)) generic_normInf(x) = float(mapreduce(norm, max, x)) generic_norm1(x) = mapreduce(float โˆ˜ norm, +, x) # faster computation of norm(x)^2, avoiding overflow for integers norm_sqr(x) = norm(x)^2 norm_sqr(x::Number) = abs2(x) norm_sqr(x::Union{T,Complex{T},Rational{T}}) where {T<:Integer} = abs2(float(x)) function generic_norm2(x) maxabs = normInf(x) (ismissing(maxabs) || iszero(maxabs) || isinf(maxabs)) && return maxabs (v, s) = iterate(x)::Tuple T = typeof(maxabs) if isfinite(length(x)*maxabs*maxabs) && !iszero(maxabs*maxabs) # Scaling not necessary sum::promote_type(Float64, T) = norm_sqr(v) while true y = iterate(x, s) y === nothing && break (v, s) = y sum += norm_sqr(v) end ismissing(sum) && return missing return convert(T, sqrt(sum)) else sum = abs2(norm(v)/maxabs) while true y = iterate(x, s) y === nothing && break (v, s) = y sum += (norm(v)/maxabs)^2 end ismissing(sum) && return missing return convert(T, maxabs*sqrt(sum)) end end # Compute L_p norm โ€–xโ€–โ‚š = sum(abs(x).^p)^(1/p) # (Not technically a "norm" for p < 1.) function generic_normp(x, p) (v, s) = iterate(x)::Tuple if p > 1 || p < -1 # might need to rescale to avoid overflow maxabs = p > 1 ? normInf(x) : normMinusInf(x) (ismissing(maxabs) || iszero(maxabs) || isinf(maxabs)) && return maxabs T = typeof(maxabs) else T = typeof(float(norm(v))) end spp::promote_type(Float64, T) = p if -1 <= p <= 1 || (isfinite(length(x)*maxabs^spp) && !iszero(maxabs^spp)) # scaling not necessary sum::promote_type(Float64, T) = norm(v)^spp while true y = iterate(x, s) y === nothing && break (v, s) = y ismissing(v) && return missing sum += norm(v)^spp end return convert(T, sum^inv(spp)) else # rescaling sum = (norm(v)/maxabs)^spp ismissing(sum) && return missing while true y = iterate(x, s) y === nothing && break (v, s) = y ismissing(v) && return missing sum += (norm(v)/maxabs)^spp end return convert(T, maxabs*sum^inv(spp)) end end normMinusInf(x) = generic_normMinusInf(x) normInf(x) = generic_normInf(x) norm1(x) = generic_norm1(x) norm2(x) = generic_norm2(x) normp(x, p) = generic_normp(x, p) """ norm(A, p::Real=2) For any iterable container `A` (including arrays of any dimension) of numbers (or any element type for which `norm` is defined), compute the `p`-norm (defaulting to `p=2`) as if `A` were a vector of the corresponding length. The `p`-norm is defined as ```math \\|A\\|_p = \\left( \\sum_{i=1}^n | a_i | ^p \\right)^{1/p} ``` with ``a_i`` the entries of ``A``, ``| a_i |`` the [`norm`](@ref) of ``a_i``, and ``n`` the length of ``A``. Since the `p`-norm is computed using the [`norm`](@ref)s of the entries of `A`, the `p`-norm of a vector of vectors is not compatible with the interpretation of it as a block vector in general if `p != 2`. `p` can assume any numeric value (even though not all values produce a mathematically valid vector norm). In particular, `norm(A, Inf)` returns the largest value in `abs.(A)`, whereas `norm(A, -Inf)` returns the smallest. If `A` is a matrix and `p=2`, then this is equivalent to the Frobenius norm. The second argument `p` is not necessarily a part of the interface for `norm`, i.e. a custom type may only implement `norm(A)` without second argument. Use [`opnorm`](@ref) to compute the operator norm of a matrix. # Examples ```jldoctest julia> v = [3, -2, 6] 3-element Vector{Int64}: 3 -2 6 julia> norm(v) 7.0 julia> norm(v, 1) 11.0 julia> norm(v, Inf) 6.0 julia> norm([1 2 3; 4 5 6; 7 8 9]) 16.881943016134134 julia> norm([1 2 3 4 5 6 7 8 9]) 16.881943016134134 julia> norm(1:9) 16.881943016134134 julia> norm(hcat(v,v), 1) == norm(vcat(v,v), 1) != norm([v,v], 1) true julia> norm(hcat(v,v), 2) == norm(vcat(v,v), 2) == norm([v,v], 2) true julia> norm(hcat(v,v), Inf) == norm(vcat(v,v), Inf) != norm([v,v], Inf) true ``` """ function norm(itr, p::Real=2) isempty(itr) && return float(norm(zero(eltype(itr)))) if p == 2 return norm2(itr) elseif p == 1 return norm1(itr) elseif p == Inf return normInf(itr) elseif p == 0 return typeof(float(norm(first(itr))))(count(!iszero, itr)) elseif p == -Inf return normMinusInf(itr) else normp(itr, p) end end """ norm(x::Number, p::Real=2) For numbers, return ``\\left( |x|^p \\right)^{1/p}``. # Examples ```jldoctest julia> norm(2, 1) 2.0 julia> norm(-2, 1) 2.0 julia> norm(2, 2) 2.0 julia> norm(-2, 2) 2.0 julia> norm(2, Inf) 2.0 julia> norm(-2, Inf) 2.0 ``` """ @inline function norm(x::Number, p::Real=2) afx = abs(float(x)) if p == 0 if iszero(x) return zero(afx) elseif !isnan(x) return oneunit(afx) else return afx end else return afx end end norm(::Missing, p::Real=2) = missing # special cases of opnorm function opnorm1(A::AbstractMatrix{T}) where T require_one_based_indexing(A) m, n = size(A) Tnorm = typeof(float(real(zero(T)))) Tsum = promote_type(Float64, Tnorm) nrm::Tsum = 0 @inbounds begin for j = 1:n nrmj::Tsum = 0 for i = 1:m nrmj += norm(A[i,j]) end nrm = max(nrm,nrmj) end end return convert(Tnorm, nrm) end function opnorm2(A::AbstractMatrix{T}) where T require_one_based_indexing(A) m,n = size(A) Tnorm = typeof(float(real(zero(T)))) if m == 0 || n == 0 return zero(Tnorm) end if m == 1 || n == 1 return norm2(A) end return svdvals(A)[1] end function opnormInf(A::AbstractMatrix{T}) where T require_one_based_indexing(A) m,n = size(A) Tnorm = typeof(float(real(zero(T)))) Tsum = promote_type(Float64, Tnorm) nrm::Tsum = 0 @inbounds begin for i = 1:m nrmi::Tsum = 0 for j = 1:n nrmi += norm(A[i,j]) end nrm = max(nrm,nrmi) end end return convert(Tnorm, nrm) end """ opnorm(A::AbstractMatrix, p::Real=2) Compute the operator norm (or matrix norm) induced by the vector `p`-norm, where valid values of `p` are `1`, `2`, or `Inf`. (Note that for sparse matrices, `p=2` is currently not implemented.) Use [`norm`](@ref) to compute the Frobenius norm. When `p=1`, the operator norm is the maximum absolute column sum of `A`: ```math \\|A\\|_1 = \\max_{1 โ‰ค j โ‰ค n} \\sum_{i=1}^m | a_{ij} | ``` with ``a_{ij}`` the entries of ``A``, and ``m`` and ``n`` its dimensions. When `p=2`, the operator norm is the spectral norm, equal to the largest singular value of `A`. When `p=Inf`, the operator norm is the maximum absolute row sum of `A`: ```math \\|A\\|_\\infty = \\max_{1 โ‰ค i โ‰ค m} \\sum _{j=1}^n | a_{ij} | ``` # Examples ```jldoctest julia> A = [1 -2 -3; 2 3 -1] 2ร—3 Matrix{Int64}: 1 -2 -3 2 3 -1 julia> opnorm(A, Inf) 6.0 julia> opnorm(A, 1) 5.0 ``` """ function opnorm(A::AbstractMatrix, p::Real=2) if p == 2 return opnorm2(A) elseif p == 1 return opnorm1(A) elseif p == Inf return opnormInf(A) else throw(ArgumentError("invalid p-norm p=$p. Valid: 1, 2, Inf")) end end """ opnorm(x::Number, p::Real=2) For numbers, return ``\\left( |x|^p \\right)^{1/p}``. This is equivalent to [`norm`](@ref). """ @inline opnorm(x::Number, p::Real=2) = norm(x, p) """ opnorm(A::Adjoint{<:Any,<:AbstracVector}, q::Real=2) opnorm(A::Transpose{<:Any,<:AbstracVector}, q::Real=2) For Adjoint/Transpose-wrapped vectors, return the operator ``q``-norm of `A`, which is equivalent to the `p`-norm with value `p = q/(q-1)`. They coincide at `p = q = 2`. Use [`norm`](@ref) to compute the `p` norm of `A` as a vector. The difference in norm between a vector space and its dual arises to preserve the relationship between duality and the dot product, and the result is consistent with the operator `p`-norm of a `1 ร— n` matrix. # Examples ```jldoctest julia> v = [1; im]; julia> vc = v'; julia> opnorm(vc, 1) 1.0 julia> norm(vc, 1) 2.0 julia> norm(v, 1) 2.0 julia> opnorm(vc, 2) 1.4142135623730951 julia> norm(vc, 2) 1.4142135623730951 julia> norm(v, 2) 1.4142135623730951 julia> opnorm(vc, Inf) 2.0 julia> norm(vc, Inf) 1.0 julia> norm(v, Inf) 1.0 ``` """ opnorm(v::TransposeAbsVec, q::Real) = q == Inf ? norm(v.parent, 1) : norm(v.parent, q/(q-1)) opnorm(v::AdjointAbsVec, q::Real) = q == Inf ? norm(conj(v.parent), 1) : norm(conj(v.parent), q/(q-1)) opnorm(v::AdjointAbsVec) = norm(conj(v.parent)) opnorm(v::TransposeAbsVec) = norm(v.parent) norm(v::AdjOrTrans, p::Real) = norm(v.parent, p) """ dot(x, y) x โ‹… y Compute the dot product between two vectors. For complex vectors, the first vector is conjugated. `dot` also works on arbitrary iterable objects, including arrays of any dimension, as long as `dot` is defined on the elements. `dot` is semantically equivalent to `sum(dot(vx,vy) for (vx,vy) in zip(x, y))`, with the added restriction that the arguments must have equal lengths. `x โ‹… y` (where `โ‹…` can be typed by tab-completing `\\cdot` in the REPL) is a synonym for `dot(x, y)`. # Examples ```jldoctest julia> dot([1; 1], [2; 3]) 5 julia> dot([im; im], [1; 1]) 0 - 2im julia> dot(1:5, 2:6) 70 julia> x = fill(2., (5,5)); julia> y = fill(3., (5,5)); julia> dot(x, y) 150.0 ``` """ function dot end function dot(x, y) # arbitrary iterables ix = iterate(x) iy = iterate(y) if ix === nothing if iy !== nothing throw(DimensionMismatch("x and y are of different lengths!")) end return dot(zero(eltype(x)), zero(eltype(y))) end if iy === nothing throw(DimensionMismatch("x and y are of different lengths!")) end (vx, xs) = ix (vy, ys) = iy s = dot(vx, vy) while true ix = iterate(x, xs) iy = iterate(y, ys) ix === nothing && break iy === nothing && break (vx, xs), (vy, ys) = ix, iy s += dot(vx, vy) end if !(iy === nothing && ix === nothing) throw(DimensionMismatch("x and y are of different lengths!")) end return s end dot(x::Number, y::Number) = conj(x) * y function dot(x::AbstractArray, y::AbstractArray) lx = length(x) if lx != length(y) throw(DimensionMismatch("first array has length $(lx) which does not match the length of the second, $(length(y)).")) end if lx == 0 return dot(zero(eltype(x)), zero(eltype(y))) end s = zero(dot(first(x), first(y))) for (Ix, Iy) in zip(eachindex(x), eachindex(y)) @inbounds s += dot(x[Ix], y[Iy]) end s end function dot(x::Adjoint{<:Union{Real,Complex}}, y::Adjoint{<:Union{Real,Complex}}) return conj(dot(parent(x), parent(y))) end dot(x::Transpose, y::Transpose) = dot(parent(x), parent(y)) """ dot(x, A, y) Compute the generalized dot product `dot(x, A*y)` between two vectors `x` and `y`, without storing the intermediate result of `A*y`. As for the two-argument [`dot(_,_)`](@ref), this acts recursively. Moreover, for complex vectors, the first vector is conjugated. !!! compat "Julia 1.4" Three-argument `dot` requires at least Julia 1.4. # Examples ```jldoctest julia> dot([1; 1], [1 2; 3 4], [2; 3]) 26 julia> dot(1:5, reshape(1:25, 5, 5), 2:6) 4850 julia> โ‹…(1:5, reshape(1:25, 5, 5), 2:6) == dot(1:5, reshape(1:25, 5, 5), 2:6) true ``` """ dot(x, A, y) = dot(x, A*y) # generic fallback for cases that are not covered by specialized methods function dot(x::AbstractVector, A::AbstractMatrix, y::AbstractVector) (axes(x)..., axes(y)...) == axes(A) || throw(DimensionMismatch()) T = typeof(dot(first(x), first(A), first(y))) s = zero(T) iโ‚ = first(eachindex(x)) xโ‚ = first(x) @inbounds for j in eachindex(y) yj = y[j] if !iszero(yj) temp = zero(adjoint(A[iโ‚,j]) * xโ‚) @simd for i in eachindex(x) temp += adjoint(A[i,j]) * x[i] end s += dot(temp, yj) end end return s end dot(x::AbstractVector, adjA::Adjoint, y::AbstractVector) = adjoint(dot(y, adjA.parent, x)) dot(x::AbstractVector, transA::Transpose{<:Real}, y::AbstractVector) = adjoint(dot(y, transA.parent, x)) ########################################################################################### """ rank(A::AbstractMatrix; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ฯต) rank(A::AbstractMatrix, rtol::Real) Compute the numerical rank of a matrix by counting how many outputs of `svdvals(A)` are greater than `max(atol, rtol*ฯƒโ‚)` where `ฯƒโ‚` is `A`'s largest calculated singular value. `atol` and `rtol` are the absolute and relative tolerances, respectively. The default relative tolerance is `n*ฯต`, where `n` is the size of the smallest dimension of `A`, and `ฯต` is the [`eps`](@ref) of the element type of `A`. !!! note Numerical rank can be a sensitive and imprecise characterization of ill-conditioned matrices with singular values that are close to the threshold tolerance `max(atol, rtol*ฯƒโ‚)`. In such cases, slight perturbations to the singular-value computation or to the matrix can change the result of `rank` by pushing one or more singular values across the threshold. These variations can even occur due to changes in floating-point errors between different Julia versions, architectures, compilers, or operating systems. !!! compat "Julia 1.1" The `atol` and `rtol` keyword arguments requires at least Julia 1.1. In Julia 1.0 `rtol` is available as a positional argument, but this will be deprecated in Julia 2.0. # Examples ```jldoctest julia> rank(Matrix(I, 3, 3)) 3 julia> rank(diagm(0 => [1, 0, 2])) 2 julia> rank(diagm(0 => [1, 0.001, 2]), rtol=0.1) 2 julia> rank(diagm(0 => [1, 0.001, 2]), rtol=0.00001) 3 julia> rank(diagm(0 => [1, 0.001, 2]), atol=1.5) 1 ``` """ function rank(A::AbstractMatrix; atol::Real = 0.0, rtol::Real = (min(size(A)...)*eps(real(float(one(eltype(A))))))*iszero(atol)) isempty(A) && return 0 # 0-dimensional case s = svdvals(A) tol = max(atol, rtol*s[1]) count(>(tol), s) end rank(x::Union{Number,AbstractVector}) = iszero(x) ? 0 : 1 """ tr(M) Matrix trace. Sums the diagonal elements of `M`. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> tr(A) 5 ``` """ function tr(A::AbstractMatrix) checksquare(A) sum(diag(A)) end tr(x::Number) = x #kron(a::AbstractVector, b::AbstractVector) #kron(a::AbstractMatrix{T}, b::AbstractMatrix{S}) where {T,S} #det(a::AbstractMatrix) """ inv(M) Matrix inverse. Computes matrix `N` such that `M * N = I`, where `I` is the identity matrix. Computed by solving the left-division `N = M \\ I`. # Examples ```jldoctest julia> M = [2 5; 1 3] 2ร—2 Matrix{Int64}: 2 5 1 3 julia> N = inv(M) 2ร—2 Matrix{Float64}: 3.0 -5.0 -1.0 2.0 julia> M*N == N*M == Matrix(I, 2, 2) true ``` """ function inv(A::AbstractMatrix{T}) where T n = checksquare(A) S = typeof(zero(T)/one(T)) # dimensionful S0 = typeof(zero(T)/oneunit(T)) # dimensionless dest = Matrix{S0}(I, n, n) ldiv!(factorize(convert(AbstractMatrix{S}, A)), dest) end inv(A::Adjoint) = adjoint(inv(parent(A))) inv(A::Transpose) = transpose(inv(parent(A))) pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T<:Real} = _vectorpinv(transpose, v, tol) pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T<:Complex} = _vectorpinv(adjoint, v, tol) pinv(v::AbstractVector{T}, tol::Real = real(zero(T))) where {T} = _vectorpinv(adjoint, v, tol) function _vectorpinv(dualfn::Tf, v::AbstractVector{Tv}, tol) where {Tv,Tf} res = dualfn(similar(v, typeof(zero(Tv) / (abs2(one(Tv)) + abs2(one(Tv)))))) den = sum(abs2, v) # as tol is the threshold relative to the maximum singular value, for a vector with # single singular value ฯƒ=โˆšden, ฯƒ โ‰ฆ tol*ฯƒ is equivalent to den=0 โˆจ tolโ‰ฅ1 if iszero(den) || tol >= one(tol) fill!(res, zero(eltype(res))) else res .= dualfn(v) ./ den end return res end # this method is just an optimization: literal negative powers of A are # already turned by literal_pow into powers of inv(A), but for A^-1 this # would turn into inv(A)^1 = copy(inv(A)), which makes an extra copy. @inline Base.literal_pow(::typeof(^), A::AbstractMatrix, ::Val{-1}) = inv(A) """ \\(A, B) Matrix division using a polyalgorithm. For input matrices `A` and `B`, the result `X` is such that `A*X == B` when `A` is square. The solver that is used depends upon the structure of `A`. If `A` is upper or lower triangular (or diagonal), no factorization of `A` is required and the system is solved with either forward or backward substitution. For non-triangular square matrices, an LU factorization is used. For rectangular `A` the result is the minimum-norm least squares solution computed by a pivoted QR factorization of `A` and a rank estimate of `A` based on the R factor. When `A` is sparse, a similar polyalgorithm is used. For indefinite matrices, the `LDLt` factorization does not use pivoting during the numerical factorization and therefore the procedure can fail even for invertible matrices. See also: [`factorize`](@ref), [`pinv`](@ref). # Examples ```jldoctest julia> A = [1 0; 1 -2]; B = [32; -4]; julia> X = A \\ B 2-element Vector{Float64}: 32.0 18.0 julia> A * X == B true ``` """ function (\)(A::AbstractMatrix, B::AbstractVecOrMat) require_one_based_indexing(A, B) m, n = size(A) if m == n if istril(A) if istriu(A) return Diagonal(A) \ B else return LowerTriangular(A) \ B end end if istriu(A) return UpperTriangular(A) \ B end return lu(A) \ B end return qr(A, ColumnNorm()) \ B end (\)(a::AbstractVector, b::AbstractArray) = pinv(a) * b """ A / B Matrix right-division: `A / B` is equivalent to `(B' \\ A')'` where [`\\`](@ref) is the left-division operator. For square matrices, the result `X` is such that `A == X*B`. See also: [`rdiv!`](@ref). # Examples ```jldoctest julia> A = Float64[1 4 5; 3 9 2]; B = Float64[1 4 2; 3 4 2; 8 7 1]; julia> X = A / B 2ร—3 Matrix{Float64}: -0.65 3.75 -1.2 3.25 -2.75 1.0 julia> isapprox(A, X*B) true julia> isapprox(X, A*pinv(B)) true ``` """ function (/)(A::AbstractVecOrMat, B::AbstractVecOrMat) size(A,2) != size(B,2) && throw(DimensionMismatch("Both inputs should have the same number of columns")) return copy(adjoint(adjoint(B) \ adjoint(A))) end cond(x::Number) = iszero(x) ? Inf : 1.0 cond(x::Number, p) = cond(x) #Skeel condition numbers condskeel(A::AbstractMatrix, p::Real=Inf) = opnorm(abs.(inv(A))*abs.(A), p) """ condskeel(M, [x, p::Real=Inf]) ```math \\kappa_S(M, p) = \\left\\Vert \\left\\vert M \\right\\vert \\left\\vert M^{-1} \\right\\vert \\right\\Vert_p \\\\ \\kappa_S(M, x, p) = \\frac{\\left\\Vert \\left\\vert M \\right\\vert \\left\\vert M^{-1} \\right\\vert \\left\\vert x \\right\\vert \\right\\Vert_p}{\\left \\Vert x \\right \\Vert_p} ``` Skeel condition number ``\\kappa_S`` of the matrix `M`, optionally with respect to the vector `x`, as computed using the operator `p`-norm. ``\\left\\vert M \\right\\vert`` denotes the matrix of (entry wise) absolute values of ``M``; ``\\left\\vert M \\right\\vert_{ij} = \\left\\vert M_{ij} \\right\\vert``. Valid values for `p` are `1`, `2` and `Inf` (default). This quantity is also known in the literature as the Bauer condition number, relative condition number, or componentwise relative condition number. """ function condskeel(A::AbstractMatrix, x::AbstractVector, p::Real=Inf) norm(abs.(inv(A))*(abs.(A)*abs.(x)), p) / norm(x, p) end issymmetric(A::AbstractMatrix{<:Real}) = ishermitian(A) """ issymmetric(A) -> Bool Test whether a matrix is symmetric. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> issymmetric(a) true julia> b = [1 im; -im 1] 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+1im 0-1im 1+0im julia> issymmetric(b) false ``` """ function issymmetric(A::AbstractMatrix) indsm, indsn = axes(A) if indsm != indsn return false end for i = first(indsn):last(indsn), j = (i):last(indsn) if A[i,j] != transpose(A[j,i]) return false end end return true end issymmetric(x::Number) = x == x """ ishermitian(A) -> Bool Test whether a matrix is Hermitian. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> ishermitian(a) true julia> b = [1 im; -im 1] 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+1im 0-1im 1+0im julia> ishermitian(b) true ``` """ function ishermitian(A::AbstractMatrix) indsm, indsn = axes(A) if indsm != indsn return false end for i = indsn, j = i:last(indsn) if A[i,j] != adjoint(A[j,i]) return false end end return true end ishermitian(x::Number) = (x == conj(x)) """ istriu(A::AbstractMatrix, k::Integer = 0) -> Bool Test whether `A` is upper triangular starting from the `k`th superdiagonal. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> istriu(a) false julia> istriu(a, -1) true julia> b = [1 im; 0 -1] 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+1im 0+0im -1+0im julia> istriu(b) true julia> istriu(b, 1) false ``` """ function istriu(A::AbstractMatrix, k::Integer = 0) require_one_based_indexing(A) return _istriu(A, k) end istriu(x::Number) = true @inline function _istriu(A::AbstractMatrix, k) m, n = size(A) for j in 1:min(n, m + k - 1) all(iszero, view(A, max(1, j - k + 1):m, j)) || return false end return true end """ istril(A::AbstractMatrix, k::Integer = 0) -> Bool Test whether `A` is lower triangular starting from the `k`th superdiagonal. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> istril(a) false julia> istril(a, 1) true julia> b = [1 0; -im -1] 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+0im 0-1im -1+0im julia> istril(b) true julia> istril(b, -1) false ``` """ function istril(A::AbstractMatrix, k::Integer = 0) require_one_based_indexing(A) return _istril(A, k) end istril(x::Number) = true @inline function _istril(A::AbstractMatrix, k) m, n = size(A) for j in max(1, k + 2):n all(iszero, view(A, 1:min(j - k - 1, m), j)) || return false end return true end """ isbanded(A::AbstractMatrix, kl::Integer, ku::Integer) -> Bool Test whether `A` is banded with lower bandwidth starting from the `kl`th superdiagonal and upper bandwidth extending through the `ku`th superdiagonal. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> LinearAlgebra.isbanded(a, 0, 0) false julia> LinearAlgebra.isbanded(a, -1, 1) true julia> b = [1 0; -im -1] # lower bidiagonal 2ร—2 Matrix{Complex{Int64}}: 1+0im 0+0im 0-1im -1+0im julia> LinearAlgebra.isbanded(b, 0, 0) false julia> LinearAlgebra.isbanded(b, -1, 0) true ``` """ isbanded(A::AbstractMatrix, kl::Integer, ku::Integer) = istriu(A, kl) && istril(A, ku) """ isdiag(A) -> Bool Test whether a matrix is diagonal in the sense that `iszero(A[i,j])` is true unless `i == j`. Note that it is not necessary for `A` to be square; if you would also like to check that, you need to check that `size(A, 1) == size(A, 2)`. # Examples ```jldoctest julia> a = [1 2; 2 -1] 2ร—2 Matrix{Int64}: 1 2 2 -1 julia> isdiag(a) false julia> b = [im 0; 0 -im] 2ร—2 Matrix{Complex{Int64}}: 0+1im 0+0im 0+0im 0-1im julia> isdiag(b) true julia> c = [1 0 0; 0 2 0] 2ร—3 Matrix{Int64}: 1 0 0 0 2 0 julia> isdiag(c) true julia> d = [1 0 0; 0 2 3] 2ร—3 Matrix{Int64}: 1 0 0 0 2 3 julia> isdiag(d) false ``` """ isdiag(A::AbstractMatrix) = isbanded(A, 0, 0) isdiag(x::Number) = true """ axpy!(ฮฑ, x::AbstractArray, y::AbstractArray) Overwrite `y` with `x * ฮฑ + y` and return `y`. If `x` and `y` have the same axes, it's equivalent with `y .+= x .* a`. # Examples ```jldoctest julia> x = [1; 2; 3]; julia> y = [4; 5; 6]; julia> axpy!(2, x, y) 3-element Vector{Int64}: 6 9 12 ``` """ function axpy!(ฮฑ, x::AbstractArray, y::AbstractArray) n = length(x) if n != length(y) throw(DimensionMismatch("x has length $n, but y has length $(length(y))")) end iszero(ฮฑ) && return y for (IY, IX) in zip(eachindex(y), eachindex(x)) @inbounds y[IY] += x[IX]*ฮฑ end return y end function axpy!(ฮฑ, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractArray, ry::AbstractArray{<:Integer}) if length(rx) != length(ry) throw(DimensionMismatch("rx has length $(length(rx)), but ry has length $(length(ry))")) elseif !checkindex(Bool, eachindex(IndexLinear(), x), rx) throw(BoundsError(x, rx)) elseif !checkindex(Bool, eachindex(IndexLinear(), y), ry) throw(BoundsError(y, ry)) end iszero(ฮฑ) && return y for (IY, IX) in zip(eachindex(ry), eachindex(rx)) @inbounds y[ry[IY]] += x[rx[IX]]*ฮฑ end return y end """ axpby!(ฮฑ, x::AbstractArray, ฮฒ, y::AbstractArray) Overwrite `y` with `x * ฮฑ + y * ฮฒ` and return `y`. If `x` and `y` have the same axes, it's equivalent with `y .= x .* a .+ y .* ฮฒ`. # Examples ```jldoctest julia> x = [1; 2; 3]; julia> y = [4; 5; 6]; julia> axpby!(2, x, 2, y) 3-element Vector{Int64}: 10 14 18 ``` """ function axpby!(ฮฑ, x::AbstractArray, ฮฒ, y::AbstractArray) if length(x) != length(y) throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) end iszero(ฮฑ) && isone(ฮฒ) && return y for (IX, IY) in zip(eachindex(x), eachindex(y)) @inbounds y[IY] = x[IX]*ฮฑ + y[IY]*ฮฒ end y end DenseLike{T} = Union{DenseArray{T}, Base.StridedReshapedArray{T}, Base.StridedReinterpretArray{T}} StridedVecLike{T} = Union{DenseLike{T}, Base.FastSubArray{T,<:Any,<:DenseLike{T}}} axpy!(ฮฑ::Number, x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasFloat} = BLAS.axpy!(ฮฑ, x, y) axpby!(ฮฑ::Number, x::StridedVecLike{T}, ฮฒ::Number, y::StridedVecLike{T}) where {T<:BlasFloat} = BLAS.axpby!(ฮฑ, x, ฮฒ, y) function axpy!(ฮฑ::Number, x::StridedVecLike{T}, rx::AbstractRange{<:Integer}, y::StridedVecLike{T}, ry::AbstractRange{<:Integer}, ) where {T<:BlasFloat} if Base.has_offset_axes(rx, ry) return @invoke axpy!(ฮฑ, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractArray, ry::AbstractArray{<:Integer}, ) end @views BLAS.axpy!(ฮฑ, x[rx], y[ry]) return y end """ rotate!(x, y, c, s) Overwrite `x` with `c*x + s*y` and `y` with `-conj(s)*x + c*y`. Returns `x` and `y`. !!! compat "Julia 1.5" `rotate!` requires at least Julia 1.5. """ function rotate!(x::AbstractVector, y::AbstractVector, c, s) require_one_based_indexing(x, y) n = length(x) if n != length(y) throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) end @inbounds for i = 1:n xi, yi = x[i], y[i] x[i] = c *xi + s*yi y[i] = -conj(s)*xi + c*yi end return x, y end """ reflect!(x, y, c, s) Overwrite `x` with `c*x + s*y` and `y` with `conj(s)*x - c*y`. Returns `x` and `y`. !!! compat "Julia 1.5" `reflect!` requires at least Julia 1.5. """ function reflect!(x::AbstractVector, y::AbstractVector, c, s) require_one_based_indexing(x, y) n = length(x) if n != length(y) throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) end @inbounds for i = 1:n xi, yi = x[i], y[i] x[i] = c *xi + s*yi y[i] = conj(s)*xi - c*yi end return x, y end # Elementary reflection similar to LAPACK. The reflector is not Hermitian but # ensures that tridiagonalization of Hermitian matrices become real. See lawn72 @inline function reflector!(x::AbstractVector{T}) where {T} require_one_based_indexing(x) n = length(x) n == 0 && return zero(eltype(x)) @inbounds begin ฮพ1 = x[1] normu = norm(x) if iszero(normu) return zero(ฮพ1/normu) end ฮฝ = T(copysign(normu, real(ฮพ1))) ฮพ1 += ฮฝ x[1] = -ฮฝ for i = 2:n x[i] /= ฮพ1 end end ฮพ1/ฮฝ end """ reflectorApply!(x, ฯ„, A) Multiplies `A` in-place by a Householder reflection on the left. It is equivalent to `A .= (I - ฯ„*[1; x] * [1; x]')*A`. """ @inline function reflectorApply!(x::AbstractVector, ฯ„::Number, A::AbstractVecOrMat) require_one_based_indexing(x) m, n = size(A, 1), size(A, 2) if length(x) != m throw(DimensionMismatch("reflector has length $(length(x)), which must match the first dimension of matrix A, $m")) end m == 0 && return A @inbounds for j = 1:n Aj, xj = view(A, 2:m, j), view(x, 2:m) vAj = conj(ฯ„)*(A[1, j] + dot(xj, Aj)) A[1, j] -= vAj axpy!(-vAj, xj, Aj) end return A end """ det(M) Matrix determinant. See also: [`logdet`](@ref) and [`logabsdet`](@ref). # Examples ```jldoctest julia> M = [1 0; 2 2] 2ร—2 Matrix{Int64}: 1 0 2 2 julia> det(M) 2.0 ``` """ function det(A::AbstractMatrix{T}) where {T} if istriu(A) || istril(A) S = promote_type(T, typeof((one(T)*zero(T) + zero(T))/one(T))) return convert(S, det(UpperTriangular(A))) end return det(lu(A; check = false)) end det(x::Number) = x # Resolve Issue #40128 det(A::AbstractMatrix{BigInt}) = det_bareiss(A) """ logabsdet(M) Log of absolute value of matrix determinant. Equivalent to `(log(abs(det(M))), sign(det(M)))`, but may provide increased accuracy and/or speed. # Examples ```jldoctest julia> A = [-1. 0.; 0. 1.] 2ร—2 Matrix{Float64}: -1.0 0.0 0.0 1.0 julia> det(A) -1.0 julia> logabsdet(A) (0.0, -1.0) julia> B = [2. 0.; 0. 1.] 2ร—2 Matrix{Float64}: 2.0 0.0 0.0 1.0 julia> det(B) 2.0 julia> logabsdet(B) (0.6931471805599453, 1.0) ``` """ logabsdet(A::AbstractMatrix) = logabsdet(lu(A, check=false)) logabsdet(a::Number) = log(abs(a)), sign(a) """ logdet(M) Log of matrix determinant. Equivalent to `log(det(M))`, but may provide increased accuracy and/or speed. # Examples ```jldoctest julia> M = [1 0; 2 2] 2ร—2 Matrix{Int64}: 1 0 2 2 julia> logdet(M) 0.6931471805599453 julia> logdet(Matrix(I, 3, 3)) 0.0 ``` """ function logdet(A::AbstractMatrix) d,s = logabsdet(A) return d + log(s) end logdet(A) = log(det(A)) const NumberArray{T<:Number} = AbstractArray{T} exactdiv(a, b) = a/b exactdiv(a::Integer, b::Integer) = div(a, b) """ det_bareiss!(M) Calculates the determinant of a matrix using the [Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm) using inplace operations. # Examples ```jldoctest julia> M = [1 0; 2 2] 2ร—2 Matrix{Int64}: 1 0 2 2 julia> LinearAlgebra.det_bareiss!(M) 2 ``` """ function det_bareiss!(M) n = checksquare(M) sign, prev = Int8(1), one(eltype(M)) for i in 1:n-1 if iszero(M[i,i]) # swap with another col to make nonzero swapto = findfirst(!iszero, @view M[i,i+1:end]) isnothing(swapto) && return zero(prev) sign = -sign Base.swapcols!(M, i, i + swapto) end for k in i+1:n, j in i+1:n M[j,k] = exactdiv(M[j,k]*M[i,i] - M[j,i]*M[i,k], prev) end prev = M[i,i] end return sign * M[end,end] end """ LinearAlgebra.det_bareiss(M) Calculates the determinant of a matrix using the [Bareiss Algorithm](https://en.wikipedia.org/wiki/Bareiss_algorithm). Also refer to [`det_bareiss!`](@ref). """ det_bareiss(M) = det_bareiss!(copy(M)) """ promote_leaf_eltypes(itr) For an (possibly nested) iterable object `itr`, promote the types of leaf elements. Equivalent to `promote_type(typeof(leaf1), typeof(leaf2), ...)`. Currently supports only numeric leaf elements. # Examples ```jldoctest julia> a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]] 3-element Vector{Any}: Any[1, 2, [3, 4]] 5.0 Any[0 + 6im, [7.0, 8.0]] julia> LinearAlgebra.promote_leaf_eltypes(a) ComplexF64 (alias for Complex{Float64}) ``` """ promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:Number} = T promote_leaf_eltypes(x::Union{AbstractArray{T},Tuple{T,Vararg{T}}}) where {T<:NumberArray} = eltype(T) promote_leaf_eltypes(x::T) where {T} = T promote_leaf_eltypes(x::Union{AbstractArray,Tuple}) = mapreduce(promote_leaf_eltypes, promote_type, x; init=Bool) # isapprox: approximate equality of arrays [like isapprox(Number,Number)] # Supports nested arrays; e.g., for `a = [[1,2, [3,4]], 5.0, [6im, [7.0, 8.0]]]` # `a โ‰ˆ a` is `true`. function isapprox(x::AbstractArray, y::AbstractArray; atol::Real=0, rtol::Real=Base.rtoldefault(promote_leaf_eltypes(x),promote_leaf_eltypes(y),atol), nans::Bool=false, norm::Function=norm) d = norm(x - y) if isfinite(d) return iszero(rtol) ? d <= atol : d <= max(atol, rtol*max(norm(x), norm(y))) else # Fall back to a component-wise approximate comparison # (mapreduce instead of all for greater generality [#44893]) return mapreduce((a, b) -> isapprox(a, b; rtol=rtol, atol=atol, nans=nans), &, x, y) end end """ normalize!(a::AbstractArray, p::Real=2) Normalize the array `a` in-place so that its `p`-norm equals unity, i.e. `norm(a, p) == 1`. See also [`normalize`](@ref) and [`norm`](@ref). """ function normalize!(a::AbstractArray, p::Real=2) nrm = norm(a, p) __normalize!(a, nrm) end @inline function __normalize!(a::AbstractArray, nrm) # The largest positive floating point number whose inverse is less than infinity ฮด = inv(prevfloat(typemax(nrm))) if nrm โ‰ฅ ฮด # Safe to multiply with inverse invnrm = inv(nrm) rmul!(a, invnrm) else # scale elements to avoid overflow ฮตฮด = eps(one(nrm))/ฮด rmul!(a, ฮตฮด) rmul!(a, inv(nrm*ฮตฮด)) end return a end """ normalize(a, p::Real=2) Normalize `a` so that its `p`-norm equals unity, i.e. `norm(a, p) == 1`. For scalars, this is similar to sign(a), except normalize(0) = NaN. See also [`normalize!`](@ref), [`norm`](@ref), and [`sign`](@ref). # Examples ```jldoctest julia> a = [1,2,4]; julia> b = normalize(a) 3-element Vector{Float64}: 0.2182178902359924 0.4364357804719848 0.8728715609439696 julia> norm(b) 1.0 julia> c = normalize(a, 1) 3-element Vector{Float64}: 0.14285714285714285 0.2857142857142857 0.5714285714285714 julia> norm(c, 1) 1.0 julia> a = [1 2 4 ; 1 2 4] 2ร—3 Matrix{Int64}: 1 2 4 1 2 4 julia> norm(a) 6.48074069840786 julia> normalize(a) 2ร—3 Matrix{Float64}: 0.154303 0.308607 0.617213 0.154303 0.308607 0.617213 julia> normalize(3, 1) 1.0 julia> normalize(-8, 1) -1.0 julia> normalize(0, 1) NaN ``` """ function normalize(a::AbstractArray, p::Real = 2) nrm = norm(a, p) if !isempty(a) aa = copymutable_oftype(a, typeof(first(a)/nrm)) return __normalize!(aa, nrm) else T = typeof(zero(eltype(a))/nrm) return T[] end end normalize(x) = x / norm(x) normalize(x, p::Real) = x / norm(x, p) u/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/blas.jlŒ9# This file is a part of Julia. License is MIT: https://julialang.org/license """ Interface to BLAS subroutines. """ module BLAS import Base: copyto! using Base: require_one_based_indexing, USE_BLAS64 export # Note: `xFUNC_NAME` is a placeholder for not exported BLAS functions # ref: http://www.netlib.org/blas/blasqr.pdf # Level 1 # xROTG # xROTMG rot!, # xROTM # xSWAP scal!, scal, blascopy!, # xAXPY!, # xAXPBY!, # xDOT dotc, dotu, # xxDOT nrm2, asum, iamax, # Level 2 gemv!, gemv, gbmv!, gbmv, hemv!, hemv, # xHBMV hpmv!, symv!, symv, sbmv!, sbmv, spmv!, trmv!, trmv, # xTBMV # xTPMV trsv!, trsv, # xTBSV # xTPSV ger!, # xGERU # xGERC her!, # xHPR # xHER2 # xHPR2 syr!, spr!, # xSYR2 # xSPR2 # Level 3 gemm!, gemm, symm!, symm, hemm!, hemm, syrk!, syrk, herk!, herk, syr2k!, syr2k, her2k!, her2k, trmm!, trmm, trsm!, trsm using ..LinearAlgebra: libblastrampoline, BlasReal, BlasComplex, BlasFloat, BlasInt, DimensionMismatch, checksquare, stride1, chkstride1 include("lbt.jl") # Legacy bindings that some packages (such as NNlib.jl) use. # We maintain these for backwards-compatibility but new packages # should not look at these, instead preferring to parse the output # of BLAS.get_config() const libblas = libblastrampoline const liblapack = libblastrampoline vendor() = :lbt """ get_config() Return an object representing the current `libblastrampoline` configuration. !!! compat "Julia 1.7" `get_config()` requires at least Julia 1.7. """ get_config() = lbt_get_config() if USE_BLAS64 macro blasfunc(x) return Expr(:quote, Symbol(x, "64_")) end else macro blasfunc(x) return Expr(:quote, x) end end _tryparse_env_int(key) = tryparse(Int, get(ENV, key, "")) """ set_num_threads(n::Integer) set_num_threads(::Nothing) Set the number of threads the BLAS library should use equal to `n::Integer`. Also accepts `nothing`, in which case julia tries to guess the default number of threads. Passing `nothing` is discouraged and mainly exists for historical reasons. """ set_num_threads(nt::Integer)::Nothing = lbt_set_num_threads(Int32(nt)) function set_num_threads(::Nothing) nt = something( _tryparse_env_int("OPENBLAS_NUM_THREADS"), _tryparse_env_int("OMP_NUM_THREADS"), _tryparse_env_int("VECLIB_MAXIMUM_THREADS"), max(1, Sys.CPU_THREADS รท 2), ) return set_num_threads(nt) end """ get_num_threads() Get the number of threads the BLAS library is using. !!! compat "Julia 1.6" `get_num_threads` requires at least Julia 1.6. """ get_num_threads()::Int = lbt_get_num_threads() function check() # TODO: once we have bitfields of the BLAS functions that are actually forwarded, # ensure that we have a complete set here (warning on an incomplete BLAS implementation) config = get_config() # Ensure that one of our loaded libraries satisfies our interface requirement interface = USE_BLAS64 ? :ilp64 : :lp64 if !any(lib.interface == interface for lib in config.loaded_libs) interfacestr = uppercase(string(interface)) @error("No loaded BLAS libraries were built with $(interfacestr) support") println("Quitting.") exit() end end "Check that upper/lower (for special matrices) is correctly specified" function chkuplo(uplo::AbstractChar) if !(uplo == 'U' || uplo == 'L') throw(ArgumentError(lazy"uplo argument must be 'U' (upper) or 'L' (lower), got $uplo")) end uplo end # Level 1 # A help function to pick the pointer and inc for 1d like inputs. @inline function vec_pointer_stride(x::AbstractArray, stride0check = nothing) Base._checkcontiguous(Bool, x) && return pointer(x), 1 # simplify runtime check when possible st, ptr = checkedstride(x), pointer(x) isnothing(stride0check) || (st == 0 && throw(stride0check)) ptr += min(st, 0) * sizeof(eltype(x)) * (length(x) - 1) ptr, st end function checkedstride(x::AbstractArray) szs::Dims = size(x) sts::Dims = strides(x) _, st, n = Base.merge_adjacent_dim(szs, sts) n === ndims(x) && return st throw(ArgumentError("only support vector like inputs")) end ## copy """ blascopy!(n, X, incx, Y, incy) Copy `n` elements of array `X` with stride `incx` to array `Y` with stride `incy`. Returns `Y`. """ function blascopy! end for (fname, elty) in ((:dcopy_,:Float64), (:scopy_,:Float32), (:zcopy_,:ComplexF64), (:ccopy_,:ComplexF32)) @eval begin # SUBROUTINE DCOPY(N,DX,INCX,DY,INCY) function blascopy!(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), n, DX, incx, DY, incy) DY end end end ## rot """ rot!(n, X, incx, Y, incy, c, s) Overwrite `X` with `c*X + s*Y` and `Y` with `-conj(s)*X + c*Y` for the first `n` elements of array `X` with stride `incx` and first `n` elements of array `Y` with stride `incy`. Returns `X` and `Y`. !!! compat "Julia 1.5" `rot!` requires at least Julia 1.5. """ function rot! end for (fname, elty, cty, sty, lib) in ((:drot_, :Float64, :Float64, :Float64, libblastrampoline), (:srot_, :Float32, :Float32, :Float32, libblastrampoline), (:zdrot_, :ComplexF64, :Float64, :Float64, libblastrampoline), (:csrot_, :ComplexF32, :Float32, :Float32, libblastrampoline), (:zrot_, :ComplexF64, :Float64, :ComplexF64, libblastrampoline), (:crot_, :ComplexF32, :Float32, :ComplexF32, libblastrampoline)) @eval begin # SUBROUTINE DROT(N,DX,INCX,DY,INCY,C,S) function rot!(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer, C::$cty, S::$sty) ccall((@blasfunc($fname), $lib), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$cty}, Ref{$sty}), n, DX, incx, DY, incy, C, S) DX, DY end end end ## scal """ scal!(n, a, X, incx) scal!(a, X) Overwrite `X` with `a*X` for the first `n` elements of array `X` with stride `incx`. Returns `X`. If `n` and `incx` are not provided, `length(X)` and `stride(X,1)` are used. """ function scal! end """ scal(n, a, X, incx) scal(a, X) Return `X` scaled by `a` for the first `n` elements of array `X` with stride `incx`. If `n` and `incx` are not provided, `length(X)` and `stride(X,1)` are used. """ function scal end for (fname, elty) in ((:dscal_,:Float64), (:sscal_,:Float32), (:zscal_,:ComplexF64), (:cscal_,:ComplexF32)) @eval begin # SUBROUTINE DSCAL(N,DA,DX,INCX) function scal!(n::Integer, DA::$elty, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), n, DA, DX, incx) DX end function scal!(DA::$elty, DX::AbstractArray{$elty}) p, st = vec_pointer_stride(DX, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve DX scal!(length(DX), DA, p, abs(st)) DX end end end scal(n, DA, DX, incx) = scal!(n, DA, copy(DX), incx) scal(DA, DX) = scal!(DA, copy(DX)) ## dot """ dot(n, X, incx, Y, incy) Dot product of two vectors consisting of `n` elements of array `X` with stride `incx` and `n` elements of array `Y` with stride `incy`. # Examples ```jldoctest julia> BLAS.dot(10, fill(1.0, 10), 1, fill(1.0, 20), 2) 10.0 ``` """ function dot end """ dotc(n, X, incx, U, incy) Dot function for two complex vectors, consisting of `n` elements of array `X` with stride `incx` and `n` elements of array `U` with stride `incy`, conjugating the first vector. # Examples ```jldoctest julia> BLAS.dotc(10, fill(1.0im, 10), 1, fill(1.0+im, 20), 2) 10.0 - 10.0im ``` """ function dotc end """ dotu(n, X, incx, Y, incy) Dot function for two complex vectors consisting of `n` elements of array `X` with stride `incx` and `n` elements of array `Y` with stride `incy`. # Examples ```jldoctest julia> BLAS.dotu(10, fill(1.0im, 10), 1, fill(1.0+im, 20), 2) -10.0 + 10.0im ``` """ function dotu end for (fname, elty) in ((:cblas_ddot,:Float64), (:cblas_sdot,:Float32)) @eval begin # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) # * .. Scalar Arguments .. # INTEGER INCX,INCY,N # * .. # * .. Array Arguments .. # DOUBLE PRECISION DX(*),DY(*) function dot(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), $elty, (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt), n, DX, incx, DY, incy) end end end for (fname, elty) in ((:cblas_zdotc_sub,:ComplexF64), (:cblas_cdotc_sub,:ComplexF32)) @eval begin # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) # * .. Scalar Arguments .. # INTEGER INCX,INCY,N # * .. # * .. Array Arguments .. # DOUBLE PRECISION DX(*),DY(*) function dotc(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) result = Ref{$elty}() ccall((@blasfunc($fname), libblastrampoline), Cvoid, (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), n, DX, incx, DY, incy, result) result[] end end end for (fname, elty) in ((:cblas_zdotu_sub,:ComplexF64), (:cblas_cdotu_sub,:ComplexF32)) @eval begin # DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) # * .. Scalar Arguments .. # INTEGER INCX,INCY,N # * .. # * .. Array Arguments .. # DOUBLE PRECISION DX(*),DY(*) function dotu(n::Integer, DX::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},AbstractArray{$elty}}, incy::Integer) result = Ref{$elty}() ccall((@blasfunc($fname), libblastrampoline), Cvoid, (BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}, BlasInt, Ptr{$elty}), n, DX, incx, DY, incy, result) result[] end end end for (elty, f) in ((Float32, :dot), (Float64, :dot), (ComplexF32, :dotc), (ComplexF64, :dotc), (ComplexF32, :dotu), (ComplexF64, :dotu)) @eval begin function $f(x::AbstractArray{$elty}, y::AbstractArray{$elty}) n, m = length(x), length(y) n == m || throw(DimensionMismatch(lazy"dot product arguments have lengths $n and $m")) GC.@preserve x y $f(n, vec_pointer_stride(x)..., vec_pointer_stride(y)...) end end end ## nrm2 """ nrm2(n, X, incx) 2-norm of a vector consisting of `n` elements of array `X` with stride `incx`. # Examples ```jldoctest julia> BLAS.nrm2(4, fill(1.0, 8), 2) 2.0 julia> BLAS.nrm2(1, fill(1.0, 8), 2) 1.0 ``` """ function nrm2 end for (fname, elty, ret_type) in ((:dnrm2_,:Float64,:Float64), (:snrm2_,:Float32,:Float32), (:dznrm2_,:ComplexF64,:Float64), (:scnrm2_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE DNRM2(N,X,INCX) function nrm2(n::Integer, X::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblastrampoline), $ret_type, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), n, X, incx) end end end # openblas returns 0 for negative stride function nrm2(x::AbstractArray) p, st = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x nrm2(length(x), p, abs(st)) end ## asum """ asum(n, X, incx) Sum of the magnitudes of the first `n` elements of array `X` with stride `incx`. For a real array, the magnitude is the absolute value. For a complex array, the magnitude is the sum of the absolute value of the real part and the absolute value of the imaginary part. # Examples ```jldoctest julia> BLAS.asum(5, fill(1.0im, 10), 2) 5.0 julia> BLAS.asum(2, fill(1.0im, 10), 5) 2.0 ``` """ function asum end for (fname, elty, ret_type) in ((:dasum_,:Float64,:Float64), (:sasum_,:Float32,:Float32), (:dzasum_,:ComplexF64,:Float64), (:scasum_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ASUM(N, X, INCX) function asum(n::Integer, X::Union{Ptr{$elty},AbstractArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblastrampoline), $ret_type, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), n, X, incx) end end end function asum(x::AbstractArray) p, st = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x asum(length(x), p, abs(st)) end ## axpy """ axpy!(a, X, Y) Overwrite `Y` with `X*a + Y`, where `a` is a scalar. Return `Y`. # Examples ```jldoctest julia> x = [1.; 2; 3]; julia> y = [4. ;; 5 ;; 6]; julia> BLAS.axpy!(2, x, y) 1ร—3 Matrix{Float64}: 6.0 9.0 12.0 ``` """ function axpy! end for (fname, elty) in ((:daxpy_,:Float64), (:saxpy_,:Float32), (:zaxpy_,:ComplexF64), (:caxpy_,:ComplexF32)) @eval begin # SUBROUTINE DAXPY(N,DA,DX,INCX,DY,INCY) # DY <- DA*DX + DY #* .. Scalar Arguments .. # DOUBLE PRECISION DA # INTEGER INCX,INCY,N #* .. Array Arguments .. # DOUBLE PRECISION DX(*),DY(*) function axpy!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, dy::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), n, alpha, dx, incx, dy, incy) dy end end end function axpy!(alpha::Number, x::AbstractArray{T}, y::AbstractArray{T}) where T<:BlasFloat if length(x) != length(y) throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end GC.@preserve x y axpy!(length(x), T(alpha), vec_pointer_stride(x)..., vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed"))...) y end function axpy!(alpha::Number, x::Array{T}, rx::AbstractRange{Ti}, y::Array{T}, ry::AbstractRange{Ti}) where {T<:BlasFloat,Ti<:Integer} if length(rx) != length(ry) throw(DimensionMismatch("ranges of differing lengths")) end if minimum(rx) < 1 || maximum(rx) > length(x) throw(ArgumentError(lazy"range out of bounds for x, of length $(length(x))")) end if minimum(ry) < 1 || maximum(ry) > length(y) throw(ArgumentError(lazy"range out of bounds for y, of length $(length(y))")) end GC.@preserve x y axpy!( length(rx), T(alpha), pointer(x, minimum(rx)), step(rx), pointer(y, minimum(ry)), step(ry)) return y end """ axpby!(a, X, b, Y) Overwrite `Y` with `X*a + Y*b`, where `a` and `b` are scalars. Return `Y`. # Examples ```jldoctest julia> x = [1., 2, 3]; julia> y = [4., 5, 6]; julia> BLAS.axpby!(2., x, 3., y) 3-element Vector{Float64}: 14.0 19.0 24.0 ``` """ function axpby! end for (fname, elty) in ((:daxpby_,:Float64), (:saxpby_,:Float32), (:zaxpby_,:ComplexF64), (:caxpby_,:ComplexF32)) @eval begin # SUBROUTINE DAXPBY(N,DA,DX,INCX,DB,DY,INCY) # DY <- DA*DX + DB*DY #* .. Scalar Arguments .. # DOUBLE PRECISION DA,DB # INTEGER INCX,INCY,N #* .. Array Arguments .. # DOUBLE PRECISION DX(*),DY(*) function axpby!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, beta::($elty), dy::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), n, alpha, dx, incx, beta, dy, incy) dy end end end function axpby!(alpha::Number, x::AbstractArray{T}, beta::Number, y::AbstractArray{T}) where T<:BlasFloat require_one_based_indexing(x, y) if length(x) != length(y) throw(DimensionMismatch(lazy"x has length $(length(x)), but y has length $(length(y))")) end GC.@preserve x y axpby!(length(x), T(alpha), vec_pointer_stride(x)..., T(beta), vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed"))...) y end ## iamax for (fname, elty) in ((:idamax_,:Float64), (:isamax_,:Float32), (:izamax_,:ComplexF64), (:icamax_,:ComplexF32)) @eval begin function iamax(n::Integer, dx::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblastrampoline),BlasInt, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), n, dx, incx) end end end function iamax(dx::AbstractArray) p, st = vec_pointer_stride(dx) st <= 0 && return BlasInt(0) iamax(length(dx), p, st) end """ iamax(n, dx, incx) iamax(dx) Find the index of the element of `dx` with the maximum absolute value. `n` is the length of `dx`, and `incx` is the stride. If `n` and `incx` are not provided, they assume default values of `n=length(dx)` and `incx=stride1(dx)`. """ iamax # Level 2 ## mv ### gemv for (fname, elty) in ((:dgemv_,:Float64), (:sgemv_,:Float32), (:zgemv_,:ComplexF64), (:cgemv_,:ComplexF32)) @eval begin #SUBROUTINE DGEMV(TRANS,M,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) #* .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,LDA,M,N # CHARACTER TRANS #* .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function gemv!(trans::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractVecOrMat{$elty}, X::AbstractVector{$elty}, beta::Union{($elty), Bool}, Y::AbstractVector{$elty}) require_one_based_indexing(A, X, Y) m,n = size(A,1),size(A,2) if trans == 'N' && (length(X) != n || length(Y) != m) throw(DimensionMismatch(lazy"A has dimensions $(size(A)), X has length $(length(X)) and Y has length $(length(Y))")) elseif trans == 'C' && (length(X) != m || length(Y) != n) throw(DimensionMismatch(lazy"the adjoint of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) elseif trans == 'T' && (length(X) != m || length(Y) != n) throw(DimensionMismatch(lazy"the transpose of A has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) end chkstride1(A) lda = stride(A,2) pX, sX = vec_pointer_stride(X, ArgumentError("input vector with 0 stride is not allowed")) pY, sY = vec_pointer_stride(Y, ArgumentError("dest vector with 0 stride is not allowed")) pA = pointer(A) if lda < 0 pA += (size(A, 2) - 1) * lda * sizeof($elty) lda = -lda trans == 'N' ? (sX = -sX) : (sY = -sY) end lda >= size(A,1) || size(A,2) <= 1 || error("when `size(A,2) > 1`, `abs(stride(A,2))` must be at least `size(A,1)`") lda = max(1, size(A,1), lda) GC.@preserve A X Y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), trans, size(A,1), size(A,2), alpha, pA, lda, pX, sX, beta, pY, sY, 1) Y end function gemv(trans::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, X::AbstractVector{$elty}) gemv!(trans, alpha, A, X, zero($elty), similar(X, $elty, size(A, (trans == 'N' ? 1 : 2)))) end function gemv(trans::AbstractChar, A::AbstractMatrix{$elty}, X::AbstractVector{$elty}) gemv!(trans, one($elty), A, X, zero($elty), similar(X, $elty, size(A, (trans == 'N' ? 1 : 2)))) end end end """ gemv!(tA, alpha, A, x, beta, y) Update the vector `y` as `alpha*A*x + beta*y` or `alpha*A'x + beta*y` according to [`tA`](@ref stdlib-blas-trans). `alpha` and `beta` are scalars. Return the updated `y`. """ gemv! """ gemv(tA, alpha, A, x) Return `alpha*A*x` or `alpha*A'x` according to [`tA`](@ref stdlib-blas-trans). `alpha` is a scalar. """ gemv(tA, alpha, A, x) """ gemv(tA, A, x) Return `A*x` or `A'x` according to [`tA`](@ref stdlib-blas-trans). """ gemv(tA, A, x) ### (GB) general banded matrix-vector multiplication """ gbmv!(trans, m, kl, ku, alpha, A, x, beta, y) Update vector `y` as `alpha*A*x + beta*y` or `alpha*A'*x + beta*y` according to [`trans`](@ref stdlib-blas-trans). The matrix `A` is a general band matrix of dimension `m` by `size(A,2)` with `kl` sub-diagonals and `ku` super-diagonals. `alpha` and `beta` are scalars. Return the updated `y`. """ function gbmv! end """ gbmv(trans, m, kl, ku, alpha, A, x) Return `alpha*A*x` or `alpha*A'*x` according to [`trans`](@ref stdlib-blas-trans). The matrix `A` is a general band matrix of dimension `m` by `size(A,2)` with `kl` sub-diagonals and `ku` super-diagonals, and `alpha` is a scalar. """ function gbmv end for (fname, elty) in ((:dgbmv_,:Float64), (:sgbmv_,:Float32), (:zgbmv_,:ComplexF64), (:cgbmv_,:ComplexF32)) @eval begin # SUBROUTINE DGBMV(TRANS,M,N,KL,KU,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,KL,KU,LDA,M,N # CHARACTER TRANS # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function gbmv!(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, alpha::Union{($elty), Bool}, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::Union{($elty), Bool}, y::AbstractVector{$elty}) require_one_based_indexing(A, x, y) chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), trans, m, size(A,2), kl, ku, alpha, A, max(1,stride(A,2)), px, stx, beta, py, sty, 1) y end function gbmv(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) n = size(A,2) leny = trans == 'N' ? m : n gbmv!(trans, m, kl, ku, alpha, A, x, zero($elty), similar(x, $elty, leny)) end function gbmv(trans::AbstractChar, m::Integer, kl::Integer, ku::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) gbmv(trans, m, kl, ku, one($elty), A, x) end end end ### symv """ symv!(ul, alpha, A, x, beta, y) Update the vector `y` as `alpha*A*x + beta*y`. `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. `alpha` and `beta` are scalars. Return the updated `y`. """ function symv! end for (fname, elty, lib) in ((:dsymv_,:Float64,libblastrampoline), (:ssymv_,:Float32,libblastrampoline), (:zsymv_,:ComplexF64,libblastrampoline), (:csymv_,:ComplexF32,libblastrampoline)) # Note that the complex symv are not BLAS but auiliary functions in LAPACK @eval begin # SUBROUTINE DSYMV(UPLO,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) # .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,LDA,N # CHARACTER UPLO # .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function symv!(uplo::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::Union{($elty), Bool}, y::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x, y) m, n = size(A) if m != n throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) end if n != length(x) throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) end if m != length(y) throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) end chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), $lib), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, alpha, A, max(1,stride(A,2)), px, stx, beta, py, sty, 1) y end function symv(uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) symv!(uplo, alpha, A, x, zero($elty), similar(x)) end function symv(uplo::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) symv(uplo, one($elty), A, x) end end end """ symv(ul, alpha, A, x) Return `alpha*A*x`. `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. `alpha` is a scalar. """ symv(ul, alpha, A, x) """ symv(ul, A, x) Return `A*x`. `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ symv(ul, A, x) ### hemv """ hemv!(ul, alpha, A, x, beta, y) Update the vector `y` as `alpha*A*x + beta*y`. `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. `alpha` and `beta` are scalars. Return the updated `y`. """ function hemv! end for (fname, elty) in ((:zhemv_,:ComplexF64), (:chemv_,:ComplexF32)) @eval begin function hemv!(uplo::AbstractChar, ฮฑ::Union{$elty, Bool}, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, ฮฒ::Union{$elty, Bool}, y::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x, y) m, n = size(A) if m != n throw(DimensionMismatch(lazy"matrix A is $m by $n but must be square")) end if n != length(x) throw(DimensionMismatch(lazy"A has size $(size(A)), and x has length $(length(x))")) end if m != length(y) throw(DimensionMismatch(lazy"A has size $(size(A)), and y has length $(length(y))")) end chkstride1(A) lda = max(1, stride(A, 2)) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, ฮฑ, A, lda, px, stx, ฮฒ, py, sty, 1) y end function hemv(uplo::AbstractChar, ฮฑ::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) hemv!(uplo, ฮฑ, A, x, zero($elty), similar(x)) end function hemv(uplo::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) hemv(uplo, one($elty), A, x) end end end """ hemv(ul, alpha, A, x) Return `alpha*A*x`. `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. `alpha` is a scalar. """ hemv(ul, alpha, A, x) """ hemv(ul, A, x) Return `A*x`. `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ hemv(ul, A, x) ### hpmv!, (HP) Hermitian packed matrix-vector operation defined as y := alpha*A*x + beta*y. for (fname, elty) in ((:zhpmv_, :ComplexF64), (:chpmv_, :ComplexF32)) @eval begin # SUBROUTINE ZHPMV(UPLO,N,ALPHA,AP,X,INCX,BETA,Y,INCY) # Y <- ALPHA*AP*X + BETA*Y # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,N # CHARACTER UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(N,N),X(N),Y(N) function hpmv!(uplo::AbstractChar, n::Integer, ฮฑ::$elty, AP::Union{Ptr{$elty}, AbstractArray{$elty}}, x::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, ฮฒ::$elty, y::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, # uplo, Ref{BlasInt}, # n, Ref{$elty}, # ฮฑ, Ptr{$elty}, # AP, Ptr{$elty}, # x, Ref{BlasInt}, # incx, Ref{$elty}, # ฮฒ, Ptr{$elty}, # y, output Ref{BlasInt}, # incy Clong), # length of uplo uplo, n, ฮฑ, AP, x, incx, ฮฒ, y, incy, 1) return y end end end function hpmv!(uplo::AbstractChar, ฮฑ::Number, AP::AbstractArray{T}, x::AbstractArray{T}, ฮฒ::Number, y::AbstractArray{T}) where {T <: BlasComplex} require_one_based_indexing(AP, x, y) N = length(x) if N != length(y) throw(DimensionMismatch(lazy"x has length $(N), but y has length $(length(y))")) end if 2*length(AP) < N*(N + 1) throw(DimensionMismatch(lazy"Packed hermitian matrix A has size smaller than length(x) = $(N).")) end chkstride1(AP) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y hpmv!(uplo, N, T(ฮฑ), AP, px, stx, T(ฮฒ), py, sty) y end """ hpmv!(uplo, ฮฑ, AP, x, ฮฒ, y) Update vector `y` as `ฮฑ*A*x + ฮฒ*y`, where `A` is a Hermitian matrix provided in packed format `AP`. With `uplo = 'U'`, the array AP must contain the upper triangular part of the Hermitian matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` respectively, and so on. With `uplo = 'L'`, the array AP must contain the lower triangular part of the Hermitian matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` respectively, and so on. The scalar inputs `ฮฑ` and `ฮฒ` must be complex or real numbers. The array inputs `x`, `y` and `AP` must all be of `ComplexF32` or `ComplexF64` type. Return the updated `y`. !!! compat "Julia 1.5" `hpmv!` requires at least Julia 1.5. """ hpmv! ### sbmv, (SB) symmetric banded matrix-vector multiplication for (fname, elty) in ((:dsbmv_,:Float64), (:ssbmv_,:Float32)) @eval begin # SUBROUTINE DSBMV(UPLO,N,K,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,K,LDA,N # CHARACTER UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function sbmv!(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::($elty), y::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x, y) chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, size(A,2), k, alpha, A, max(1,stride(A,2)), px, stx, beta, py, sty, 1) y end function sbmv(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) n = size(A,2) sbmv!(uplo, k, alpha, A, x, zero($elty), similar(x, $elty, n)) end function sbmv(uplo::AbstractChar, k::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) sbmv(uplo, k, one($elty), A, x) end end end """ sbmv(uplo, k, alpha, A, x) Return `alpha*A*x` where `A` is a symmetric band matrix of order `size(A,2)` with `k` super-diagonals stored in the argument `A`. Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. """ sbmv(uplo, k, alpha, A, x) """ sbmv(uplo, k, A, x) Return `A*x` where `A` is a symmetric band matrix of order `size(A,2)` with `k` super-diagonals stored in the argument `A`. Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. """ sbmv(uplo, k, A, x) """ sbmv!(uplo, k, alpha, A, x, beta, y) Update vector `y` as `alpha*A*x + beta*y` where `A` is a symmetric band matrix of order `size(A,2)` with `k` super-diagonals stored in the argument `A`. The storage layout for `A` is described the reference BLAS module, level-2 BLAS at . Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `A` is used. Return the updated `y`. """ sbmv! ### spmv!, (SP) symmetric packed matrix-vector operation defined as y := alpha*A*x + beta*y. for (fname, elty) in ((:dspmv_, :Float64), (:sspmv_, :Float32)) @eval begin # SUBROUTINE DSPMV(UPLO,N,ALPHA,AP,X,INCX,BETA,Y,INCY) # Y <- ALPHA*AP*X + BETA*Y # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,N # CHARACTER UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(N,N),X(N),Y(N) function spmv!(uplo::AbstractChar, n::Integer, ฮฑ::$elty, AP::Union{Ptr{$elty}, AbstractArray{$elty}}, x::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, ฮฒ::$elty, y::Union{Ptr{$elty}, AbstractArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, # uplo, Ref{BlasInt}, # n, Ref{$elty}, # ฮฑ, Ptr{$elty}, # AP, Ptr{$elty}, # x, Ref{BlasInt}, # incx, Ref{$elty}, # ฮฒ, Ptr{$elty}, # y, out Ref{BlasInt}, # incy Clong), # length of uplo uplo, n, ฮฑ, AP, x, incx, ฮฒ, y, incy, 1) return y end end end function spmv!(uplo::AbstractChar, ฮฑ::Real, AP::AbstractArray{T}, x::AbstractArray{T}, ฮฒ::Real, y::AbstractArray{T}) where {T <: BlasReal} require_one_based_indexing(AP, x, y) N = length(x) if N != length(y) throw(DimensionMismatch(lazy"x has length $(N), but y has length $(length(y))")) end if 2*length(AP) < N*(N + 1) throw(DimensionMismatch(lazy"Packed symmetric matrix A has size smaller than length(x) = $(N).")) end chkstride1(AP) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y spmv!(uplo, N, T(ฮฑ), AP, px, stx, T(ฮฒ), py, sty) y end """ spmv!(uplo, ฮฑ, AP, x, ฮฒ, y) Update vector `y` as `ฮฑ*A*x + ฮฒ*y`, where `A` is a symmetric matrix provided in packed format `AP`. With `uplo = 'U'`, the array AP must contain the upper triangular part of the symmetric matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` respectively, and so on. With `uplo = 'L'`, the array AP must contain the lower triangular part of the symmetric matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` respectively, and so on. The scalar inputs `ฮฑ` and `ฮฒ` must be real. The array inputs `x`, `y` and `AP` must all be of `Float32` or `Float64` type. Return the updated `y`. !!! compat "Julia 1.5" `spmv!` requires at least Julia 1.5. """ spmv! ### spr!, (SP) symmetric packed matrix-vector operation defined as A := alpha*x*x' + A for (fname, elty) in ((:dspr_, :Float64), (:sspr_, :Float32)) @eval begin function spr!(uplo::AbstractChar, n::Integer, ฮฑ::$elty, x::Union{Ptr{$elty}, AbstractArray{$elty}}, incx::Integer, AP::Union{Ptr{$elty}, AbstractArray{$elty}}) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, # uplo, Ref{BlasInt}, # n, Ref{$elty}, # ฮฑ, Ptr{$elty}, # x, Ref{BlasInt}, # incx, Ptr{$elty}, # AP, Clong), # length of uplo uplo, n, ฮฑ, x, incx, AP, 1) return AP end end end function spr!(uplo::AbstractChar, ฮฑ::Real, x::AbstractArray{T}, AP::AbstractArray{T}) where {T <: BlasReal} chkuplo(uplo) require_one_based_indexing(AP, x) N = length(x) if 2*length(AP) < N*(N + 1) throw(DimensionMismatch(lazy"Packed symmetric matrix A has size smaller than length(x) = $(N).")) end chkstride1(AP) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) return GC.@preserve x spr!(uplo, N, T(ฮฑ), px, stx , AP) end """ spr!(uplo, ฮฑ, x, AP) Update matrix `A` as `A+ฮฑ*x*x'`, where `A` is a symmetric matrix provided in packed format `AP` and `x` is a vector. With `uplo = 'U'`, the array AP must contain the upper triangular part of the symmetric matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[1, 2]` and `A[2, 2]` respectively, and so on. With `uplo = 'L'`, the array AP must contain the lower triangular part of the symmetric matrix packed sequentially, column by column, so that `AP[1]` contains `A[1, 1]`, `AP[2]` and `AP[3]` contain `A[2, 1]` and `A[3, 1]` respectively, and so on. The scalar input `ฮฑ` must be real. The array inputs `x` and `AP` must all be of `Float32` or `Float64` type. Return the updated `AP`. !!! compat "Julia 1.8" `spr!` requires at least Julia 1.8. """ spr! ### hbmv, (HB) Hermitian banded matrix-vector multiplication for (fname, elty) in ((:zhbmv_,:ComplexF64), (:chbmv_,:ComplexF32)) @eval begin # SUBROUTINE ZHBMV(UPLO,N,K,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER INCX,INCY,K,LDA,N # CHARACTER UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function hbmv!(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}, beta::($elty), y::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x, y) chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("dest vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, size(A,2), k, alpha, A, max(1,stride(A,2)), px, stx, beta, py, sty, 1) y end function hbmv(uplo::AbstractChar, k::Integer, alpha::($elty), A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) n = size(A,2) hbmv!(uplo, k, alpha, A, x, zero($elty), similar(x, $elty, n)) end function hbmv(uplo::AbstractChar, k::Integer, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) hbmv(uplo, k, one($elty), A, x) end end end ### trmv, Triangular matrix-vector multiplication """ trmv(ul, tA, dA, A, b) Return `op(A)*b`, where `op` is determined by [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. """ function trmv end """ trmv!(ul, tA, dA, A, b) Return `op(A)*b`, where `op` is determined by [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. The multiplication occurs in-place on `b`. """ function trmv! end for (fname, elty) in ((:dtrmv_,:Float64), (:strmv_,:Float32), (:ztrmv_,:ComplexF64), (:ctrmv_,:ComplexF32)) @eval begin # SUBROUTINE DTRMV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) # * .. Scalar Arguments .. # INTEGER INCX,LDA,N # CHARACTER DIAG,TRANS,UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*) function trmv!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x) n = checksquare(A) if n != length(x) throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, A, max(1,stride(A,2)), px, stx, 1, 1, 1) x end function trmv(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) trmv!(uplo, trans, diag, A, copy(x)) end end end ### trsv, Triangular matrix-vector solve """ trsv!(ul, tA, dA, A, b) Overwrite `b` with the solution to `A*x = b` or one of the other two variants determined by [`tA`](@ref stdlib-blas-trans) and [`ul`](@ref stdlib-blas-uplo). [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. Return the updated `b`. """ function trsv! end """ trsv(ul, tA, dA, A, b) Return the solution to `A*x = b` or one of the other two variants determined by [`tA`](@ref stdlib-blas-trans) and [`ul`](@ref stdlib-blas-uplo). [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. """ function trsv end for (fname, elty) in ((:dtrsv_,:Float64), (:strsv_,:Float32), (:ztrsv_,:ComplexF64), (:ctrsv_,:ComplexF32)) @eval begin # SUBROUTINE DTRSV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) # .. Scalar Arguments .. # INTEGER INCX,LDA,N # CHARACTER DIAG,TRANS,UPLO # .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),X(*) function trsv!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) chkuplo(uplo) require_one_based_indexing(A, x) n = checksquare(A) if n != length(x) throw(DimensionMismatch(lazy"size of A is $n != length(x) = $(length(x))")) end chkstride1(A) px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, A, max(1,stride(A,2)), px, stx, 1, 1, 1) x end function trsv(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, x::AbstractVector{$elty}) trsv!(uplo, trans, diag, A, copy(x)) end end end ### ger """ ger!(alpha, x, y, A) Rank-1 update of the matrix `A` with vectors `x` and `y` as `alpha*x*y' + A`. """ function ger! end for (fname, elty) in ((:dger_,:Float64), (:sger_,:Float32), (:zgerc_,:ComplexF64), (:cgerc_,:ComplexF32)) @eval begin function ger!(ฮฑ::$elty, x::AbstractVector{$elty}, y::AbstractVector{$elty}, A::AbstractMatrix{$elty}) require_one_based_indexing(A, x, y) m, n = size(A) if m != length(x) || n != length(y) throw(DimensionMismatch(lazy"A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) end px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) py, sty = vec_pointer_stride(y, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x y ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), m, n, ฮฑ, px, stx, py, sty, A, max(1,stride(A,2))) A end end end ### syr """ syr!(uplo, alpha, x, A) Rank-1 update of the symmetric matrix `A` with vector `x` as `alpha*x*transpose(x) + A`. [`uplo`](@ref stdlib-blas-uplo) controls which triangle of `A` is updated. Returns `A`. """ function syr! end for (fname, elty, lib) in ((:dsyr_,:Float64,libblastrampoline), (:ssyr_,:Float32,libblastrampoline), (:zsyr_,:ComplexF64,libblastrampoline), (:csyr_,:ComplexF32,libblastrampoline)) @eval begin function syr!(uplo::AbstractChar, ฮฑ::$elty, x::AbstractVector{$elty}, A::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, x) n = checksquare(A) if length(x) != n throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x ccall((@blasfunc($fname), $lib), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), uplo, n, ฮฑ, px, stx, A, max(1,stride(A, 2))) A end end end ### her """ her!(uplo, alpha, x, A) Methods for complex arrays only. Rank-1 update of the Hermitian matrix `A` with vector `x` as `alpha*x*x' + A`. [`uplo`](@ref stdlib-blas-uplo) controls which triangle of `A` is updated. Returns `A`. """ function her! end for (fname, elty, relty) in ((:zher_,:ComplexF64, :Float64), (:cher_,:ComplexF32, :Float32)) @eval begin function her!(uplo::AbstractChar, ฮฑ::$relty, x::AbstractVector{$elty}, A::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, x) n = checksquare(A) if length(x) != n throw(DimensionMismatch(lazy"A has size ($n,$n), x has length $(length(x))")) end px, stx = vec_pointer_stride(x, ArgumentError("input vector with 0 stride is not allowed")) GC.@preserve x ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong), uplo, n, ฮฑ, px, stx, A, max(1,stride(A,2)), 1) A end end end # Level 3 ## (GE) general matrix-matrix multiplication """ gemm!(tA, tB, alpha, A, B, beta, C) Update `C` as `alpha*A*B + beta*C` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. Return the updated `C`. """ function gemm! end for (gemm, elty) in ((:dgemm_,:Float64), (:sgemm_,:Float32), (:zgemm_,:ComplexF64), (:cgemm_,:ComplexF32)) @eval begin # SUBROUTINE DGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER K,LDA,LDB,LDC,M,N # CHARACTER TRANSA,TRANSB # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) function gemm!(transA::AbstractChar, transB::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, beta::Union{($elty), Bool}, C::AbstractVecOrMat{$elty}) # if any([stride(A,1), stride(B,1), stride(C,1)] .!= 1) # error("gemm!: BLAS module requires contiguous matrix columns") # end # should this be checked on every call? require_one_based_indexing(A, B, C) m = size(A, transA == 'N' ? 1 : 2) ka = size(A, transA == 'N' ? 2 : 1) kb = size(B, transB == 'N' ? 1 : 2) n = size(B, transB == 'N' ? 2 : 1) if ka != kb || m != size(C,1) || n != size(C,2) throw(DimensionMismatch(lazy"A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) end chkstride1(A) chkstride1(B) chkstride1(C) ccall((@blasfunc($gemm), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), transA, transB, m, n, ka, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, C, max(1,stride(C,2)), 1, 1) C end function gemm(transA::AbstractChar, transB::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) gemm!(transA, transB, alpha, A, B, zero($elty), similar(B, $elty, (size(A, transA == 'N' ? 1 : 2), size(B, transB == 'N' ? 2 : 1)))) end function gemm(transA::AbstractChar, transB::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) gemm(transA, transB, one($elty), A, B) end end end """ gemm(tA, tB, alpha, A, B) Return `alpha*A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. """ gemm(tA, tB, alpha, A, B) """ gemm(tA, tB, A, B) Return `A*B` or the other three variants according to [`tA`](@ref stdlib-blas-trans) and `tB`. """ gemm(tA, tB, A, B) ## (SY) symmetric matrix-matrix and matrix-vector multiplication for (mfname, elty) in ((:dsymm_,:Float64), (:ssymm_,:Float32), (:zsymm_,:ComplexF64), (:csymm_,:ComplexF32)) @eval begin # SUBROUTINE DSYMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) # .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER LDA,LDB,LDC,M,N # CHARACTER SIDE,UPLO # .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) function symm!(side::AbstractChar, uplo::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, B, C) m, n = size(C) j = checksquare(A) M, N = size(B) if side == 'L' if j != m throw(DimensionMismatch(lazy"A has first dimension $j but needs to match first dimension of C, $m")) end if N != n throw(DimensionMismatch(lazy"B has second dimension $N but needs to match second dimension of C, $n")) end if j != M throw(DimensionMismatch(lazy"A has second dimension $j but needs to match first dimension of B, $M")) end else if j != n throw(DimensionMismatch(lazy"B has second dimension $j but needs to match second dimension of C, $n")) end if N != j throw(DimensionMismatch(lazy"A has second dimension $N but needs to match first dimension of B, $j")) end if M != m throw(DimensionMismatch(lazy"A has first dimension $M but needs to match first dimension of C, $m")) end end chkstride1(A) chkstride1(B) chkstride1(C) ccall((@blasfunc($mfname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), side, uplo, m, n, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, C, max(1,stride(C,2)), 1, 1) C end function symm(side::AbstractChar, uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) symm!(side, uplo, alpha, A, B, zero($elty), similar(B)) end function symm(side::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) symm(side, uplo, one($elty), A, B) end end end """ symm(side, ul, alpha, A, B) Return `alpha*A*B` or `alpha*B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ symm(side, ul, alpha, A, B) """ symm(side, ul, A, B) Return `A*B` or `B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ symm(side, ul, A, B) """ symm!(side, ul, alpha, A, B, beta, C) Update `C` as `alpha*A*B + beta*C` or `alpha*B*A + beta*C` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be symmetric. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. Return the updated `C`. """ symm! ## (HE) Hermitian matrix-matrix and matrix-vector multiplication for (mfname, elty) in ((:zhemm_,:ComplexF64), (:chemm_,:ComplexF32)) @eval begin # SUBROUTINE DHEMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) # .. Scalar Arguments .. # DOUBLE PRECISION ALPHA,BETA # INTEGER LDA,LDB,LDC,M,N # CHARACTER SIDE,UPLO # .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) function hemm!(side::AbstractChar, uplo::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, B, C) m, n = size(C) j = checksquare(A) M, N = size(B) if side == 'L' if j != m throw(DimensionMismatch(lazy"A has first dimension $j but needs to match first dimension of C, $m")) end if N != n throw(DimensionMismatch(lazy"B has second dimension $N but needs to match second dimension of C, $n")) end if j != M throw(DimensionMismatch(lazy"A has second dimension $j but needs to match first dimension of B, $M")) end else if j != n throw(DimensionMismatch(lazy"B has second dimension $j but needs to match second dimension of C, $n")) end if N != j throw(DimensionMismatch(lazy"A has second dimension $N but needs to match first dimension of B, $j")) end if M != m throw(DimensionMismatch(lazy"A has first dimension $M but needs to match first dimension of C, $m")) end end chkstride1(A) chkstride1(B) chkstride1(C) ccall((@blasfunc($mfname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), side, uplo, m, n, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, C, max(1,stride(C,2)), 1, 1) C end function hemm(side::AbstractChar, uplo::AbstractChar, alpha::($elty), A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) hemm!(side, uplo, alpha, A, B, zero($elty), similar(B)) end function hemm(side::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) hemm(side, uplo, one($elty), A, B) end end end """ hemm(side, ul, alpha, A, B) Return `alpha*A*B` or `alpha*B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ hemm(side, ul, alpha, A, B) """ hemm(side, ul, A, B) Return `A*B` or `B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. """ hemm(side, ul, A, B) """ hemm!(side, ul, alpha, A, B, beta, C) Update `C` as `alpha*A*B + beta*C` or `alpha*B*A + beta*C` according to [`side`](@ref stdlib-blas-side). `A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. Return the updated `C`. """ hemm! ## syrk """ syrk!(uplo, trans, alpha, A, beta, C) Rank-k update of the symmetric matrix `C` as `alpha*A*transpose(A) + beta*C` or `alpha*transpose(A)*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Return `C`. """ function syrk! end """ syrk(uplo, trans, alpha, A) Return either the upper triangle or the lower triangle of `A`, according to [`uplo`](@ref stdlib-blas-uplo), of `alpha*A*transpose(A)` or `alpha*transpose(A)*A`, according to [`trans`](@ref stdlib-blas-trans). """ function syrk end for (fname, elty) in ((:dsyrk_,:Float64), (:ssyrk_,:Float32), (:zsyrk_,:ComplexF64), (:csyrk_,:ComplexF32)) @eval begin # SUBROUTINE DSYRK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) # * .. Scalar Arguments .. # REAL ALPHA,BETA # INTEGER K,LDA,LDC,N # CHARACTER TRANS,UPLO # * .. Array Arguments .. # REAL A(LDA,*),C(LDC,*) function syrk!(uplo::AbstractChar, trans::AbstractChar, alpha::Union{($elty), Bool}, A::AbstractVecOrMat{$elty}, beta::Union{($elty), Bool}, C::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) chkstride1(A) chkstride1(C) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), uplo, trans, n, k, alpha, A, max(1,stride(A,2)), beta, C, max(1,stride(C,2)), 1, 1) C end end end function syrk(uplo::AbstractChar, trans::AbstractChar, alpha::Number, A::AbstractVecOrMat) T = eltype(A) n = size(A, trans == 'N' ? 1 : 2) syrk!(uplo, trans, convert(T,alpha), A, zero(T), similar(A, T, (n, n))) end syrk(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat) = syrk(uplo, trans, one(eltype(A)), A) """ herk!(uplo, trans, alpha, A, beta, C) Methods for complex arrays only. Rank-k update of the Hermitian matrix `C` as `alpha*A*A' + beta*C` or `alpha*A'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is updated. Returns `C`. """ function herk! end """ herk(uplo, trans, alpha, A) Methods for complex arrays only. Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*A'` or `alpha*A'*A`, according to [`trans`](@ref stdlib-blas-trans). """ function herk end for (fname, elty, relty) in ((:zherk_, :ComplexF64, :Float64), (:cherk_, :ComplexF32, :Float32)) @eval begin # SUBROUTINE CHERK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) # * .. Scalar Arguments .. # REAL ALPHA,BETA # INTEGER K,LDA,LDC,N # CHARACTER TRANS,UPLO # * .. # * .. Array Arguments .. # COMPLEX A(LDA,*),C(LDC,*) function herk!(uplo::AbstractChar, trans::AbstractChar, ฮฑ::Union{$relty, Bool}, A::AbstractVecOrMat{$elty}, ฮฒ::Union{$relty, Bool}, C::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) if nn != n throw(DimensionMismatch(lazy"the matrix to update has dimension $n but the implied dimension of the update is $(size(A, trans == 'N' ? 1 : 2))")) end chkstride1(A) chkstride1(C) k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), uplo, trans, n, k, ฮฑ, A, max(1,stride(A,2)), ฮฒ, C, max(1,stride(C,2)), 1, 1) C end function herk(uplo::AbstractChar, trans::AbstractChar, ฮฑ::$relty, A::AbstractVecOrMat{$elty}) n = size(A, trans == 'N' ? 1 : 2) herk!(uplo, trans, ฮฑ, A, zero($relty), similar(A, (n,n))) end herk(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat{$elty}) = herk(uplo, trans, one($relty), A) end end ## syr2k for (fname, elty) in ((:dsyr2k_,:Float64), (:ssyr2k_,:Float32), (:zsyr2k_,:ComplexF64), (:csyr2k_,:ComplexF32)) @eval begin # SUBROUTINE DSYR2K(UPLO,TRANS,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) # # .. Scalar Arguments .. # REAL PRECISION ALPHA,BETA # INTEGER K,LDA,LDB,LDC,N # CHARACTER TRANS,UPLO # .. # .. Array Arguments .. # REAL PRECISION A(LDA,*),B(LDB,*),C(LDC,*) function syr2k!(uplo::AbstractChar, trans::AbstractChar, alpha::($elty), A::AbstractVecOrMat{$elty}, B::AbstractVecOrMat{$elty}, beta::($elty), C::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, B, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) chkstride1(A) chkstride1(B) chkstride1(C) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong), uplo, trans, n, k, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, C, max(1,stride(C,2)), 1, 1) C end end end """ syr2k!(uplo, trans, alpha, A, B, beta, C) Rank-2k update of the symmetric matrix `C` as `alpha*A*transpose(B) + alpha*B*transpose(A) + beta*C` or `alpha*transpose(A)*B + alpha*transpose(B)*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Returns `C`. """ function syr2k! end """ syr2k(uplo, trans, alpha, A, B) Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*transpose(B) + alpha*B*transpose(A)` or `alpha*transpose(A)*B + alpha*transpose(B)*A`, according to [`trans`](@ref stdlib-blas-trans). """ function syr2k(uplo::AbstractChar, trans::AbstractChar, alpha::Number, A::AbstractVecOrMat, B::AbstractVecOrMat) T = eltype(A) n = size(A, trans == 'N' ? 1 : 2) syr2k!(uplo, trans, convert(T,alpha), A, B, zero(T), similar(A, T, (n, n))) end """ syr2k(uplo, trans, A, B) Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*transpose(B) + B*transpose(A)` or `transpose(A)*B + transpose(B)*A`, according to [`trans`](@ref stdlib-blas-trans). """ syr2k(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat, B::AbstractVecOrMat) = syr2k(uplo, trans, one(eltype(A)), A, B) for (fname, elty1, elty2) in ((:zher2k_,:ComplexF64,:Float64), (:cher2k_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE CHER2K(UPLO,TRANS,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) # # .. Scalar Arguments .. # COMPLEX ALPHA # REAL BETA # INTEGER K,LDA,LDB,LDC,N # CHARACTER TRANS,UPLO # .. # .. Array Arguments .. # COMPLEX A(LDA,*),B(LDB,*),C(LDC,*) function her2k!(uplo::AbstractChar, trans::AbstractChar, alpha::($elty1), A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}, beta::($elty2), C::AbstractMatrix{$elty1}) chkuplo(uplo) require_one_based_indexing(A, B, C) n = checksquare(C) nn = size(A, trans == 'N' ? 1 : 2) if nn != n throw(DimensionMismatch(lazy"C has size ($n,$n), corresponding dimension of A is $nn")) end chkstride1(A) chkstride1(B) chkstride1(C) k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty1}, Ptr{$elty1}, Ref{BlasInt}, Ptr{$elty1}, Ref{BlasInt}, Ref{$elty2}, Ptr{$elty1}, Ref{BlasInt}, Clong, Clong), uplo, trans, n, k, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, C, max(1,stride(C,2)), 1, 1) C end function her2k(uplo::AbstractChar, trans::AbstractChar, alpha::($elty1), A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}) n = size(A, trans == 'N' ? 1 : 2) her2k!(uplo, trans, alpha, A, B, zero($elty2), similar(A, $elty1, (n,n))) end her2k(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat{$elty1}, B::AbstractVecOrMat{$elty1}) = her2k(uplo, trans, one($elty1), A, B) end end """ her2k!(uplo, trans, alpha, A, B, beta, C) Rank-2k update of the Hermitian matrix `C` as `alpha*A*B' + alpha*B*A' + beta*C` or `alpha*A'*B + alpha*B'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). The scalar `beta` has to be real. Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Return `C`. """ function her2k! end """ her2k(uplo, trans, alpha, A, B) Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*B' + alpha*B*A'` or `alpha*A'*B + alpha*B'*A`, according to [`trans`](@ref stdlib-blas-trans). """ her2k(uplo, trans, alpha, A, B) """ her2k(uplo, trans, A, B) Return the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*B' + B*A'` or `A'*B + B'*A`, according to [`trans`](@ref stdlib-blas-trans). """ her2k(uplo, trans, A, B) ## (TR) Triangular matrix and vector multiplication and solution """ trmm!(side, ul, tA, dA, alpha, A, B) Update `B` as `alpha*A*B` or one of the other three variants determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. Return the updated `B`. """ function trmm! end """ trmm(side, ul, tA, dA, alpha, A, B) Return `alpha*A*B` or one of the other three variants determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. """ function trmm end """ trsm!(side, ul, tA, dA, alpha, A, B) Overwrite `B` with the solution to `A*X = alpha*B` or one of the other three variants determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. Returns the updated `B`. """ function trsm! end """ trsm(side, ul, tA, dA, alpha, A, B) Return the solution to `A*X = alpha*B` or one of the other three variants determined by determined by [`side`](@ref stdlib-blas-side) and [`tA`](@ref stdlib-blas-trans). Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. [`dA`](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones. """ function trsm end for (mmname, smname, elty) in ((:dtrmm_,:dtrsm_,:Float64), (:strmm_,:strsm_,:Float32), (:ztrmm_,:ztrsm_,:ComplexF64), (:ctrmm_,:ctrsm_,:ComplexF32)) @eval begin # SUBROUTINE DTRMM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA # INTEGER LDA,LDB,M,N # CHARACTER DIAG,SIDE,TRANSA,UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),B(LDB,*) function trmm!(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::Number, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, B) m, n = size(B) nA = checksquare(A) if nA != (side == 'L' ? m : n) throw(DimensionMismatch(lazy"size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) end chkstride1(A) chkstride1(B) ccall((@blasfunc($mmname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong, Clong, Clong), side, uplo, transa, diag, m, n, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), 1, 1, 1, 1) B end function trmm(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) trmm!(side, uplo, transa, diag, alpha, A, copy(B)) end # SUBROUTINE DTRSM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) # * .. Scalar Arguments .. # DOUBLE PRECISION ALPHA # INTEGER LDA,LDB,M,N # CHARACTER DIAG,SIDE,TRANSA,UPLO # * .. Array Arguments .. # DOUBLE PRECISION A(LDA,*),B(LDB,*) function trsm!(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkuplo(uplo) require_one_based_indexing(A, B) m, n = size(B) k = checksquare(A) if k != (side == 'L' ? m : n) throw(DimensionMismatch(lazy"size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) end chkstride1(A) chkstride1(B) ccall((@blasfunc($smname), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Clong, Clong, Clong, Clong), side, uplo, transa, diag, m, n, alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), 1, 1, 1, 1) B end function trsm(side::AbstractChar, uplo::AbstractChar, transa::AbstractChar, diag::AbstractChar, alpha::$elty, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) trsm!(side, uplo, transa, diag, alpha, A, copy(B)) end end end end # module function copyto!(dest::Array{T}, rdest::AbstractRange{Ti}, src::Array{T}, rsrc::AbstractRange{Ti}) where {T<:BlasFloat,Ti<:Integer} if minimum(rdest) < 1 || maximum(rdest) > length(dest) throw(ArgumentError(lazy"range out of bounds for dest, of length $(length(dest))")) end if minimum(rsrc) < 1 || maximum(rsrc) > length(src) throw(ArgumentError(lazy"range out of bounds for src, of length $(length(src))")) end if length(rdest) != length(rsrc) throw(DimensionMismatch(lazy"ranges must be of the same length")) end GC.@preserve src dest BLAS.blascopy!( length(rsrc), pointer(src, minimum(rsrc)), step(rsrc), pointer(dest, minimum(rdest)), step(rdest)) return dest end t/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/lbt.jlP+# This file is a part of Julia. License is MIT: https://julialang.org/license ## This file contains libblastrampoline-specific APIs # Keep these in sync with `src/libblastrampoline_internal.h` struct lbt_library_info_t libname::Cstring handle::Ptr{Cvoid} suffix::Cstring active_forwards::Ptr{UInt8} interface::Int32 complex_retstyle::Int32 f2c::Int32 cblas::Int32 end const LBT_INTERFACE_LP64 = 32 const LBT_INTERFACE_ILP64 = 64 const LBT_INTERFACE_UNKNOWN = -1 const LBT_INTERFACE_MAP = Dict( LBT_INTERFACE_LP64 => :lp64, LBT_INTERFACE_ILP64 => :ilp64, LBT_INTERFACE_UNKNOWN => :unknown, ) const LBT_INV_INTERFACE_MAP = Dict(v => k for (k, v) in LBT_INTERFACE_MAP) const LBT_F2C_PLAIN = 0 const LBT_F2C_REQUIRED = 1 const LBT_F2C_UNKNOWN = -1 const LBT_F2C_MAP = Dict( LBT_F2C_PLAIN => :plain, LBT_F2C_REQUIRED => :required, LBT_F2C_UNKNOWN => :unknown, ) const LBT_INV_F2C_MAP = Dict(v => k for (k, v) in LBT_F2C_MAP) const LBT_COMPLEX_RETSTYLE_NORMAL = 0 const LBT_COMPLEX_RETSTYLE_ARGUMENT = 1 const LBT_COMPLEX_RETSTYLE_UNKNOWN = -1 const LBT_COMPLEX_RETSTYLE_MAP = Dict( LBT_COMPLEX_RETSTYLE_NORMAL => :normal, LBT_COMPLEX_RETSTYLE_ARGUMENT => :argument, LBT_COMPLEX_RETSTYLE_UNKNOWN => :unknown, ) const LBT_INV_COMPLEX_RETSTYLE_MAP = Dict(v => k for (k, v) in LBT_COMPLEX_RETSTYLE_MAP) const LBT_CBLAS_CONFORMANT = 0 const LBT_CBLAS_DIVERGENT = 1 const LBT_CBLAS_UNKNOWN = -1 const LBT_CBLAS_MAP = Dict( LBT_CBLAS_CONFORMANT => :conformant, LBT_CBLAS_DIVERGENT => :divergent, LBT_CBLAS_UNKNOWN => :unknown, ) const LBT_INV_CBLAS_MAP = Dict(v => k for (k, v) in LBT_CBLAS_MAP) struct LBTLibraryInfo libname::String handle::Ptr{Cvoid} suffix::String active_forwards::Vector{UInt8} interface::Symbol complex_retstyle::Symbol f2c::Symbol cblas::Symbol function LBTLibraryInfo(lib_info::lbt_library_info_t, num_exported_symbols::UInt32) return new( unsafe_string(lib_info.libname), lib_info.handle, unsafe_string(lib_info.suffix), unsafe_wrap(Vector{UInt8}, lib_info.active_forwards, div(num_exported_symbols,8)+1), LBT_INTERFACE_MAP[lib_info.interface], LBT_COMPLEX_RETSTYLE_MAP[lib_info.complex_retstyle], LBT_F2C_MAP[lib_info.f2c], LBT_CBLAS_MAP[lib_info.cblas], ) end end struct lbt_config_t loaded_libs::Ptr{Ptr{lbt_library_info_t}} build_flags::UInt32 exported_symbols::Ptr{Cstring} num_exported_symbols::UInt32 end const LBT_BUILDFLAGS_DEEPBINDLESS = 0x01 const LBT_BUILDFLAGS_F2C_CAPABLE = 0x02 const LBT_BUILDFLAGS_CBLAS_DIVERGENCE = 0x04 const LBT_BUILDFLAGS_COMPLEX_RETSTYLE = 0x08 const LBT_BUILDFLAGS_SYMBOL_TRIMMING = 0x10 const LBT_BUILDFLAGS_MAP = Dict( LBT_BUILDFLAGS_DEEPBINDLESS => :deepbindless, LBT_BUILDFLAGS_F2C_CAPABLE => :f2c_capable, LBT_BUILDFLAGS_CBLAS_DIVERGENCE => :cblas_divergence, LBT_BUILDFLAGS_COMPLEX_RETSTYLE => :complex_retstyle, LBT_BUILDFLAGS_SYMBOL_TRIMMING => :symbol_trimming, ) struct LBTConfig loaded_libs::Vector{LBTLibraryInfo} build_flags::Vector{Symbol} exported_symbols::Vector{String} function LBTConfig(config::lbt_config_t) # Decode OR'ed flags into a list of names build_flag_names = Symbol[] for (flag, name) in LBT_BUILDFLAGS_MAP if config.build_flags & flag != 0x00 push!(build_flag_names, name) end end # Load all exported symbol names exported_symbols = String[] for sym_idx in 1:config.num_exported_symbols str_ptr = unsafe_load(config.exported_symbols, sym_idx) if str_ptr != C_NULL push!(exported_symbols, unsafe_string(str_ptr)) else @error("NULL string in lbt_config.exported_symbols[$(sym_idx)]") end end # Unpack library info structures libs = LBTLibraryInfo[] idx = 1 lib_ptr = unsafe_load(config.loaded_libs, idx) while lib_ptr != C_NULL push!(libs, LBTLibraryInfo(unsafe_load(lib_ptr), config.num_exported_symbols)) idx += 1 lib_ptr = unsafe_load(config.loaded_libs, idx) end return new( libs, build_flag_names, exported_symbols, ) end end Base.show(io::IO, lbt::LBTLibraryInfo) = print(io, "LBTLibraryInfo(", basename(lbt.libname), ", ", lbt.interface, ")") function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, lbt::LBTLibraryInfo) summary(io, lbt); println(io) println(io, "โ”œ Library: ", basename(lbt.libname)) println(io, "โ”œ Interface: ", lbt.interface) println(io, "โ”œ Complex return style: ", lbt.complex_retstyle) println(io, "โ”œ F2C: ", lbt.f2c) print(io, "โ”” CBLAS: ", lbt.cblas) end function Base.show(io::IO, lbt::LBTConfig) if length(lbt.loaded_libs) <= 3 print(io, "LBTConfig(") gen = (string("[", uppercase(string(l.interface)), "] ", basename(l.libname)) for l in lbt.loaded_libs) print(io, join(gen, ", ")) print(io, ")") else print(io, "LBTConfig(...)") end end function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, lbt::LBTConfig) summary(io, lbt); println(io) println(io, "Libraries: ") for (i,l) in enumerate(lbt.loaded_libs) char = i == length(lbt.loaded_libs) ? "โ””" : "โ”œ" interface_str = if l.interface === :ilp64 "ILP64" elseif l.interface === :lp64 " LP64" else "UNKWN" end print(io, char, " [", interface_str,"] ", basename(l.libname)) i !== length(lbt.loaded_libs) && println() end end mutable struct ConfigCache @atomic config::Union{Nothing,LBTConfig} lock::ReentrantLock end # In the event that users want to call `lbt_get_config()` multiple times (e.g. for # runtime checks of which BLAS vendor is providing a symbol), let's cache the value # and clear it only when someone calls something that would cause it to change. const _CACHED_CONFIG = ConfigCache(nothing, ReentrantLock()) function lbt_get_config() config = @atomic :acquire _CACHED_CONFIG.config config === nothing || return config return lock(_CACHED_CONFIG.lock) do local config = @atomic :monotonic _CACHED_CONFIG.config config === nothing || return config config_ptr = ccall((:lbt_get_config, libblastrampoline), Ptr{lbt_config_t}, ()) @atomic :release _CACHED_CONFIG.config = LBTConfig(unsafe_load(config_ptr)) end end function _clear_config_with(f) lock(_CACHED_CONFIG.lock) do @atomic :release _CACHED_CONFIG.config = nothing f() end end function lbt_get_num_threads() return ccall((:lbt_get_num_threads, libblastrampoline), Int32, ()) end function lbt_set_num_threads(nthreads) return ccall((:lbt_set_num_threads, libblastrampoline), Cvoid, (Int32,), nthreads) end function lbt_forward(path::AbstractString; clear::Bool = false, verbose::Bool = false, suffix_hint::Union{String,Nothing} = nothing) _clear_config_with() do return ccall((:lbt_forward, libblastrampoline), Int32, (Cstring, Int32, Int32, Cstring), path, clear ? 1 : 0, verbose ? 1 : 0, something(suffix_hint, C_NULL)) end end function lbt_set_default_func(addr) _clear_config_with() do return ccall((:lbt_set_default_func, libblastrampoline), Cvoid, (Ptr{Cvoid},), addr) end end function lbt_get_default_func() return ccall((:lbt_get_default_func, libblastrampoline), Ptr{Cvoid}, ()) end """ lbt_find_backing_library(symbol_name, interface; config::LBTConfig = lbt_get_config()) Return the `LBTLibraryInfo` that represents the backing library for the given symbol exported from libblastrampoline. This allows us to discover which library will service a particular BLAS call from Julia code. This method returns `nothing` if either of the following conditions are met: * No loaded library exports the desired symbol (the default function will be called) * The symbol was set via `lbt_set_forward()`, which does not track library provenance. If the given `symbol_name` is not contained within the list of exported symbols, an `ArgumentError` will be thrown. """ function lbt_find_backing_library(symbol_name, interface::Symbol; config::LBTConfig = lbt_get_config()) if interface โˆ‰ (:ilp64, :lp64) throw(ArgumentError("Invalid interface specification: '$(interface)'")) end symbol_idx = findfirst(s -> s == symbol_name, config.exported_symbols) if symbol_idx === nothing throw(ArgumentError("Invalid exported symbol name '$(symbol_name)'")) end # Convert to zero-indexed symbol_idx -= 1 forward_byte_offset = div(symbol_idx, 8) forward_byte_mask = 1 << mod(symbol_idx, 8) for lib in filter(l -> l.interface == interface, config.loaded_libs) if lib.active_forwards[forward_byte_offset+1] & forward_byte_mask != 0x00 return lib end end # No backing library was found return nothing end ## NOTE: Manually setting forwards is referred to as the 'footgun API'. It allows truly ## bizarre and complex setups to be created. If you run into strange errors while using ## it, the first thing you should ask yourself is whether you've set things up properly. function lbt_set_forward(symbol_name, addr, interface, complex_retstyle = LBT_COMPLEX_RETSTYLE_NORMAL, f2c = LBT_F2C_PLAIN; verbose::Bool = false) _clear_config_with() do return ccall( (:lbt_set_forward, libblastrampoline), Int32, (Cstring, Ptr{Cvoid}, Int32, Int32, Int32, Int32), string(symbol_name), addr, Int32(interface), Int32(complex_retstyle), Int32(f2c), verbose ? Int32(1) : Int32(0), ) end end function lbt_set_forward(symbol_name, addr, interface::Symbol, complex_retstyle::Symbol = :normal, f2c::Symbol = :plain; kwargs...) return lbt_set_forward(symbol_name, addr, LBT_INV_INTERFACE_MAP[interface], LBT_INV_COMPLEX_RETSTYLE_MAP[complex_retstyle], LBT_INV_F2C_MAP[f2c]; kwargs...) end function lbt_get_forward(symbol_name, interface, f2c = LBT_F2C_PLAIN) return ccall( (:lbt_get_forward, libblastrampoline), Ptr{Cvoid}, (Cstring, Int32, Int32), string(symbol_name), Int32(interface), Int32(f2c), ) end function lbt_get_forward(symbol_name, interface::Symbol, f2c::Symbol = :plain) return lbt_get_forward(symbol_name, LBT_INV_INTERFACE_MAP[interface], LBT_INV_F2C_MAP[f2c]) end w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/matmul.jlmม# This file is a part of Julia. License is MIT: https://julialang.org/license # matmul.jl: Everything to do with dense matrix multiplication # Matrix-matrix multiplication AdjOrTransStridedMat{T} = Union{Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} StridedMaybeAdjOrTransMat{T} = Union{StridedMatrix{T}, Adjoint{<:Any, <:StridedMatrix{T}}, Transpose{<:Any, <:StridedMatrix{T}}} StridedMaybeAdjOrTransVecOrMat{T} = Union{StridedVecOrMat{T}, AdjOrTrans{<:Any, <:StridedVecOrMat{T}}} matprod(x, y) = x*y + x*y # dot products dot(x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasReal} = BLAS.dot(x, y) dot(x::StridedVecLike{T}, y::StridedVecLike{T}) where {T<:BlasComplex} = BLAS.dotc(x, y) function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasReal,TI<:Integer} if length(rx) != length(ry) throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) end if minimum(ry) < 1 || maximum(ry) > length(y) throw(BoundsError(y, ry)) end GC.@preserve x y BLAS.dot(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry)) end function dot(x::Vector{T}, rx::AbstractRange{TI}, y::Vector{T}, ry::AbstractRange{TI}) where {T<:BlasComplex,TI<:Integer} if length(rx) != length(ry) throw(DimensionMismatch(lazy"length of rx, $(length(rx)), does not equal length of ry, $(length(ry))")) end if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) end if minimum(ry) < 1 || maximum(ry) > length(y) throw(BoundsError(y, ry)) end GC.@preserve x y BLAS.dotc(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry)) end function *(transx::Transpose{<:Any,<:StridedVector{T}}, y::StridedVector{T}) where {T<:BlasComplex} x = transx.parent return BLAS.dotu(x, y) end # Matrix-vector multiplication function (*)(A::StridedMaybeAdjOrTransMat{T}, x::StridedVector{S}) where {T<:BlasFloat,S<:Real} TS = promote_op(matprod, T, S) y = isconcretetype(TS) ? convert(AbstractVector{TS}, x) : x mul!(similar(x, TS, size(A,1)), A, y) end function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S} TS = promote_op(matprod, T, S) mul!(similar(x, TS, axes(A,1)), A, x) end # these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case): (*)(a::AbstractVector, tB::TransposeAbsMat) = reshape(a, length(a), 1) * tB (*)(a::AbstractVector, adjB::AdjointAbsMat) = reshape(a, length(a), 1) * adjB (*)(a::AbstractVector, B::AbstractMatrix) = reshape(a, length(a), 1) * B @inline mul!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector, alpha::Number, beta::Number) = generic_matvecmul!(y, wrapper_char(A), _unwrap(A), x, MulAddMul(alpha, beta)) # BLAS cases # equal eltypes @inline generic_matvecmul!(y::StridedVector{T}, tA, A::StridedVecOrMat{T}, x::StridedVector{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasFloat} = gemv!(y, tA, A, x, _add.alpha, _add.beta) # Real (possibly transposed) matrix times complex vector. # Multiply the matrix with the real and imaginary parts separately @inline generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, _add::MulAddMul=MulAddMul()) where {T<:BlasReal} = gemv!(y, tA, A, x, _add.alpha, _add.beta) # Complex matrix times real vector. # Reinterpret the matrix as a real matrix and do real matvec computation. # works only in cooperation with BLAS when A is untransposed (tA == 'N') # but that check is included in gemv! anyway @inline generic_matvecmul!(y::StridedVector{Complex{T}}, tA, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasReal} = gemv!(y, tA, A, x, _add.alpha, _add.beta) # Vector-Matrix multiplication (*)(x::AdjointAbsVec, A::AbstractMatrix) = (A'*x')' (*)(x::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A)*transpose(x)) # Matrix-matrix multiplication """ *(A::AbstractMatrix, B::AbstractMatrix) Matrix multiplication. # Examples ```jldoctest julia> [1 1; 0 1] * [1 0; 1 1] 2ร—2 Matrix{Int64}: 2 1 1 1 ``` """ function (*)(A::AbstractMatrix, B::AbstractMatrix) TS = promote_op(matprod, eltype(A), eltype(B)) mul!(similar(B, TS, (size(A, 1), size(B, 2))), A, B) end # optimization for dispatching to BLAS, e.g. *(::Matrix{Float32}, ::Matrix{Float64}) # but avoiding the case *(::Matrix{<:BlasComplex}, ::Matrix{<:BlasReal}) # which is better handled by reinterpreting rather than promotion function (*)(A::StridedMaybeAdjOrTransMat{<:BlasReal}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) TS = promote_type(eltype(A), eltype(B)) mul!(similar(B, TS, (size(A, 1), size(B, 2))), wrapperop(A)(convert(AbstractArray{TS}, _unwrap(A))), wrapperop(B)(convert(AbstractArray{TS}, _unwrap(B)))) end function (*)(A::StridedMaybeAdjOrTransMat{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasComplex}) TS = promote_type(eltype(A), eltype(B)) mul!(similar(B, TS, (size(A, 1), size(B, 2))), wrapperop(A)(convert(AbstractArray{TS}, _unwrap(A))), wrapperop(B)(convert(AbstractArray{TS}, _unwrap(B)))) end # Complex Matrix times real matrix: We use that it is generally faster to reinterpret the # first matrix as a real matrix and carry out real matrix matrix multiply function (*)(A::StridedMatrix{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) TS = promote_type(eltype(A), eltype(B)) mul!(similar(B, TS, (size(A, 1), size(B, 2))), convert(AbstractArray{TS}, A), wrapperop(B)(convert(AbstractArray{real(TS)}, _unwrap(B)))) end function (*)(A::AdjOrTransStridedMat{<:BlasComplex}, B::StridedMaybeAdjOrTransMat{<:BlasReal}) TS = promote_type(eltype(A), eltype(B)) mul!(similar(B, TS, (size(A, 1), size(B, 2))), copymutable_oftype(A, TS), # remove AdjOrTrans to use reinterpret trick below wrapperop(B)(convert(AbstractArray{real(TS)}, _unwrap(B)))) end # the following case doesn't seem to benefit from the translation A*B = (B' * A')' function (*)(A::StridedMatrix{<:BlasReal}, B::StridedMatrix{<:BlasComplex}) temp = real(B) R = A * temp temp .= imag.(B) I = A * temp Complex.(R, I) end (*)(A::AdjOrTransStridedMat{<:BlasReal}, B::StridedMatrix{<:BlasComplex}) = copy(transpose(transpose(B) * parent(A))) (*)(A::StridedMaybeAdjOrTransMat{<:BlasReal}, B::AdjOrTransStridedMat{<:BlasComplex}) = copy(wrapperop(B)(parent(B) * transpose(A))) """ muladd(A, y, z) Combined multiply-add, `A*y .+ z`, for matrix-matrix or matrix-vector multiplication. The result is always the same size as `A*y`, but `z` may be smaller, or a scalar. !!! compat "Julia 1.6" These methods require Julia 1.6 or later. # Examples ```jldoctest julia> A=[1.0 2.0; 3.0 4.0]; B=[1.0 1.0; 1.0 1.0]; z=[0, 100]; julia> muladd(A, B, z) 2ร—2 Matrix{Float64}: 3.0 3.0 107.0 107.0 ``` """ function Base.muladd(A::AbstractMatrix, y::AbstractVecOrMat, z::Union{Number, AbstractArray}) Ay = A * y for d in 1:ndims(Ay) # Same error as Ay .+= z would give, to match StridedMatrix method: size(z,d) > size(Ay,d) && throw(DimensionMismatch("array could not be broadcast to match destination")) end for d in ndims(Ay)+1:ndims(z) # Similar error to what Ay + z would give, to match (Any,Any,Any) method: size(z,d) > 1 && throw(DimensionMismatch(string("dimensions must match: z has dims ", axes(z), ", must have singleton at dim ", d))) end Ay .+ z end function Base.muladd(u::AbstractVector, v::AdjOrTransAbsVec, z::Union{Number, AbstractArray}) if size(z,1) > length(u) || size(z,2) > length(v) # Same error as (u*v) .+= z: throw(DimensionMismatch("array could not be broadcast to match destination")) end for d in 3:ndims(z) # Similar error to (u*v) + z: size(z,d) > 1 && throw(DimensionMismatch(string("dimensions must match: z has dims ", axes(z), ", must have singleton at dim ", d))) end (u .* v) .+ z end Base.muladd(x::AdjointAbsVec, A::AbstractMatrix, z::Union{Number, AbstractVecOrMat}) = muladd(A', x', z')' Base.muladd(x::TransposeAbsVec, A::AbstractMatrix, z::Union{Number, AbstractVecOrMat}) = transpose(muladd(transpose(A), transpose(x), transpose(z))) function Base.muladd(A::StridedMaybeAdjOrTransMat{<:Number}, y::AbstractVector{<:Number}, z::Union{Number, AbstractVector}) T = promote_type(eltype(A), eltype(y), eltype(z)) C = similar(A, T, axes(A,1)) C .= z mul!(C, A, y, true, true) end function Base.muladd(A::StridedMaybeAdjOrTransMat{<:Number}, B::StridedMaybeAdjOrTransMat{<:Number}, z::Union{Number, AbstractVecOrMat}) T = promote_type(eltype(A), eltype(B), eltype(z)) C = similar(A, T, axes(A,1), axes(B,2)) C .= z mul!(C, A, B, true, true) end """ mul!(Y, A, B) -> Y Calculates the matrix-matrix or matrix-vector product ``AB`` and stores the result in `Y`, overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or `B`. # Examples ```jldoctest julia> A=[1.0 2.0; 3.0 4.0]; B=[1.0 1.0; 1.0 1.0]; Y = similar(B); mul!(Y, A, B); julia> Y 2ร—2 Matrix{Float64}: 3.0 3.0 7.0 7.0 ``` # Implementation For custom matrix and vector types, it is recommended to implement 5-argument `mul!` rather than implementing 3-argument `mul!` directly if possible. """ @inline function mul!(C, A, B) return mul!(C, A, B, true, false) end """ mul!(C, A, B, ฮฑ, ฮฒ) -> C Combined inplace matrix-matrix or matrix-vector multiply-add ``A B ฮฑ + C ฮฒ``. The result is stored in `C` by overwriting it. Note that `C` must not be aliased with either `A` or `B`. !!! compat "Julia 1.3" Five-argument `mul!` requires at least Julia 1.3. # Examples ```jldoctest julia> A=[1.0 2.0; 3.0 4.0]; B=[1.0 1.0; 1.0 1.0]; C=[1.0 2.0; 3.0 4.0]; julia> mul!(C, A, B, 100.0, 10.0) === C true julia> C 2ร—2 Matrix{Float64}: 310.0 320.0 730.0 740.0 ``` """ @inline mul!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat, ฮฑ::Number, ฮฒ::Number) = generic_matmatmul!( C, wrapper_char(A), wrapper_char(B), _unwrap(A), _unwrap(B), MulAddMul(ฮฑ, ฮฒ) ) """ rmul!(A, B) Calculate the matrix-matrix product ``AB``, overwriting `A`, and return the result. Here, `B` must be of special matrix type, like, e.g., [`Diagonal`](@ref), [`UpperTriangular`](@ref) or [`LowerTriangular`](@ref), or of some orthogonal type, see [`QR`](@ref). # Examples ```jldoctest julia> A = [0 1; 1 0]; julia> B = UpperTriangular([1 2; 0 3]); julia> rmul!(A, B); julia> A 2ร—2 Matrix{Int64}: 0 3 1 2 julia> A = [1.0 2.0; 3.0 4.0]; julia> F = qr([0 1; -1 0]); julia> rmul!(A, F.Q) 2ร—2 Matrix{Float64}: 2.0 1.0 4.0 3.0 ``` """ rmul!(A, B) """ lmul!(A, B) Calculate the matrix-matrix product ``AB``, overwriting `B`, and return the result. Here, `A` must be of special matrix type, like, e.g., [`Diagonal`](@ref), [`UpperTriangular`](@ref) or [`LowerTriangular`](@ref), or of some orthogonal type, see [`QR`](@ref). # Examples ```jldoctest julia> B = [0 1; 1 0]; julia> A = UpperTriangular([1 2; 0 3]); julia> lmul!(A, B); julia> B 2ร—2 Matrix{Int64}: 2 1 3 0 julia> B = [1.0 2.0; 3.0 4.0]; julia> F = qr([0 1; -1 0]); julia> lmul!(F.Q, B) 2ร—2 Matrix{Float64}: 3.0 4.0 1.0 2.0 ``` """ lmul!(A, B) # THE one big BLAS dispatch @inline function generic_matmatmul!(C::StridedMatrix{T}, tA, tB, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasFloat} if all(in(('N', 'T', 'C')), (tA, tB)) if tA == 'T' && tB == 'N' && A === B return syrk_wrapper!(C, 'T', A, _add) elseif tA == 'N' && tB == 'T' && A === B return syrk_wrapper!(C, 'N', A, _add) elseif tA == 'C' && tB == 'N' && A === B return herk_wrapper!(C, 'C', A, _add) elseif tA == 'N' && tB == 'C' && A === B return herk_wrapper!(C, 'N', A, _add) else return gemm_wrapper!(C, tA, tB, A, B, _add) end end alpha, beta = promote(_add.alpha, _add.beta, zero(T)) if alpha isa Union{Bool,T} && beta isa Union{Bool,T} if (tA == 'S' || tA == 's') && tB == 'N' return BLAS.symm!('L', tA == 'S' ? 'U' : 'L', alpha, A, B, beta, C) elseif (tB == 'S' || tB == 's') && tA == 'N' return BLAS.symm!('R', tB == 'S' ? 'U' : 'L', alpha, B, A, beta, C) elseif (tA == 'H' || tA == 'h') && tB == 'N' return BLAS.hemm!('L', tA == 'H' ? 'U' : 'L', alpha, A, B, beta, C) elseif (tB == 'H' || tB == 'h') && tA == 'N' return BLAS.hemm!('R', tB == 'H' ? 'U' : 'L', alpha, B, A, beta, C) end end return _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) end # Complex matrix times (transposed) real matrix. Reinterpret the first matrix to real for efficiency. @inline function generic_matmatmul!(C::StridedVecOrMat{Complex{T}}, tA, tB, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, _add::MulAddMul=MulAddMul()) where {T<:BlasReal} if all(in(('N', 'T', 'C')), (tA, tB)) gemm_wrapper!(C, tA, tB, A, B, _add) else _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) end end # Supporting functions for matrix multiplication # copy transposed(adjoint) of upper(lower) side-diagonals. Optionally include diagonal. @inline function copytri!(A::AbstractMatrix, uplo::AbstractChar, conjugate::Bool=false, diag::Bool=false) n = checksquare(A) off = diag ? 0 : 1 if uplo == 'U' for i = 1:n, j = (i+off):n A[j,i] = conjugate ? adjoint(A[i,j]) : transpose(A[i,j]) end elseif uplo == 'L' for i = 1:n, j = (i+off):n A[i,j] = conjugate ? adjoint(A[j,i]) : transpose(A[j,i]) end else throw(ArgumentError(lazy"uplo argument must be 'U' (upper) or 'L' (lower), got $uplo")) end A end function gemv!(y::StridedVector{T}, tA::AbstractChar, A::StridedVecOrMat{T}, x::StridedVector{T}, ฮฑ::Number=true, ฮฒ::Number=false) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) nA != length(x) && throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) mA != length(y) && throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) mA == 0 && return y nA == 0 && return _rmul_or_fill!(y, ฮฒ) alpha, beta = promote(ฮฑ, ฮฒ, zero(T)) if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == 1 && abs(stride(A, 2)) >= size(A, 1) && !iszero(stride(x, 1)) && # We only check input's stride here. if tA in ('N', 'T', 'C') return BLAS.gemv!(tA, alpha, A, x, beta, y) elseif tA in ('S', 's') return BLAS.symv!(tA == 'S' ? 'U' : 'L', alpha, A, x, beta, y) elseif tA in ('H', 'h') return BLAS.hemv!(tA == 'H' ? 'U' : 'L', alpha, A, x, beta, y) end end if tA in ('S', 's', 'H', 'h') # re-wrap again and use plain ('N') matvec mul algorithm, # because _generic_matvecmul! can't handle the HermOrSym cases specifically return _generic_matvecmul!(y, 'N', wrap(A, tA), x, MulAddMul(ฮฑ, ฮฒ)) else return _generic_matvecmul!(y, tA, A, x, MulAddMul(ฮฑ, ฮฒ)) end end function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, A::StridedVecOrMat{Complex{T}}, x::StridedVector{T}, ฮฑ::Number = true, ฮฒ::Number = false) where {T<:BlasReal} mA, nA = lapack_size(tA, A) nA != length(x) && throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) mA != length(y) && throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) mA == 0 && return y nA == 0 && return _rmul_or_fill!(y, ฮฒ) alpha, beta = promote(ฮฑ, ฮฒ, zero(T)) if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == 1 && abs(stride(A, 2)) >= size(A, 1) && stride(y, 1) == 1 && tA == 'N' && # reinterpret-based optimization is valid only for contiguous `y` !iszero(stride(x, 1)) BLAS.gemv!(tA, alpha, reinterpret(T, A), x, beta, reinterpret(T, y)) return y else Anew, ta = tA in ('S', 's', 'H', 'h') ? (wrap(A, tA), 'N') : (A, tA) return _generic_matvecmul!(y, ta, Anew, x, MulAddMul(ฮฑ, ฮฒ)) end end function gemv!(y::StridedVector{Complex{T}}, tA::AbstractChar, A::StridedVecOrMat{T}, x::StridedVector{Complex{T}}, ฮฑ::Number = true, ฮฒ::Number = false) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) nA != length(x) && throw(DimensionMismatch(lazy"second dimension of A, $nA, does not match length of x, $(length(x))")) mA != length(y) && throw(DimensionMismatch(lazy"first dimension of A, $mA, does not match length of y, $(length(y))")) mA == 0 && return y nA == 0 && return _rmul_or_fill!(y, ฮฒ) alpha, beta = promote(ฮฑ, ฮฒ, zero(T)) @views if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == 1 && abs(stride(A, 2)) >= size(A, 1) && !iszero(stride(x, 1)) && tA in ('N', 'T', 'C') xfl = reinterpret(reshape, T, x) # Use reshape here. yfl = reinterpret(reshape, T, y) BLAS.gemv!(tA, alpha, A, xfl[1, :], beta, yfl[1, :]) BLAS.gemv!(tA, alpha, A, xfl[2, :], beta, yfl[2, :]) return y elseif tA in ('S', 's', 'H', 'h') # re-wrap again and use plain ('N') matvec mul algorithm, # because _generic_matvecmul! can't handle the HermOrSym cases specifically return _generic_matvecmul!(y, 'N', wrap(A, tA), x, MulAddMul(ฮฑ, ฮฒ)) else return _generic_matvecmul!(y, tA, A, x, MulAddMul(ฮฑ, ฮฒ)) end end function syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat{T}, _add = MulAddMul()) where {T<:BlasFloat} nC = checksquare(C) if tA == 'T' (nA, mA) = size(A,1), size(A,2) tAt = 'N' else (mA, nA) = size(A,1), size(A,2) tAt = 'T' end if nC != mA throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) end if mA == 0 || nA == 0 || iszero(_add.alpha) return _rmul_or_fill!(C, _add.beta) end if mA == 2 && nA == 2 return matmul2x2!(C, tA, tAt, A, A, _add) end if mA == 3 && nA == 3 return matmul3x3!(C, tA, tAt, A, A, _add) end # BLAS.syrk! only updates symmetric C # alternatively, make non-zero ฮฒ a show-stopper for BLAS.syrk! if iszero(_add.beta) || issymmetric(C) alpha, beta = promote(_add.alpha, _add.beta, zero(T)) if (alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1)) return copytri!(BLAS.syrk!('U', tA, alpha, A, beta, C), 'U') end end return gemm_wrapper!(C, tA, tAt, A, A, _add) end function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA::AbstractChar, A::Union{StridedVecOrMat{T}, StridedVecOrMat{Complex{T}}}, _add = MulAddMul()) where {T<:BlasReal} nC = checksquare(C) if tA == 'C' (nA, mA) = size(A,1), size(A,2) tAt = 'N' else (mA, nA) = size(A,1), size(A,2) tAt = 'C' end if nC != mA throw(DimensionMismatch(lazy"output matrix has size: $(nC), but should have size $(mA)")) end if mA == 0 || nA == 0 || iszero(_add.alpha) return _rmul_or_fill!(C, _add.beta) end if mA == 2 && nA == 2 return matmul2x2!(C, tA, tAt, A, A, _add) end if mA == 3 && nA == 3 return matmul3x3!(C, tA, tAt, A, A, _add) end # Result array does not need to be initialized as long as beta==0 # C = Matrix{T}(undef, mA, mA) if iszero(_add.beta) || issymmetric(C) alpha, beta = promote(_add.alpha, _add.beta, zero(T)) if (alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1)) return copytri!(BLAS.herk!('U', tA, alpha, A, beta, C), 'U', true) end end return gemm_wrapper!(C, tA, tAt, A, A, _add) end function gemm_wrapper(tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) C = similar(B, T, mA, nB) if all(in(('N', 'T', 'C')), (tA, tB)) gemm_wrapper!(C, tA, tB, A, B) else _generic_matmatmul!(C, 'N', 'N', wrap(A, tA), wrap(B, tB), _add) end end function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, _add = MulAddMul()) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if nA != mB throw(DimensionMismatch(lazy"A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) end if C === A || B === C throw(ArgumentError("output matrix must not be aliased with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) if size(C) != (mA, nB) throw(DimensionMismatch(lazy"C has dimensions $(size(C)), should have ($mA,$nB)")) end return _rmul_or_fill!(C, _add.beta) end if mA == 2 && nA == 2 && nB == 2 return matmul2x2!(C, tA, tB, A, B, _add) end if mA == 3 && nA == 3 && nB == 3 return matmul3x3!(C, tA, tB, A, B, _add) end alpha, beta = promote(_add.alpha, _add.beta, zero(T)) if (alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(B, 2) >= size(B, 1) && stride(C, 2) >= size(C, 1)) return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) end _generic_matmatmul!(C, tA, tB, A, B, _add) end function gemm_wrapper!(C::StridedVecOrMat{Complex{T}}, tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{Complex{T}}, B::StridedVecOrMat{T}, _add = MulAddMul()) where {T<:BlasReal} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if nA != mB throw(DimensionMismatch(lazy"A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)")) end if C === A || B === C throw(ArgumentError("output matrix must not be aliased with input matrix")) end if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) if size(C) != (mA, nB) throw(DimensionMismatch(lazy"C has dimensions $(size(C)), should have ($mA,$nB)")) end return _rmul_or_fill!(C, _add.beta) end if mA == 2 && nA == 2 && nB == 2 return matmul2x2!(C, tA, tB, A, B, _add) end if mA == 3 && nA == 3 && nB == 3 return matmul3x3!(C, tA, tB, A, B, _add) end alpha, beta = promote(_add.alpha, _add.beta, zero(T)) # Make-sure reinterpret-based optimization is BLAS-compatible. if (alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(B, 2) >= size(B, 1) && stride(C, 2) >= size(C, 1) && tA == 'N') BLAS.gemm!(tA, tB, alpha, reinterpret(T, A), B, beta, reinterpret(T, C)) return C end _generic_matmatmul!(C, tA, tB, A, B, _add) end # blas.jl defines matmul for floats; other integer and mixed precision # cases are handled here lapack_size(t::AbstractChar, M::AbstractVecOrMat) = (size(M, t=='N' ? 1 : 2), size(M, t=='N' ? 2 : 1)) function copyto!(B::AbstractVecOrMat, ir_dest::AbstractUnitRange{Int}, jr_dest::AbstractUnitRange{Int}, tM::AbstractChar, M::AbstractVecOrMat, ir_src::AbstractUnitRange{Int}, jr_src::AbstractUnitRange{Int}) if tM == 'N' copyto!(B, ir_dest, jr_dest, M, ir_src, jr_src) else LinearAlgebra.copy_transpose!(B, ir_dest, jr_dest, M, jr_src, ir_src) tM == 'C' && conj!(@view B[ir_dest, jr_dest]) end B end function copy_transpose!(B::AbstractMatrix, ir_dest::AbstractUnitRange{Int}, jr_dest::AbstractUnitRange{Int}, tM::AbstractChar, M::AbstractVecOrMat, ir_src::AbstractUnitRange{Int}, jr_src::AbstractUnitRange{Int}) if tM == 'N' LinearAlgebra.copy_transpose!(B, ir_dest, jr_dest, M, ir_src, jr_src) else copyto!(B, ir_dest, jr_dest, M, jr_src, ir_src) tM == 'C' && conj!(@view B[ir_dest, jr_dest]) end B end # TODO: It will be faster for large matrices to convert to float, # call BLAS, and convert back to required type. # NOTE: the generic version is also called as fallback for # strides != 1 cases @inline function generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) Anew, ta = tA in ('S', 's', 'H', 'h') ? (wrap(A, tA), 'N') : (A, tA) return _generic_matvecmul!(C, ta, Anew, B, _add) end function _generic_matvecmul!(C::AbstractVector, tA, A::AbstractVecOrMat, B::AbstractVector, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) @assert tA in ('N', 'T', 'C') mB = length(B) mA, nA = lapack_size(tA, A) if mB != nA throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), vector B has length $mB")) end if mA != length(C) throw(DimensionMismatch(lazy"result C has length $(length(C)), needs length $mA")) end Astride = size(A, 1) @inbounds begin if tA == 'T' # fastest case if nA == 0 for k = 1:mA _modify!(_add, false, C, k) end else for k = 1:mA aoffs = (k-1)*Astride s = zero(A[aoffs + 1]*B[1] + A[aoffs + 1]*B[1]) for i = 1:nA s += transpose(A[aoffs+i]) * B[i] end _modify!(_add, s, C, k) end end elseif tA == 'C' if nA == 0 for k = 1:mA _modify!(_add, false, C, k) end else for k = 1:mA aoffs = (k-1)*Astride s = zero(A[aoffs + 1]*B[1] + A[aoffs + 1]*B[1]) for i = 1:nA s += A[aoffs + i]'B[i] end _modify!(_add, s, C, k) end end else # tA == 'N' for i = 1:mA if !iszero(_add.beta) C[i] *= _add.beta elseif mB == 0 C[i] = false else C[i] = zero(A[i]*B[1] + A[i]*B[1]) end end for k = 1:mB aoffs = (k-1)*Astride b = _add(B[k]) for i = 1:mA C[i] += A[aoffs + i] * b end end end end # @inbounds C end function generic_matmatmul(tA, tB, A::AbstractVecOrMat{T}, B::AbstractMatrix{S}) where {T,S} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) C = similar(B, promote_op(matprod, T, S), mA, nB) generic_matmatmul!(C, tA, tB, A, B) end const tilebufsize = 10800 # Approximately 32k/3 function generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat, _add::MulAddMul) mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) mC, nC = size(C) if iszero(_add.alpha) return _rmul_or_fill!(C, _add.beta) end if mA == nA == mB == nB == mC == nC == 2 return matmul2x2!(C, tA, tB, A, B, _add) end if mA == nA == mB == nB == mC == nC == 3 return matmul3x3!(C, tA, tB, A, B, _add) end A, tA = tA in ('H', 'h', 'S', 's') ? (wrap(A, tA), 'N') : (A, tA) B, tB = tB in ('H', 'h', 'S', 's') ? (wrap(B, tB), 'N') : (B, tB) _generic_matmatmul!(C, tA, tB, A, B, _add) end function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}, _add::MulAddMul) where {T,S,R} @assert tA in ('N', 'T', 'C') && tB in ('N', 'T', 'C') require_one_based_indexing(C, A, B) mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) if mB != nA throw(DimensionMismatch(lazy"matrix A has dimensions ($mA,$nA), matrix B has dimensions ($mB,$nB)")) end if size(C,1) != mA || size(C,2) != nB throw(DimensionMismatch(lazy"result C has dimensions $(size(C)), needs ($mA,$nB)")) end if iszero(_add.alpha) || isempty(A) || isempty(B) return _rmul_or_fill!(C, _add.beta) end tile_size = 0 if isbitstype(R) && isbitstype(T) && isbitstype(S) && (tA == 'N' || tB != 'N') tile_size = floor(Int, sqrt(tilebufsize / max(sizeof(R), sizeof(S), sizeof(T), 1))) end @inbounds begin if tile_size > 0 sz = (tile_size, tile_size) Atile = Array{T}(undef, sz) Btile = Array{S}(undef, sz) z1 = zero(A[1, 1]*B[1, 1] + A[1, 1]*B[1, 1]) z = convert(promote_type(typeof(z1), R), z1) if mA < tile_size && nA < tile_size && nB < tile_size copy_transpose!(Atile, 1:nA, 1:mA, tA, A, 1:mA, 1:nA) copyto!(Btile, 1:mB, 1:nB, tB, B, 1:mB, 1:nB) for j = 1:nB boff = (j-1)*tile_size for i = 1:mA aoff = (i-1)*tile_size s = z for k = 1:nA s += Atile[aoff+k] * Btile[boff+k] end _modify!(_add, s, C, (i,j)) end end else Ctile = Array{R}(undef, sz) for jb = 1:tile_size:nB jlim = min(jb+tile_size-1,nB) jlen = jlim-jb+1 for ib = 1:tile_size:mA ilim = min(ib+tile_size-1,mA) ilen = ilim-ib+1 fill!(Ctile, z) for kb = 1:tile_size:nA klim = min(kb+tile_size-1,mB) klen = klim-kb+1 copy_transpose!(Atile, 1:klen, 1:ilen, tA, A, ib:ilim, kb:klim) copyto!(Btile, 1:klen, 1:jlen, tB, B, kb:klim, jb:jlim) for j=1:jlen bcoff = (j-1)*tile_size for i = 1:ilen aoff = (i-1)*tile_size s = z for k = 1:klen s += Atile[aoff+k] * Btile[bcoff+k] end Ctile[bcoff+i] += s end end end if isone(_add.alpha) && iszero(_add.beta) copyto!(C, ib:ilim, jb:jlim, Ctile, 1:ilen, 1:jlen) else C[ib:ilim, jb:jlim] .= @views _add.(Ctile[1:ilen, 1:jlen], C[ib:ilim, jb:jlim]) end end end end else # Multiplication for non-plain-data uses the naive algorithm if tA == 'N' if tB == 'N' for i = 1:mA, j = 1:nB z2 = zero(A[i, 1]*B[1, j] + A[i, 1]*B[1, j]) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += A[i, k]*B[k, j] end _modify!(_add, Ctmp, C, (i,j)) end elseif tB == 'T' for i = 1:mA, j = 1:nB z2 = zero(A[i, 1]*transpose(B[j, 1]) + A[i, 1]*transpose(B[j, 1])) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += A[i, k] * transpose(B[j, k]) end _modify!(_add, Ctmp, C, (i,j)) end else for i = 1:mA, j = 1:nB z2 = zero(A[i, 1]*B[j, 1]' + A[i, 1]*B[j, 1]') Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += A[i, k]*B[j, k]' end _modify!(_add, Ctmp, C, (i,j)) end end elseif tA == 'T' if tB == 'N' for i = 1:mA, j = 1:nB z2 = zero(transpose(A[1, i])*B[1, j] + transpose(A[1, i])*B[1, j]) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += transpose(A[k, i]) * B[k, j] end _modify!(_add, Ctmp, C, (i,j)) end elseif tB == 'T' for i = 1:mA, j = 1:nB z2 = zero(transpose(A[1, i])*transpose(B[j, 1]) + transpose(A[1, i])*transpose(B[j, 1])) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += transpose(A[k, i]) * transpose(B[j, k]) end _modify!(_add, Ctmp, C, (i,j)) end else for i = 1:mA, j = 1:nB z2 = zero(transpose(A[1, i])*B[j, 1]' + transpose(A[1, i])*B[j, 1]') Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += transpose(A[k, i]) * adjoint(B[j, k]) end _modify!(_add, Ctmp, C, (i,j)) end end else if tB == 'N' for i = 1:mA, j = 1:nB z2 = zero(A[1, i]'*B[1, j] + A[1, i]'*B[1, j]) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += A[k, i]'B[k, j] end _modify!(_add, Ctmp, C, (i,j)) end elseif tB == 'T' for i = 1:mA, j = 1:nB z2 = zero(A[1, i]'*transpose(B[j, 1]) + A[1, i]'*transpose(B[j, 1])) Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += adjoint(A[k, i]) * transpose(B[j, k]) end _modify!(_add, Ctmp, C, (i,j)) end else for i = 1:mA, j = 1:nB z2 = zero(A[1, i]'*B[j, 1]' + A[1, i]'*B[j, 1]') Ctmp = convert(promote_type(R, typeof(z2)), z2) for k = 1:nA Ctmp += A[k, i]'B[j, k]' end _modify!(_add, Ctmp, C, (i,j)) end end end end end # @inbounds C end # multiply 2x2 matrices function matmul2x2(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} matmul2x2!(similar(B, promote_op(matprod, T, S), 2, 2), tA, tB, A, B) end function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (2,2)) throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) end @inbounds begin if tA == 'N' A11 = A[1,1]; A12 = A[1,2]; A21 = A[2,1]; A22 = A[2,2] elseif tA == 'T' # TODO making these lazy could improve perf A11 = copy(transpose(A[1,1])); A12 = copy(transpose(A[2,1])) A21 = copy(transpose(A[1,2])); A22 = copy(transpose(A[2,2])) elseif tA == 'C' # TODO making these lazy could improve perf A11 = copy(A[1,1]'); A12 = copy(A[2,1]') A21 = copy(A[1,2]'); A22 = copy(A[2,2]') elseif tA == 'S' A11 = symmetric(A[1,1], :U); A12 = A[1,2] A21 = copy(transpose(A[1,2])); A22 = symmetric(A[2,2], :U) elseif tA == 's' A11 = symmetric(A[1,1], :L); A12 = copy(transpose(A[2,1])) A21 = A[2,1]; A22 = symmetric(A[2,2], :L) elseif tA == 'H' A11 = hermitian(A[1,1], :U); A12 = A[1,2] A21 = copy(adjoint(A[1,2])); A22 = hermitian(A[2,2], :U) else # if tA == 'h' A11 = hermitian(A[1,1], :L); A12 = copy(adjoint(A[2,1])) A21 = A[2,1]; A22 = hermitian(A[2,2], :L) end if tB == 'N' B11 = B[1,1]; B12 = B[1,2]; B21 = B[2,1]; B22 = B[2,2] elseif tB == 'T' # TODO making these lazy could improve perf B11 = copy(transpose(B[1,1])); B12 = copy(transpose(B[2,1])) B21 = copy(transpose(B[1,2])); B22 = copy(transpose(B[2,2])) elseif tB == 'C' # TODO making these lazy could improve perf B11 = copy(B[1,1]'); B12 = copy(B[2,1]') B21 = copy(B[1,2]'); B22 = copy(B[2,2]') elseif tB == 'S' B11 = symmetric(B[1,1], :U); B12 = B[1,2] B21 = copy(transpose(B[1,2])); B22 = symmetric(B[2,2], :U) elseif tB == 's' B11 = symmetric(B[1,1], :L); B12 = copy(transpose(B[2,1])) B21 = B[2,1]; B22 = symmetric(B[2,2], :L) elseif tB == 'H' B11 = hermitian(B[1,1], :U); B12 = B[1,2] B21 = copy(adjoint(B[1,2])); B22 = hermitian(B[2,2], :U) else # if tB == 'h' B11 = hermitian(B[1,1], :L); B12 = copy(adjoint(B[2,1])) B21 = B[2,1]; B22 = hermitian(B[2,2], :L) end _modify!(_add, A11*B11 + A12*B21, C, (1,1)) _modify!(_add, A11*B12 + A12*B22, C, (1,2)) _modify!(_add, A21*B11 + A22*B21, C, (2,1)) _modify!(_add, A21*B12 + A22*B22, C, (2,2)) end # inbounds C end # Multiply 3x3 matrices function matmul3x3(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} matmul3x3!(similar(B, promote_op(matprod, T, S), 3, 3), tA, tB, A, B) end function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A, B) if !(size(A) == size(B) == size(C) == (3,3)) throw(DimensionMismatch(lazy"A has size $(size(A)), B has size $(size(B)), C has size $(size(C))")) end @inbounds begin if tA == 'N' A11 = A[1,1]; A12 = A[1,2]; A13 = A[1,3] A21 = A[2,1]; A22 = A[2,2]; A23 = A[2,3] A31 = A[3,1]; A32 = A[3,2]; A33 = A[3,3] elseif tA == 'T' # TODO making these lazy could improve perf A11 = copy(transpose(A[1,1])); A12 = copy(transpose(A[2,1])); A13 = copy(transpose(A[3,1])) A21 = copy(transpose(A[1,2])); A22 = copy(transpose(A[2,2])); A23 = copy(transpose(A[3,2])) A31 = copy(transpose(A[1,3])); A32 = copy(transpose(A[2,3])); A33 = copy(transpose(A[3,3])) elseif tA == 'C' # TODO making these lazy could improve perf A11 = copy(A[1,1]'); A12 = copy(A[2,1]'); A13 = copy(A[3,1]') A21 = copy(A[1,2]'); A22 = copy(A[2,2]'); A23 = copy(A[3,2]') A31 = copy(A[1,3]'); A32 = copy(A[2,3]'); A33 = copy(A[3,3]') elseif tA == 'S' A11 = symmetric(A[1,1], :U); A12 = A[1,2]; A13 = A[1,3] A21 = copy(transpose(A[1,2])); A22 = symmetric(A[2,2], :U); A23 = A[2,3] A31 = copy(transpose(A[1,3])); A32 = copy(transpose(A[2,3])); A33 = symmetric(A[3,3], :U) elseif tA == 's' A11 = symmetric(A[1,1], :L); A12 = copy(transpose(A[2,1])); A13 = copy(transpose(A[3,1])) A21 = A[2,1]; A22 = symmetric(A[2,2], :L); A23 = copy(transpose(A[3,2])) A31 = A[3,1]; A32 = A[3,2]; A33 = symmetric(A[3,3], :L) elseif tA == 'H' A11 = hermitian(A[1,1], :U); A12 = A[1,2]; A13 = A[1,3] A21 = copy(adjoint(A[1,2])); A22 = hermitian(A[2,2], :U); A23 = A[2,3] A31 = copy(adjoint(A[1,3])); A32 = copy(adjoint(A[2,3])); A33 = hermitian(A[3,3], :U) else # if tA == 'h' A11 = hermitian(A[1,1], :L); A12 = copy(adjoint(A[2,1])); A13 = copy(adjoint(A[3,1])) A21 = A[2,1]; A22 = hermitian(A[2,2], :L); A23 = copy(adjoint(A[3,2])) A31 = A[3,1]; A32 = A[3,2]; A33 = hermitian(A[3,3], :L) end if tB == 'N' B11 = B[1,1]; B12 = B[1,2]; B13 = B[1,3] B21 = B[2,1]; B22 = B[2,2]; B23 = B[2,3] B31 = B[3,1]; B32 = B[3,2]; B33 = B[3,3] elseif tB == 'T' # TODO making these lazy could improve perf B11 = copy(transpose(B[1,1])); B12 = copy(transpose(B[2,1])); B13 = copy(transpose(B[3,1])) B21 = copy(transpose(B[1,2])); B22 = copy(transpose(B[2,2])); B23 = copy(transpose(B[3,2])) B31 = copy(transpose(B[1,3])); B32 = copy(transpose(B[2,3])); B33 = copy(transpose(B[3,3])) elseif tB == 'C' # TODO making these lazy could improve perf B11 = copy(B[1,1]'); B12 = copy(B[2,1]'); B13 = copy(B[3,1]') B21 = copy(B[1,2]'); B22 = copy(B[2,2]'); B23 = copy(B[3,2]') B31 = copy(B[1,3]'); B32 = copy(B[2,3]'); B33 = copy(B[3,3]') elseif tB == 'S' B11 = symmetric(B[1,1], :U); B12 = B[1,2]; B13 = B[1,3] B21 = copy(transpose(B[1,2])); B22 = symmetric(B[2,2], :U); B23 = B[2,3] B31 = copy(transpose(B[1,3])); B32 = copy(transpose(B[2,3])); B33 = symmetric(B[3,3], :U) elseif tB == 's' B11 = symmetric(B[1,1], :L); B12 = copy(transpose(B[2,1])); B13 = copy(transpose(B[3,1])) B21 = B[2,1]; B22 = symmetric(B[2,2], :L); B23 = copy(transpose(B[3,2])) B31 = B[3,1]; B32 = B[3,2]; B33 = symmetric(B[3,3], :L) elseif tB == 'H' B11 = hermitian(B[1,1], :U); B12 = B[1,2]; B13 = B[1,3] B21 = copy(adjoint(B[1,2])); B22 = hermitian(B[2,2], :U); B23 = B[2,3] B31 = copy(adjoint(B[1,3])); B32 = copy(adjoint(B[2,3])); B33 = hermitian(B[3,3], :U) else # if tB == 'h' B11 = hermitian(B[1,1], :L); B12 = copy(adjoint(B[2,1])); B13 = copy(adjoint(B[3,1])) B21 = B[2,1]; B22 = hermitian(B[2,2], :L); B23 = copy(adjoint(B[3,2])) B31 = B[3,1]; B32 = B[3,2]; B33 = hermitian(B[3,3], :L) end _modify!(_add, A11*B11 + A12*B21 + A13*B31, C, (1,1)) _modify!(_add, A11*B12 + A12*B22 + A13*B32, C, (1,2)) _modify!(_add, A11*B13 + A12*B23 + A13*B33, C, (1,3)) _modify!(_add, A21*B11 + A22*B21 + A23*B31, C, (2,1)) _modify!(_add, A21*B12 + A22*B22 + A23*B32, C, (2,2)) _modify!(_add, A21*B13 + A22*B23 + A23*B33, C, (2,3)) _modify!(_add, A31*B11 + A32*B21 + A33*B31, C, (3,1)) _modify!(_add, A31*B12 + A32*B22 + A33*B32, C, (3,2)) _modify!(_add, A31*B13 + A32*B23 + A33*B33, C, (3,3)) end # inbounds C end const RealOrComplex = Union{Real,Complex} # Three-argument * """ *(A, B::AbstractMatrix, C) A * B * C * D Chained multiplication of 3 or 4 matrices is done in the most efficient sequence, based on the sizes of the arrays. That is, the number of scalar multiplications needed for `(A * B) * C` (with 3 dense matrices) is compared to that for `A * (B * C)` to choose which of these to execute. If the last factor is a vector, or the first a transposed vector, then it is efficient to deal with these first. In particular `x' * B * y` means `(x' * B) * y` for an ordinary column-major `B::Matrix`. Unlike `dot(x, B, y)`, this allocates an intermediate array. If the first or last factor is a number, this will be fused with the matrix multiplication, using 5-arg [`mul!`](@ref). See also [`muladd`](@ref), [`dot`](@ref). !!! compat "Julia 1.7" These optimisations require at least Julia 1.7. """ *(A::AbstractMatrix, B::AbstractMatrix, x::AbstractVector) = A * (B*x) *(tu::AdjOrTransAbsVec, B::AbstractMatrix, v::AbstractVector) = (tu*B) * v *(tu::AdjOrTransAbsVec, B::AdjOrTransAbsMat, v::AbstractVector) = tu * (B*v) *(A::AbstractMatrix, x::AbstractVector, ฮณ::Number) = mat_vec_scalar(A,x,ฮณ) *(A::AbstractMatrix, B::AbstractMatrix, ฮณ::Number) = mat_mat_scalar(A,B,ฮณ) *(ฮฑ::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractVector{<:RealOrComplex}) = mat_vec_scalar(B,C,ฮฑ) *(ฮฑ::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}) = mat_mat_scalar(B,C,ฮฑ) *(ฮฑ::Number, u::AbstractVector, tv::AdjOrTransAbsVec) = broadcast(*, ฮฑ, u, tv) *(u::AbstractVector, tv::AdjOrTransAbsVec, ฮณ::Number) = broadcast(*, u, tv, ฮณ) *(u::AbstractVector, tv::AdjOrTransAbsVec, C::AbstractMatrix) = u * (tv*C) *(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix) = _tri_matmul(A,B,C) *(tv::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix) = (tv*B) * C function _tri_matmul(A,B,C,ฮด=nothing) n,m = size(A) # m,k == size(B) k,l = size(C) costAB_C = n*m*k + n*k*l # multiplications, allocations n*k + n*l costA_BC = m*k*l + n*m*l # m*l + n*l if costA_BC < costAB_C isnothing(ฮด) ? A * (B*C) : A * mat_mat_scalar(B,C,ฮด) else isnothing(ฮด) ? (A*B) * C : mat_mat_scalar(A*B, C, ฮด) end end # Fast path for two arrays * one scalar is opt-in, via mat_vec_scalar and mat_mat_scalar. mat_vec_scalar(A, x, ฮณ) = A * (x * ฮณ) # fallback mat_vec_scalar(A::StridedMaybeAdjOrTransMat, x::StridedVector, ฮณ) = _mat_vec_scalar(A, x, ฮณ) mat_vec_scalar(A::AdjOrTransAbsVec, x::StridedVector, ฮณ) = (A * x) * ฮณ function _mat_vec_scalar(A, x, ฮณ) T = promote_type(eltype(A), eltype(x), typeof(ฮณ)) C = similar(A, T, axes(A,1)) mul!(C, A, x, ฮณ, false) end mat_mat_scalar(A, B, ฮณ) = (A*B) * ฮณ # fallback mat_mat_scalar(A::StridedMaybeAdjOrTransMat, B::StridedMaybeAdjOrTransMat, ฮณ) = _mat_mat_scalar(A, B, ฮณ) function _mat_mat_scalar(A, B, ฮณ) T = promote_type(eltype(A), eltype(B), typeof(ฮณ)) C = similar(A, T, axes(A,1), axes(B,2)) mul!(C, A, B, ฮณ, false) end mat_mat_scalar(A::AdjointAbsVec, B, ฮณ) = (ฮณ' * (A * B)')' # preserving order, adjoint reverses mat_mat_scalar(A::AdjointAbsVec{<:RealOrComplex}, B::StridedMaybeAdjOrTransMat{<:RealOrComplex}, ฮณ::RealOrComplex) = mat_vec_scalar(B', A', ฮณ')' mat_mat_scalar(A::TransposeAbsVec, B, ฮณ) = transpose(ฮณ * transpose(A * B)) mat_mat_scalar(A::TransposeAbsVec{<:RealOrComplex}, B::StridedMaybeAdjOrTransMat{<:RealOrComplex}, ฮณ::RealOrComplex) = transpose(mat_vec_scalar(transpose(B), transpose(A), ฮณ)) # Four-argument *, by type *(ฮฑ::Number, ฮฒ::Number, C::AbstractMatrix, x::AbstractVector) = (ฮฑ*ฮฒ) * C * x *(ฮฑ::Number, ฮฒ::Number, C::AbstractMatrix, D::AbstractMatrix) = (ฮฑ*ฮฒ) * C * D *(ฮฑ::Number, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = ฮฑ * B * (C*x) *(ฮฑ::Number, vt::AdjOrTransAbsVec, C::AbstractMatrix, x::AbstractVector) = ฮฑ * (vt*C*x) *(ฮฑ::RealOrComplex, vt::AdjOrTransAbsVec{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}, D::AbstractMatrix{<:RealOrComplex}) = (ฮฑ*vt*C) * D # solves an ambiguity *(A::AbstractMatrix, x::AbstractVector, ฮณ::Number, ฮด::Number) = A * x * (ฮณ*ฮด) *(A::AbstractMatrix, B::AbstractMatrix, ฮณ::Number, ฮด::Number) = A * B * (ฮณ*ฮด) *(A::AbstractMatrix, B::AbstractMatrix, x::AbstractVector, ฮด::Number, ) = A * (B*x*ฮด) *(vt::AdjOrTransAbsVec, B::AbstractMatrix, x::AbstractVector, ฮด::Number) = (vt*B*x) * ฮด *(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, ฮด::Number) = (vt*B) * C * ฮด *(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = A * B * (C*x) *(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix) = (vt*B) * C * D *(vt::AdjOrTransAbsVec, B::AbstractMatrix, C::AbstractMatrix, x::AbstractVector) = vt * B * (C*x) # Four-argument *, by size *(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, ฮด::Number) = _tri_matmul(A,B,C,ฮด) *(ฮฑ::RealOrComplex, B::AbstractMatrix{<:RealOrComplex}, C::AbstractMatrix{<:RealOrComplex}, D::AbstractMatrix{<:RealOrComplex}) = _tri_matmul(B,C,D,ฮฑ) *(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, D::AbstractMatrix) = _quad_matmul(A,B,C,D) function _quad_matmul(A,B,C,D) c1 = _mul_cost((A,B),(C,D)) c2 = _mul_cost(((A,B),C),D) c3 = _mul_cost(A,(B,(C,D))) c4 = _mul_cost((A,(B,C)),D) c5 = _mul_cost(A,((B,C),D)) cmin = min(c1,c2,c3,c4,c5) if c1 == cmin (A*B) * (C*D) elseif c2 == cmin ((A*B) * C) * D elseif c3 == cmin A * (B * (C*D)) elseif c4 == cmin (A * (B*C)) * D else A * ((B*C) * D) end end @inline _mul_cost(A::AbstractMatrix) = 0 @inline _mul_cost((A,B)::Tuple) = _mul_cost(A,B) @inline _mul_cost(A,B) = _mul_cost(A) + _mul_cost(B) + *(_mul_sizes(A)..., last(_mul_sizes(B))) @inline _mul_sizes(A::AbstractMatrix) = size(A) @inline _mul_sizes((A,B)::Tuple) = first(_mul_sizes(A)), last(_mul_sizes(B)) w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/lapack.jlkž# This file is a part of Julia. License is MIT: https://julialang.org/license module LAPACK @doc """ Interfaces to LAPACK subroutines. """ LAPACK using ..LinearAlgebra.BLAS: @blasfunc, chkuplo using ..LinearAlgebra: libblastrampoline, BlasFloat, BlasInt, LAPACKException, DimensionMismatch, SingularException, PosDefException, chkstride1, checksquare,triu, tril, dot using Base: iszero, require_one_based_indexing # Legacy binding maintained for backwards-compatibility but new packages # should not look at this, instead preferring to parse the output # of BLAS.get_config() const liblapack = libblastrampoline #Generic LAPACK error handlers """ Handle only negative LAPACK error codes *NOTE* use only if the positive error code is useful. """ function chkargsok(ret::BlasInt) if ret < 0 throw(ArgumentError("invalid argument #$(-ret) to LAPACK call")) end end "Handle all nonzero info codes" function chklapackerror(ret::BlasInt) if ret == 0 return elseif ret < 0 throw(ArgumentError("invalid argument #$(-ret) to LAPACK call")) else # ret > 0 throw(LAPACKException(ret)) end end function chknonsingular(ret::BlasInt) if ret > 0 throw(SingularException(ret)) end end function chkposdef(ret::BlasInt) if ret > 0 throw(PosDefException(ret)) end end "Check that {c}transpose is correctly specified" function chktrans(trans::AbstractChar) if !(trans == 'N' || trans == 'C' || trans == 'T') throw(ArgumentError("trans argument must be 'N' (no transpose), 'T' (transpose), or 'C' (conjugate transpose), got $trans")) end trans end "Check that left/right hand side multiply is correctly specified" function chkside(side::AbstractChar) if !(side == 'L' || side == 'R') throw(ArgumentError("side argument must be 'L' (left hand multiply) or 'R' (right hand multiply), got $side")) end side end "Check that unit diagonal flag is correctly specified" function chkdiag(diag::AbstractChar) if !(diag == 'U' || diag =='N') throw(ArgumentError("diag argument must be 'U' (unit diagonal) or 'N' (non-unit diagonal), got $diag")) end diag end subsetrows(X::AbstractVector, Y::AbstractArray, k) = Y[1:k] subsetrows(X::AbstractMatrix, Y::AbstractArray, k) = Y[1:k, :] function chkfinite(A::AbstractMatrix) for a in A if !isfinite(a) throw(ArgumentError("matrix contains Infs or NaNs")) end end return true end function chkuplofinite(A::AbstractMatrix, uplo::AbstractChar) require_one_based_indexing(A) m, n = size(A) if uplo == 'U' @inbounds for j in 1:n, i in 1:j if !isfinite(A[i,j]) throw(ArgumentError("matrix contains Infs or NaNs")) end end else @inbounds for j in 1:n, i in j:m if !isfinite(A[i,j]) throw(ArgumentError("matrix contains Infs or NaNs")) end end end end # LAPACK version number function version() major = Ref{BlasInt}(0) minor = Ref{BlasInt}(0) patch = Ref{BlasInt}(0) ccall((@blasfunc(ilaver_), libblastrampoline), Cvoid, (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), major, minor, patch) return VersionNumber(major[], minor[], patch[]) end # (GB) general banded matrices, LU decomposition and solver for (gbtrf, gbtrs, elty) in ((:dgbtrf_,:dgbtrs_,:Float64), (:sgbtrf_,:sgbtrs_,:Float32), (:zgbtrf_,:zgbtrs_,:ComplexF64), (:cgbtrf_,:cgbtrs_,:ComplexF32)) @eval begin # SUBROUTINE DGBTRF( M, N, KL, KU, AB, LDAB, IPIV, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, KL, KU, LDAB, M, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION AB( LDAB, * ) function gbtrf!(kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix{$elty}) require_one_based_indexing(AB) chkstride1(AB) n = size(AB, 2) mnmn = min(m, n) ipiv = similar(AB, BlasInt, mnmn) info = Ref{BlasInt}() ccall((@blasfunc($gbtrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), m, n, kl, ku, AB, max(1,stride(AB,2)), ipiv, info) chklapackerror(info[]) AB, ipiv end # SUBROUTINE DGBTRS( TRANS, N, KL, KU, NRHS, AB, LDAB, IPIV, B, LDB, INFO) # * .. Scalar Arguments .. # CHARACTER TRANS # INTEGER INFO, KL, KU, LDAB, LDB, N, NRHS # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION AB( LDAB, * ), B( LDB, * ) function gbtrs!(trans::AbstractChar, kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(AB, B) chkstride1(AB, B, ipiv) chktrans(trans) info = Ref{BlasInt}() n = size(AB,2) if m != n || m != size(B,1) throw(DimensionMismatch("matrix AB has dimensions $(size(AB)), but right hand side matrix B has dimensions $(size(B))")) end ccall((@blasfunc($gbtrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), trans, n, kl, ku, size(B,2), AB, max(1,stride(AB,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end """ gbtrf!(kl, ku, m, AB) -> (AB, ipiv) Compute the LU factorization of a banded matrix `AB`. `kl` is the first subdiagonal containing a nonzero band, `ku` is the last superdiagonal containing one, and `m` is the first dimension of the matrix `AB`. Returns the LU factorization in-place and `ipiv`, the vector of pivots used. """ gbtrf!(kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix) """ gbtrs!(trans, kl, ku, m, AB, ipiv, B) Solve the equation `AB * X = B`. `trans` determines the orientation of `AB`. It may be `N` (no transpose), `T` (transpose), or `C` (conjugate transpose). `kl` is the first subdiagonal containing a nonzero band, `ku` is the last superdiagonal containing one, and `m` is the first dimension of the matrix `AB`. `ipiv` is the vector of pivots returned from `gbtrf!`. Returns the vector or matrix `X`, overwriting `B` in-place. """ gbtrs!(trans::AbstractChar, kl::Integer, ku::Integer, m::Integer, AB::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) ## (GE) general matrices: balancing and back-transforming for (gebal, gebak, elty, relty) in ((:dgebal_, :dgebak_, :Float64, :Float64), (:sgebal_, :sgebak_, :Float32, :Float32), (:zgebal_, :zgebak_, :ComplexF64, :Float64), (:cgebal_, :cgebak_, :ComplexF32, :Float32)) @eval begin # SUBROUTINE DGEBAL( JOB, N, A, LDA, ILO, IHI, SCALE, INFO ) #* .. Scalar Arguments .. # CHARACTER JOB # INTEGER IHI, ILP, INFO, LDA, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), SCALE( * ) function gebal!(job::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkfinite(A) # balancing routines don't support NaNs and Infs ihi = Ref{BlasInt}() ilo = Ref{BlasInt}() scale = similar(A, $relty, n) info = Ref{BlasInt}() ccall((@blasfunc($gebal), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong), job, n, A, max(1,stride(A,2)), ilo, ihi, scale, info, 1) chklapackerror(info[]) ilo[], ihi[], scale end # SUBROUTINE DGEBAK( JOB, SIDE, N, ILO, IHI, SCALE, M, V, LDV, INFO ) #* .. Scalar Arguments .. # CHARACTER JOB, SIDE # INTEGER IHI, ILP, INFO, LDV, M, N # .. Array Arguments .. # DOUBLE PRECISION SCALE( * ), V( LDV, * ) function gebak!(job::AbstractChar, side::AbstractChar, ilo::BlasInt, ihi::BlasInt, scale::AbstractVector{$relty}, V::AbstractMatrix{$elty}) require_one_based_indexing(scale, V) chkstride1(scale, V) chkside(side) chkfinite(V) # balancing routines don't support NaNs and Infs n = checksquare(V) info = Ref{BlasInt}() ccall((@blasfunc($gebak), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), job, side, size(V,1), ilo, ihi, scale, n, V, max(1,stride(V,2)), info, 1, 1) chklapackerror(info[]) V end end end """ gebal!(job, A) -> (ilo, ihi, scale) Balance the matrix `A` before computing its eigensystem or Schur factorization. `job` can be one of `N` (`A` will not be permuted or scaled), `P` (`A` will only be permuted), `S` (`A` will only be scaled), or `B` (`A` will be both permuted and scaled). Modifies `A` in-place and returns `ilo`, `ihi`, and `scale`. If permuting was turned on, `A[i,j] = 0` if `j > i` and `1 < j < ilo` or `j > ihi`. `scale` contains information about the scaling/permutations performed. """ gebal!(job::AbstractChar, A::AbstractMatrix) """ gebak!(job, side, ilo, ihi, scale, V) Transform the eigenvectors `V` of a matrix balanced using `gebal!` to the unscaled/unpermuted eigenvectors of the original matrix. Modifies `V` in-place. `side` can be `L` (left eigenvectors are transformed) or `R` (right eigenvectors are transformed). """ gebak!(job::AbstractChar, side::AbstractChar, ilo::BlasInt, ihi::BlasInt, scale::AbstractVector, V::AbstractMatrix) # (GE) general matrices, direct decompositions # # These mutating functions take as arguments all the values they # return, even if the value of the function does not depend on them # (e.g. the tau argument). This is so that a factorization can be # updated in place. The condensed mutating functions, usually a # function of A only, are defined after this block. for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty) in ((:dgebrd_,:dgelqf_,:dgeqlf_,:dgeqrf_,:dgeqp3_,:dgeqrt_,:dgeqrt3_,:dgerqf_,:dgetrf_,:Float64,:Float64), (:sgebrd_,:sgelqf_,:sgeqlf_,:sgeqrf_,:sgeqp3_,:sgeqrt_,:sgeqrt3_,:sgerqf_,:sgetrf_,:Float32,:Float32), (:zgebrd_,:zgelqf_,:zgeqlf_,:zgeqrf_,:zgeqp3_,:zgeqrt_,:zgeqrt3_,:zgerqf_,:zgetrf_,:ComplexF64,:Float64), (:cgebrd_,:cgelqf_,:cgeqlf_,:cgeqrf_,:cgeqp3_,:cgeqrt_,:cgeqrt3_,:cgerqf_,:cgetrf_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE DGEBRD( M, N, A, LDA, D, E, TAUQ, TAUP, WORK, LWORK, # INFO ) # .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAUP( * ), # TAUQ( * ), WORK( * ) function gebrd!(A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) m, n = size(A) k = min(m, n) d = similar(A, $relty, k) e = similar(A, $relty, k) tauq = similar(A, $elty, k) taup = similar(A, $elty, k) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gebrd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, max(1,stride(A,2)), d, e, tauq, taup, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, d, e, tauq, taup end # SUBROUTINE DGELQF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function gelqf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A,tau) m = BlasInt(size(A, 1)) n = BlasInt(size(A, 2)) lda = BlasInt(max(1,stride(A, 2))) if length(tau) != min(m,n) throw(DimensionMismatch("tau has length $(length(tau)), but needs length $(min(m,n))")) end lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelqf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, lda, tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, tau end # SUBROUTINE DGEQLF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function geqlf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A,tau) m = BlasInt(size(A, 1)) n = BlasInt(size(A, 2)) lda = BlasInt(max(1,stride(A, 2))) if length(tau) != min(m,n) throw(DimensionMismatch("tau has length $(length(tau)), but needs length $(min(m,n))")) end lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqlf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, lda, tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, tau end # SUBROUTINE DGEQP3( M, N, A, LDA, JPVT, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # * .. Array Arguments .. # INTEGER JPVT( * ) # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function geqp3!(A::AbstractMatrix{$elty}, jpvt::AbstractVector{BlasInt}, tau::AbstractVector{$elty}) require_one_based_indexing(A, jpvt, tau) chkstride1(A,jpvt,tau) m,n = size(A) if length(tau) != min(m,n) throw(DimensionMismatch("tau has length $(length(tau)), but needs length $(min(m,n))")) end if length(jpvt) != n throw(DimensionMismatch("jpvt has length $(length(jpvt)), but needs length $n")) end lda = stride(A,2) if lda == 0 return A, tau, jpvt end # Early exit work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) cmplx = eltype(A)<:Complex if cmplx rwork = Vector{$relty}(undef, 2n) end info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geqp3), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), m, n, A, lda, jpvt, tau, work, lwork, rwork, info) else ccall((@blasfunc($geqp3), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, lda, jpvt, tau, work, lwork, info) end chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end return A, tau, jpvt end function geqrt!(A::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}) require_one_based_indexing(A, T) chkstride1(A) m, n = size(A) minmn = min(m, n) nb = size(T, 1) if nb > minmn throw(ArgumentError("block size $nb > $minmn too large")) end lda = max(1, stride(A,2)) work = Vector{$elty}(undef, nb*n) if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), m, n, nb, A, lda, T, max(1,stride(T,2)), work, info) chklapackerror(info[]) end A, T end function geqrt3!(A::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}) require_one_based_indexing(A, T) chkstride1(A) chkstride1(T) m, n = size(A) p, q = size(T) if m < n throw(DimensionMismatch("input matrix A has dimensions ($m,$n), but should have more rows than columns")) end if p != n || q != n throw(DimensionMismatch("block reflector T has dimensions ($p,$q), but should have dimensions ($n,$n)")) end if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt3), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, max(1, stride(A, 2)), T, max(1,stride(T,2)), info) chklapackerror(info[]) end A, T end ## geqrfp! - positive elements on diagonal of R - not defined yet # SUBROUTINE DGEQRFP( M, N, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function geqrf!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A,tau) m, n = size(A) if length(tau) != min(m,n) throw(DimensionMismatch("tau has length $(length(tau)), but needs length $(min(m,n))")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = max(BlasInt(1),BlasInt(real(work[1]))) resize!(work, lwork) end end A, tau end # SUBROUTINE DGERQF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function gerqf!(A::AbstractMatrix{$elty},tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A,tau) m, n = size(A) if length(tau) != min(m,n) throw(DimensionMismatch("tau has length $(length(tau)), but needs length $(min(m,n))")) end lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gerqf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = max(BlasInt(m), BlasInt(real(work[1]))) resize!(work, lwork) end end A, tau end # SUBROUTINE DGETRF( M, N, A, LDA, IPIV, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, M, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ) function getrf!(A::AbstractMatrix{$elty}; check = true) require_one_based_indexing(A) check && chkfinite(A) chkstride1(A) m, n = size(A) lda = max(1,stride(A, 2)) ipiv = similar(A, BlasInt, min(m,n)) info = Ref{BlasInt}() ccall((@blasfunc($getrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), m, n, A, lda, ipiv, info) chkargsok(info[]) A, ipiv, info[] #Error code is stored in LU factorization type end end end """ gebrd!(A) -> (A, d, e, tauq, taup) Reduce `A` in-place to bidiagonal form `A = QBP'`. Returns `A`, containing the bidiagonal matrix `B`; `d`, containing the diagonal elements of `B`; `e`, containing the off-diagonal elements of `B`; `tauq`, containing the elementary reflectors representing `Q`; and `taup`, containing the elementary reflectors representing `P`. """ gebrd!(A::AbstractMatrix) """ gelqf!(A, tau) Compute the `LQ` factorization of `A`, `A = LQ`. `tau` contains scalars which parameterize the elementary reflectors of the factorization. `tau` must have length greater than or equal to the smallest dimension of `A`. Returns `A` and `tau` modified in-place. """ gelqf!(A::AbstractMatrix, tau::AbstractVector) """ geqlf!(A, tau) Compute the `QL` factorization of `A`, `A = QL`. `tau` contains scalars which parameterize the elementary reflectors of the factorization. `tau` must have length greater than or equal to the smallest dimension of `A`. Returns `A` and `tau` modified in-place. """ geqlf!(A::AbstractMatrix, tau::AbstractVector) """ geqp3!(A, [jpvt, tau]) -> (A, tau, jpvt) Compute the pivoted `QR` factorization of `A`, `AP = QR` using BLAS level 3. `P` is a pivoting matrix, represented by `jpvt`. `tau` stores the elementary reflectors. The arguments `jpvt` and `tau` are optional and allow for passing preallocated arrays. When passed, `jpvt` must have length greater than or equal to `n` if `A` is an `(m x n)` matrix and `tau` must have length greater than or equal to the smallest dimension of `A`. `A`, `jpvt`, and `tau` are modified in-place. """ geqp3!(A::AbstractMatrix, jpvt::AbstractVector{BlasInt}, tau::AbstractVector) function geqp3!(A::AbstractMatrix{<:BlasFloat}, jpvt::AbstractVector{BlasInt}) m, n = size(A) geqp3!(A, jpvt, similar(A, min(m, n))) end function geqp3!(A::AbstractMatrix{<:BlasFloat}) m, n = size(A) geqp3!(A, zeros(BlasInt, n), similar(A, min(m, n))) end """ geqrt!(A, T) Compute the blocked `QR` factorization of `A`, `A = QR`. `T` contains upper triangular block reflectors which parameterize the elementary reflectors of the factorization. The first dimension of `T` sets the block size and it must be between 1 and `n`. The second dimension of `T` must equal the smallest dimension of `A`. Returns `A` and `T` modified in-place. """ geqrt!(A::AbstractMatrix, T::AbstractMatrix) """ geqrt3!(A, T) Recursively computes the blocked `QR` factorization of `A`, `A = QR`. `T` contains upper triangular block reflectors which parameterize the elementary reflectors of the factorization. The first dimension of `T` sets the block size and it must be between 1 and `n`. The second dimension of `T` must equal the smallest dimension of `A`. Returns `A` and `T` modified in-place. """ geqrt3!(A::AbstractMatrix, T::AbstractMatrix) """ geqrf!(A, tau) Compute the `QR` factorization of `A`, `A = QR`. `tau` contains scalars which parameterize the elementary reflectors of the factorization. `tau` must have length greater than or equal to the smallest dimension of `A`. Returns `A` and `tau` modified in-place. """ geqrf!(A::AbstractMatrix, tau::AbstractVector) """ gerqf!(A, tau) Compute the `RQ` factorization of `A`, `A = RQ`. `tau` contains scalars which parameterize the elementary reflectors of the factorization. `tau` must have length greater than or equal to the smallest dimension of `A`. Returns `A` and `tau` modified in-place. """ gerqf!(A::AbstractMatrix, tau::AbstractVector) """ getrf!(A) -> (A, ipiv, info) Compute the pivoted `LU` factorization of `A`, `A = LU`. Returns `A`, modified in-place, `ipiv`, the pivoting information, and an `info` code which indicates success (`info = 0`), a singular value in `U` (`info = i`, in which case `U[i,i]` is singular), or an error code (`info < 0`). """ getrf!(A::AbstractMatrix, tau::AbstractVector) """ gelqf!(A) -> (A, tau) Compute the `LQ` factorization of `A`, `A = LQ`. Returns `A`, modified in-place, and `tau`, which contains scalars which parameterize the elementary reflectors of the factorization. """ gelqf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); gelqf!(A, similar(A, min(m, n)))) """ geqlf!(A) -> (A, tau) Compute the `QL` factorization of `A`, `A = QL`. Returns `A`, modified in-place, and `tau`, which contains scalars which parameterize the elementary reflectors of the factorization. """ geqlf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); geqlf!(A, similar(A, min(m, n)))) """ geqrt!(A, nb) -> (A, T) Compute the blocked `QR` factorization of `A`, `A = QR`. `nb` sets the block size and it must be between 1 and `n`, the second dimension of `A`. Returns `A`, modified in-place, and `T`, which contains upper triangular block reflectors which parameterize the elementary reflectors of the factorization. """ geqrt!(A::AbstractMatrix{<:BlasFloat}, nb::Integer) = geqrt!(A, similar(A, nb, minimum(size(A)))) """ geqrt3!(A) -> (A, T) Recursively computes the blocked `QR` factorization of `A`, `A = QR`. Returns `A`, modified in-place, and `T`, which contains upper triangular block reflectors which parameterize the elementary reflectors of the factorization. """ geqrt3!(A::AbstractMatrix{<:BlasFloat}) = (n = size(A, 2); geqrt3!(A, similar(A, n, n))) """ geqrf!(A) -> (A, tau) Compute the `QR` factorization of `A`, `A = QR`. Returns `A`, modified in-place, and `tau`, which contains scalars which parameterize the elementary reflectors of the factorization. """ geqrf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); geqrf!(A, similar(A, min(m, n)))) """ gerqf!(A) -> (A, tau) Compute the `RQ` factorization of `A`, `A = RQ`. Returns `A`, modified in-place, and `tau`, which contains scalars which parameterize the elementary reflectors of the factorization. """ gerqf!(A::AbstractMatrix{<:BlasFloat}) = ((m,n) = size(A); gerqf!(A, similar(A, min(m, n)))) ## Tools to compute and apply elementary reflectors for (larfg, elty) in ((:dlarfg_, Float64), (:slarfg_, Float32), (:zlarfg_, ComplexF64), (:clarfg_, ComplexF32)) @eval begin # .. Scalar Arguments .. # INTEGER incx, n # DOUBLE PRECISION alpha, tau # .. # .. Array Arguments .. # DOUBLE PRECISION x( * ) function larfg!(x::AbstractVector{$elty}) N = BlasInt(length(x)) ฮฑ = Ref{$elty}(x[1]) incx = BlasInt(1) ฯ„ = Ref{$elty}(0) ccall((@blasfunc($larfg), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}), N, ฮฑ, pointer(x, 2), incx, ฯ„) @inbounds x[1] = one($elty) return ฯ„[] end end end for (larf, elty) in ((:dlarf_, Float64), (:slarf_, Float32), (:zlarf_, ComplexF64), (:clarf_, ComplexF32)) @eval begin # .. Scalar Arguments .. # CHARACTER side # INTEGER incv, ldc, m, n # DOUBLE PRECISION tau # .. # .. Array Arguments .. # DOUBLE PRECISION c( ldc, * ), v( * ), work( * ) function larf!(side::AbstractChar, v::AbstractVector{$elty}, ฯ„::$elty, C::AbstractMatrix{$elty}, work::AbstractVector{$elty}) m, n = size(C) chkside(side) ldc = max(1, stride(C, 2)) l = side == 'L' ? n : m incv = BlasInt(1) ccall((@blasfunc($larf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Clong), side, m, n, v, incv, ฯ„, C, ldc, work, 1) return C end function larf!(side::AbstractChar, v::AbstractVector{$elty}, ฯ„::$elty, C::AbstractMatrix{$elty}) m, n = size(C) chkside(side) lwork = side == 'L' ? n : m return larf!(side, v, ฯ„, C, Vector{$elty}(undef,lwork)) end end end ## Complete orthogonaliztion tools for (tzrzf, ormrz, elty) in ((:dtzrzf_,:dormrz_,:Float64), (:stzrzf_,:sormrz_,:Float32), (:ztzrzf_,:zunmrz_,:ComplexF64), (:ctzrzf_,:cunmrz_,:ComplexF32)) @eval begin # SUBROUTINE ZTZRZF( M, N, A, LDA, TAU, WORK, LWORK, INFO ) # # .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, M, N # .. # .. Array Arguments .. # COMPLEX*16 A( LDA, * ), TAU( * ), WORK( * ) function tzrzf!(A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) m, n = size(A) if n < m throw(DimensionMismatch("input matrix A has dimensions ($m,$n), but cannot have fewer columns than rows")) end lda = max(1, stride(A,2)) tau = similar(A, $elty, m) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($tzrzf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, A, lda, tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, tau end # SUBROUTINE ZUNMRZ( SIDE, TRANS, M, N, K, L, A, LDA, TAU, C, LDC, # WORK, LWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER SIDE, TRANS # INTEGER INFO, K, L, LDA, LDC, LWORK, M, N # .. # .. Array Arguments .. # COMPLEX*16 A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) function ormrz!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractMatrix{$elty}) require_one_based_indexing(A, tau, C) chktrans(trans) chkside(side) chkstride1(A, tau, C) m, n = size(C) k = length(tau) l = size(A, 2) - size(A, 1) lda = max(1, stride(A,2)) ldc = max(1, stride(C,2)) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrz), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, l, A, lda, tau, C, ldc, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end end end """ ormrz!(side, trans, A, tau, C) Multiplies the matrix `C` by `Q` from the transformation supplied by `tzrzf!`. Depending on `side` or `trans` the multiplication can be left-sided (`side = L, Q*C`) or right-sided (`side = R, C*Q`) and `Q` can be unmodified (`trans = N`), transposed (`trans = T`), or conjugate transposed (`trans = C`). Returns matrix `C` which is modified in-place with the result of the multiplication. """ ormrz!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractMatrix) """ tzrzf!(A) -> (A, tau) Transforms the upper trapezoidal matrix `A` to upper triangular form in-place. Returns `A` and `tau`, the scalar parameters for the elementary reflectors of the transformation. """ tzrzf!(A::AbstractMatrix) ## (GE) general matrices, solvers with factorization, solver and inverse for (gels, gesv, getrs, getri, elty) in ((:dgels_,:dgesv_,:dgetrs_,:dgetri_,:Float64), (:sgels_,:sgesv_,:sgetrs_,:sgetri_,:Float32), (:zgels_,:zgesv_,:zgetrs_,:zgetri_,:ComplexF64), (:cgels_,:cgesv_,:cgetrs_,:cgetri_,:ComplexF32)) @eval begin # SUBROUTINE DGELS( TRANS, M, N, NRHS, A, LDA, B, LDB, WORK, LWORK,INFO) # * .. Scalar Arguments .. # CHARACTER TRANS # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS function gels!(trans::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chktrans(trans) chkstride1(A, B) btrn = trans == 'T' m, n = size(A) if size(B,1) != (btrn ? n : m) throw(DimensionMismatch("matrix A has dimensions ($m,$n), transposed: $btrn, but leading dimension of B is $(size(B,1))")) end info = Ref{BlasInt}() work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gels), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), (btrn ? 'T' : 'N'), m, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end k = min(m, n) F = m < n ? tril(A[1:k, 1:k]) : triu(A[1:k, 1:k]) ssr = Vector{$elty}(undef, size(B, 2)) for i = 1:size(B,2) x = zero($elty) for j = k+1:size(B,1) x += abs2(B[j,i]) end ssr[i] = x end F, subsetrows(B, B, k), ssr end # SUBROUTINE DGESV( N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function gesv!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) n = checksquare(A) if size(B,1) != n throw(DimensionMismatch("B has leading dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gesv), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B, A, ipiv end # SUBROUTINE DGETRS( TRANS, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) #* .. Scalar Arguments .. # CHARACTER TRANS # INTEGER INFO, LDA, LDB, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function getrs!(trans::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chktrans(trans) chkstride1(A, B, ipiv) n = checksquare(A) if n != size(B, 1) throw(DimensionMismatch("B has leading dimension $(size(B,1)), but needs $n")) end if n != length(ipiv) throw(DimensionMismatch("ipiv has length $(length(ipiv)), but needs to be $n")) end nrhs = size(B, 2) info = Ref{BlasInt}() ccall((@blasfunc($getrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), trans, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end # SUBROUTINE DGETRI( N, A, LDA, IPIV, WORK, LWORK, INFO ) #* .. Scalar Arguments .. # INTEGER INFO, LDA, LWORK, N #* .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function getri!(A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) require_one_based_indexing(A, ipiv) chkstride1(A, ipiv) n = checksquare(A) if n != length(ipiv) throw(DimensionMismatch("ipiv has length $(length(ipiv)), but needs $n")) end lda = max(1,stride(A, 2)) lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($getri), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, A, lda, ipiv, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A end end end """ gels!(trans, A, B) -> (F, B, ssr) Solves the linear equation `A * X = B`, `transpose(A) * X = B`, or `adjoint(A) * X = B` using a QR or LQ factorization. Modifies the matrix/vector `B` in place with the solution. `A` is overwritten with its `QR` or `LQ` factorization. `trans` may be one of `N` (no modification), `T` (transpose), or `C` (conjugate transpose). `gels!` searches for the minimum norm/least squares solution. `A` may be under or over determined. The solution is returned in `B`. """ gels!(trans::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) """ gesv!(A, B) -> (B, A, ipiv) Solves the linear equation `A * X = B` where `A` is a square matrix using the `LU` factorization of `A`. `A` is overwritten with its `LU` factorization and `B` is overwritten with the solution `X`. `ipiv` contains the pivoting information for the `LU` factorization of `A`. """ gesv!(A::AbstractMatrix, B::AbstractVecOrMat) """ getrs!(trans, A, ipiv, B) Solves the linear equation `A * X = B`, `transpose(A) * X = B`, or `adjoint(A) * X = B` for square `A`. Modifies the matrix/vector `B` in place with the solution. `A` is the `LU` factorization from `getrf!`, with `ipiv` the pivoting information. `trans` may be one of `N` (no modification), `T` (transpose), or `C` (conjugate transpose). """ getrs!(trans::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) """ getri!(A, ipiv) Computes the inverse of `A`, using its `LU` factorization found by `getrf!`. `ipiv` is the pivot information output and `A` contains the `LU` factorization of `getrf!`. `A` is overwritten with its inverse. """ getri!(A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) for (gesvx, elty) in ((:dgesvx_,:Float64), (:sgesvx_,:Float32)) @eval begin # SUBROUTINE DGESVX( FACT, TRANS, N, NRHS, A, LDA, AF, LDAF, IPIV, # EQUED, R, C, B, LDB, X, LDX, RCOND, FERR, BERR, # WORK, IWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER EQUED, FACT, TRANS # INTEGER INFO, LDA, LDAF, LDB, LDX, N, NRHS # DOUBLE PRECISION RCOND # .. # .. Array Arguments .. # INTEGER IPIV( * ), IWORK( * ) # DOUBLE PRECISION A( LDA, * ), AF( LDAF, * ), B( LDB, * ), # $ BERR( * ), C( * ), FERR( * ), R( * ), # $ WORK( * ), X( LDX, * # function gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, AF::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, equed::AbstractChar, R::AbstractVector{$elty}, C::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, AF, ipiv, R, C, B) chktrans(trans) chkstride1(ipiv, R, C, B) n = checksquare(A) lda = stride(A,2) n = checksquare(AF) ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) rcond = Ref{$elty}() ferr = similar(A, $elty, nrhs) berr = similar(A, $elty, nrhs) work = Vector{$elty}(undef, 4n) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, ldb, X, n, rcond, ferr, berr, work, iwork, info, 1, 1, 1) chklapackerror(info[]) if info[] == n + 1 @warn "Matrix is singular to working precision" else chknonsingular(info[]) end #WORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) X, equed, R, C, B, rcond[], ferr, berr, work[1] end function gesvx!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) n = size(A,1) X, equed, R, C, B, rcond, ferr, berr, rpgf = gesvx!('N', 'N', A, similar(A, $elty, n, n), similar(A, BlasInt, n), 'N', similar(A, $elty, n), similar(A, $elty, n), B) X, rcond, ferr, berr, rpgf end end end for (gesvx, elty, relty) in ((:zgesvx_,:ComplexF64,:Float64), (:cgesvx_,:ComplexF32 ,:Float32)) @eval begin # SUBROUTINE ZGESVX( FACT, TRANS, N, NRHS, A, LDA, AF, LDAF, IPIV, # EQUED, R, C, B, LDB, X, LDX, RCOND, FERR, BERR, # WORK, RWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER EQUED, FACT, TRANS # INTEGER INFO, LDA, LDAF, LDB, LDX, N, NRHS # DOUBLE PRECISION RCOND # .. # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION BERR( * ), C( * ), FERR( * ), R( * ), # $ RWORK( * ) # COMPLEX*16 A( LDA, * ), AF( LDAF, * ), B( LDB, * ), # $ WORK( * ), X( LDX, * ) function gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, AF::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, equed::AbstractChar, R::AbstractVector{$relty}, C::AbstractVector{$relty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, AF, ipiv, R, C, B) chktrans(trans) chkstride1(A, AF, ipiv, R, C, B) n = checksquare(A) lda = stride(A,2) n = checksquare(AF) ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) rcond = Ref{$relty}() ferr = similar(A, $relty, nrhs) berr = similar(A, $relty, nrhs) work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, 2n) info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong), fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, ldb, X, n, rcond, ferr, berr, work, rwork, info, 1, 1, 1) chklapackerror(info[]) if info[] == n + 1 @warn "Matrix is singular to working precision" else chknonsingular(info[]) end #RWORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) X, equed, R, C, B, rcond[], ferr, berr, rwork[1] end #Wrapper for the no-equilibration, no-transpose calculation function gesvx!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) n = size(A,1) X, equed, R, C, B, rcond, ferr, berr, rpgf = gesvx!('N', 'N', A, similar(A, $elty, n, n), similar(A, BlasInt, n), 'N', similar(A, $relty, n), similar(A, $relty, n), B) X, rcond, ferr, berr, rpgf end end end """ gesvx!(fact, trans, A, AF, ipiv, equed, R, C, B) -> (X, equed, R, C, B, rcond, ferr, berr, work) Solves the linear equation `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), or `adjoint(A) * X = B` (`trans = C`) using the `LU` factorization of `A`. `fact` may be `E`, in which case `A` will be equilibrated and copied to `AF`; `F`, in which case `AF` and `ipiv` from a previous `LU` factorization are inputs; or `N`, in which case `A` will be copied to `AF` and then factored. If `fact = F`, `equed` may be `N`, meaning `A` has not been equilibrated; `R`, meaning `A` was multiplied by `Diagonal(R)` from the left; `C`, meaning `A` was multiplied by `Diagonal(C)` from the right; or `B`, meaning `A` was multiplied by `Diagonal(R)` from the left and `Diagonal(C)` from the right. If `fact = F` and `equed = R` or `B` the elements of `R` must all be positive. If `fact = F` and `equed = C` or `B` the elements of `C` must all be positive. Returns the solution `X`; `equed`, which is an output if `fact` is not `N`, and describes the equilibration that was performed; `R`, the row equilibration diagonal; `C`, the column equilibration diagonal; `B`, which may be overwritten with its equilibrated form `Diagonal(R)*B` (if `trans = N` and `equed = R,B`) or `Diagonal(C)*B` (if `trans = T,C` and `equed = C,B`); `rcond`, the reciprocal condition number of `A` after equilbrating; `ferr`, the forward error bound for each solution vector in `X`; `berr`, the forward error bound for each solution vector in `X`; and `work`, the reciprocal pivot growth factor. """ gesvx!(fact::AbstractChar, trans::AbstractChar, A::AbstractMatrix, AF::AbstractMatrix, ipiv::AbstractVector{BlasInt}, equed::AbstractChar, R::AbstractVector, C::AbstractVector, B::AbstractVecOrMat) """ gesvx!(A, B) The no-equilibration, no-transpose simplification of `gesvx!`. """ gesvx!(A::AbstractMatrix, B::AbstractVecOrMat) for (gelsd, gelsy, elty) in ((:dgelsd_,:dgelsy_,:Float64), (:sgelsd_,:sgelsy_,:Float32)) @eval begin # SUBROUTINE DGELSD( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, # $ WORK, LWORK, IWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK # DOUBLE PRECISION RCOND # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), S( * ), WORK( * ) function gelsd!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=-one($elty)) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) if size(B, 1) != m throw(DimensionMismatch("B has leading dimension $(size(B,1)) but needs $m")) end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $elty, min(m, n)) rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) for i = 1:2 # first call returns lwork as work[1] and iwork length as iwork[1] ccall((@blasfunc($gelsd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), m, n, size(B,2), A, max(1,stride(A,2)), newB, max(1,stride(B,2),n), s, $elty(rcond), rnk, work, lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) resize!(iwork, iwork[1]) end end subsetrows(B, newB, n), rnk[] end # SUBROUTINE DGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, # $ WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK # DOUBLE PRECISION RCOND # * .. # * .. Array Arguments .. # INTEGER JPVT( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) function gelsy!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=eps($elty)) require_one_based_indexing(A, B) chkstride1(A) m = size(A, 1) n = size(A, 2) nrhs = size(B, 2) if size(B, 1) != m throw(DimensionMismatch("B has leading dimension $(size(B,1)) but needs $m")) end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] lda = max(1, stride(A,2)) ldb = max(1, stride(newB,2)) jpvt = zeros(BlasInt, n) rnk = Ref{BlasInt}() work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, nrhs, A, lda, newB, ldb, jpvt, $elty(rcond), rnk, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end subsetrows(B, newB, n), rnk[] end end end for (gelsd, gelsy, elty, relty) in ((:zgelsd_,:zgelsy_,:ComplexF64,:Float64), (:cgelsd_,:cgelsy_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZGELSD( M, N, NRHS, A, LDA, B, LDB, S, RCOND, RANK, # $ WORK, LWORK, RWORK, IWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK # DOUBLE PRECISION RCOND # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION RWORK( * ), S( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function gelsd!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=-one($relty)) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) if size(B, 1) != m throw(DimensionMismatch("B has leading dimension $(size(B,1)) but needs $m")) end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $relty, min(m, n)) rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 1) iwork = Vector{BlasInt}(undef, 1) for i = 1:2 # first call returns lwork as work[1], rwork length as rwork[1] and iwork length as iwork[1] ccall((@blasfunc($gelsd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ref{BlasInt}), m, n, size(B,2), A, max(1,stride(A,2)), newB, max(1,stride(B,2),n), s, $relty(rcond), rnk, work, lwork, rwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) resize!(rwork, BlasInt(rwork[1])) resize!(iwork, iwork[1]) end end subsetrows(B, newB, n), rnk[] end # SUBROUTINE ZGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, # $ WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, LWORK, M, N, NRHS, RANK # DOUBLE PRECISION RCOND # * .. # * .. Array Arguments .. # INTEGER JPVT( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function gelsy!(A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, rcond::Real=eps($relty)) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) nrhs = size(B, 2) if size(B, 1) != m throw(DimensionMismatch("B has leading dimension $(size(B,1)) but needs $m")) end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] lda = max(1, m) ldb = max(1, m, n) jpvt = zeros(BlasInt, n) rnk = Ref{BlasInt}(1) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 2n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), m, n, nrhs, A, lda, newB, ldb, jpvt, $relty(rcond), rnk, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end subsetrows(B, newB, n), rnk[] end end end """ gelsd!(A, B, rcond) -> (B, rnk) Computes the least norm solution of `A * X = B` by finding the `SVD` factorization of `A`, then dividing-and-conquering the problem. `B` is overwritten with the solution `X`. Singular values below `rcond` will be treated as zero. Returns the solution in `B` and the effective rank of `A` in `rnk`. """ gelsd!(A::AbstractMatrix, B::AbstractVecOrMat, rcond::Real) """ gelsy!(A, B, rcond) -> (B, rnk) Computes the least norm solution of `A * X = B` by finding the full `QR` factorization of `A`, then dividing-and-conquering the problem. `B` is overwritten with the solution `X`. Singular values below `rcond` will be treated as zero. Returns the solution in `B` and the effective rank of `A` in `rnk`. """ gelsy!(A::AbstractMatrix, B::AbstractVecOrMat, rcond::Real) for (gglse, elty) in ((:dgglse_, :Float64), (:sgglse_, :Float32), (:zgglse_, :ComplexF64), (:cgglse_, :ComplexF32)) @eval begin # SUBROUTINE DGGLSE( M, N, P, A, LDA, B, LDB, C, D, X, WORK, LWORK, # $ INFO ) # * .. Scalar Arguments .. # INTEGER INFO, LDA, LDB, LWORK, M, N, P # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), C( * ), D( * ), # $ WORK( * ), X( * ) function gglse!(A::AbstractMatrix{$elty}, c::AbstractVector{$elty}, B::AbstractMatrix{$elty}, d::AbstractVector{$elty}) require_one_based_indexing(A, c, B, d) chkstride1(A, c, B, d) m, n = size(A) p = size(B, 1) if size(B, 2) != n throw(DimensionMismatch("B has second dimension $(size(B,2)), needs $n")) end if length(c) != m throw(DimensionMismatch("c has length $(length(c)), needs $m")) end if length(d) != p throw(DimensionMismatch("d has length $(length(d)), needs $p")) end X = zeros($elty, n) info = Ref{BlasInt}() work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gglse), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, p, A, max(1,stride(A,2)), B, max(1,stride(B,2)), c, d, X, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end X, dot(view(c, n - p + 1:m), view(c, n - p + 1:m)) end end end """ gglse!(A, c, B, d) -> (X,res) Solves the equation `A * x = c` where `x` is subject to the equality constraint `B * x = d`. Uses the formula `||c - A*x||^2 = 0` to solve. Returns `X` and the residual sum-of-squares. """ gglse!(A::AbstractMatrix, c::AbstractVector, B::AbstractMatrix, d::AbstractVector) # (GE) general matrices eigenvalue-eigenvector and singular value decompositions for (geev, gesvd, gesdd, ggsvd, elty, relty) in ((:dgeev_,:dgesvd_,:dgesdd_,:dggsvd_,:Float64,:Float64), (:sgeev_,:sgesvd_,:sgesdd_,:sggsvd_,:Float32,:Float32), (:zgeev_,:zgesvd_,:zgesdd_,:zggsvd_,:ComplexF64,:Float64), (:cgeev_,:cgesvd_,:cgesdd_,:cggsvd_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE DGEEV( JOBVL, JOBVR, N, A, LDA, WR, WI, VL, LDVL, VR, # $ LDVR, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBVL, JOBVR # INTEGER INFO, LDA, LDVL, LDVR, LWORK, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), VL( LDVL, * ), VR( LDVR, * ), # $ WI( * ), WORK( * ), WR( * ) function geev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkfinite(A) # balancing routines don't support NaNs and Infs lvecs = jobvl == 'V' rvecs = jobvr == 'V' VL = similar(A, $elty, (n, lvecs ? n : 0)) VR = similar(A, $elty, (n, rvecs ? n : 0)) cmplx = eltype(A) <: Complex if cmplx W = similar(A, $elty, n) rwork = similar(A, $relty, 2n) else WR = similar(A, $elty, n) WI = similar(A, $elty, n) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, max(1,stride(A,2)), W, VL, n, VR, n, work, lwork, rwork, info, 1, 1) else ccall((@blasfunc($geev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, max(1,stride(A,2)), WR, WI, VL, n, VR, n, work, lwork, info, 1, 1) end chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end cmplx ? (W, VL, VR) : (WR, WI, VL, VR) end # SUBROUTINE DGESDD( JOBZ, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, # LWORK, IWORK, INFO ) #* .. Scalar Arguments .. # CHARACTER JOBZ # INTEGER INFO, LDA, LDU, LDVT, LWORK, M, N #* .. #* .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), S( * ), U( LDU, * ), # VT( LDVT, * ), WORK( * ) function gesdd!(job::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) m, n = size(A) minmn = min(m, n) if job == 'A' U = similar(A, $elty, (m, m)) VT = similar(A, $elty, (n, n)) elseif job == 'S' U = similar(A, $elty, (m, minmn)) VT = similar(A, $elty, (minmn, n)) elseif job == 'O' U = similar(A, $elty, (m, m >= n ? 0 : m)) VT = similar(A, $elty, (n, m >= n ? n : 0)) else U = similar(A, $elty, (m, 0)) VT = similar(A, $elty, (n, 0)) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) S = similar(A, $relty, minmn) cmplx = eltype(A)<:Complex if cmplx rwork = Vector{$relty}(undef, job == 'N' ? 7*minmn : minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) end iwork = Vector{BlasInt}(undef, 8*minmn) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesdd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, rwork, iwork, info, 1) else ccall((@blasfunc($gesdd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), job, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, iwork, info, 1) end chklapackerror(info[]) if i == 1 # Work around issue with truncated Float32 representation of lwork in # sgesdd by using nextfloat. See # http://icl.cs.utk.edu/lapack-forum/viewtopic.php?f=13&t=4587&p=11036&hilit=sgesdd#p11036 # and # https://github.com/scipy/scipy/issues/5401 lwork = round(BlasInt, nextfloat(real(work[1]))) resize!(work, lwork) end end if job == 'O' if m >= n return (A, S, VT) else # ()__ # ||::Z__ # ||::|:::Z____ # ||::|:::|====| # ||==|===|====| # ||""|===|====| # || `"""|====| # || `""""` return (U, S, A) end end return (U, S, VT) end # SUBROUTINE DGESVD( JOBU, JOBVT, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBU, JOBVT # INTEGER INFO, LDA, LDU, LDVT, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), S( * ), U( LDU, * ), # $ VT( LDVT, * ), WORK( * ) function gesvd!(jobu::AbstractChar, jobvt::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) m, n = size(A) minmn = min(m, n) S = similar(A, $relty, minmn) U = similar(A, $elty, jobu == 'A' ? (m, m) : (jobu == 'S' ? (m, minmn) : (m, 0))) VT = similar(A, $elty, jobvt == 'A' ? (n, n) : (jobvt == 'S' ? (minmn, n) : (n, 0))) work = Vector{$elty}(undef, 1) cmplx = eltype(A) <: Complex if cmplx rwork = Vector{$relty}(undef, 5minmn) end lwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesvd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, rwork, info, 1, 1) else ccall((@blasfunc($gesvd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobu, jobvt, m, n, A, max(1,stride(A,2)), S, U, max(1,stride(U,2)), VT, max(1,stride(VT,2)), work, lwork, info, 1, 1) end chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end if jobu == 'O' return (A, S, VT) elseif jobvt == 'O' # =============|===========|() # # # #:::::: # # # #:::::: # # # #:::::: # # # #:::::: # # # # # # # # # # # # # # # # # # # # # return (U, S, A) # # # # # # # else # # # # # # # return (U, S, VT) # # # # # # # end end # SUBROUTINE ZGGSVD( JOBU, JOBV, JOBQ, M, N, P, K, L, A, LDA, B, # $ LDB, ALPHA, BETA, U, LDU, V, LDV, Q, LDQ, WORK, # $ RWORK, IWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBQ, JOBU, JOBV # INTEGER INFO, K, L, LDA, LDB, LDQ, LDU, LDV, M, N, P # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION ALPHA( * ), BETA( * ), RWORK( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), Q( LDQ, * ), # $ U( LDU, * ), V( LDV, * ), WORK( * ) function ggsvd!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) if size(B, 2) != n throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs $n")) end p = size(B, 1) k = Vector{BlasInt}(undef, 1) l = Vector{BlasInt}(undef, 1) lda = max(1,stride(A, 2)) ldb = max(1,stride(B, 2)) alpha = similar(A, $relty, n) beta = similar(A, $relty, n) ldu = max(1, m) U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) ldv = max(1, p) V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) ldq = max(1, n) Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) work = Vector{$elty}(undef, max(3n, m, p) + n) cmplx = eltype(A) <: Complex if cmplx rwork = Vector{$relty}(undef, 2n) end iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() if cmplx ccall((@blasfunc($ggsvd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), jobu, jobv, jobq, m, n, p, k, l, A, lda, B, ldb, alpha, beta, U, ldu, V, ldv, Q, ldq, work, rwork, iwork, info, 1, 1, 1) else ccall((@blasfunc($ggsvd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), jobu, jobv, jobq, m, n, p, k, l, A, lda, B, ldb, alpha, beta, U, ldu, V, ldv, Q, ldq, work, iwork, info, 1, 1, 1) end chklapackerror(info[]) if m - k[1] - l[1] >= 0 R = triu(A[1:k[1] + l[1],n - k[1] - l[1] + 1:n]) else R = triu([A[1:m, n - k[1] - l[1] + 1:n]; B[m - k[1] + 1:l[1], n - k[1] - l[1] + 1:n]]) end U, V, Q, alpha, beta, k[1], l[1], R end end end """ geev!(jobvl, jobvr, A) -> (W, VL, VR) Finds the eigensystem of `A`. If `jobvl = N`, the left eigenvectors of `A` aren't computed. If `jobvr = N`, the right eigenvectors of `A` aren't computed. If `jobvl = V` or `jobvr = V`, the corresponding eigenvectors are computed. Returns the eigenvalues in `W`, the right eigenvectors in `VR`, and the left eigenvectors in `VL`. """ geev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix) """ gesdd!(job, A) -> (U, S, VT) Finds the singular value decomposition of `A`, `A = U * S * V'`, using a divide and conquer approach. If `job = A`, all the columns of `U` and the rows of `V'` are computed. If `job = N`, no columns of `U` or rows of `V'` are computed. If `job = O`, `A` is overwritten with the columns of (thin) `U` and the rows of (thin) `V'`. If `job = S`, the columns of (thin) `U` and the rows of (thin) `V'` are computed and returned separately. """ gesdd!(job::AbstractChar, A::AbstractMatrix) """ gesvd!(jobu, jobvt, A) -> (U, S, VT) Finds the singular value decomposition of `A`, `A = U * S * V'`. If `jobu = A`, all the columns of `U` are computed. If `jobvt = A` all the rows of `V'` are computed. If `jobu = N`, no columns of `U` are computed. If `jobvt = N` no rows of `V'` are computed. If `jobu = O`, `A` is overwritten with the columns of (thin) `U`. If `jobvt = O`, `A` is overwritten with the rows of (thin) `V'`. If `jobu = S`, the columns of (thin) `U` are computed and returned separately. If `jobvt = S` the rows of (thin) `V'` are computed and returned separately. `jobu` and `jobvt` can't both be `O`. Returns `U`, `S`, and `Vt`, where `S` are the singular values of `A`. """ gesvd!(jobu::AbstractChar, jobvt::AbstractChar, A::AbstractMatrix) """ ggsvd!(jobu, jobv, jobq, A, B) -> (U, V, Q, alpha, beta, k, l, R) Finds the generalized singular value decomposition of `A` and `B`, `U'*A*Q = D1*R` and `V'*B*Q = D2*R`. `D1` has `alpha` on its diagonal and `D2` has `beta` on its diagonal. If `jobu = U`, the orthogonal/unitary matrix `U` is computed. If `jobv = V` the orthogonal/unitary matrix `V` is computed. If `jobq = Q`, the orthogonal/unitary matrix `Q` is computed. If `jobu`, `jobv` or `jobq` is `N`, that matrix is not computed. This function is only available in LAPACK versions prior to 3.6.0. """ ggsvd!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) for (f, elty) in ((:dggsvd3_, :Float64), (:sggsvd3_, :Float32)) @eval begin function ggsvd3!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) if size(B, 2) != n throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs $n")) end p = size(B, 1) k = Ref{BlasInt}() l = Ref{BlasInt}() lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) alpha = similar(A, $elty, n) beta = similar(A, $elty, n) ldu = max(1, m) U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) ldv = max(1, p) V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) ldq = max(1, n) Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), jobu, jobv, jobq, m, n, p, k, l, A, lda, B, ldb, alpha, beta, U, ldu, V, ldv, Q, ldq, work, lwork, iwork, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end if m - k[] - l[] >= 0 R = triu(A[1:k[] + l[],n - k[] - l[] + 1:n]) else R = triu([A[1:m, n - k[] - l[] + 1:n]; B[m - k[] + 1:l[], n - k[] - l[] + 1:n]]) end return U, V, Q, alpha, beta, k[], l[], R end end end for (f, elty, relty) in ((:zggsvd3_, :ComplexF64, :Float64), (:cggsvd3_, :ComplexF32, :Float32)) @eval begin function ggsvd3!(jobu::AbstractChar, jobv::AbstractChar, jobq::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) m, n = size(A) if size(B, 2) != n throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs $n")) end p = size(B, 1) k = Vector{BlasInt}(undef, 1) l = Vector{BlasInt}(undef, 1) lda = max(1,stride(A, 2)) ldb = max(1,stride(B, 2)) alpha = similar(A, $relty, n) beta = similar(A, $relty, n) ldu = max(1, m) U = jobu == 'U' ? similar(A, $elty, ldu, m) : similar(A, $elty, 0) ldv = max(1, p) V = jobv == 'V' ? similar(A, $elty, ldv, p) : similar(A, $elty, 0) ldq = max(1, n) Q = jobq == 'Q' ? similar(A, $elty, ldq, n) : similar(A, $elty, 0) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 2n) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), jobu, jobv, jobq, m, n, p, k, l, A, lda, B, ldb, alpha, beta, U, ldu, V, ldv, Q, ldq, work, lwork, rwork, iwork, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end if m - k[1] - l[1] >= 0 R = triu(A[1:k[1] + l[1],n - k[1] - l[1] + 1:n]) else R = triu([A[1:m, n - k[1] - l[1] + 1:n]; B[m - k[1] + 1:l[1], n - k[1] - l[1] + 1:n]]) end return U, V, Q, alpha, beta, k[1], l[1], R end end end """ ggsvd3!(jobu, jobv, jobq, A, B) -> (U, V, Q, alpha, beta, k, l, R) Finds the generalized singular value decomposition of `A` and `B`, `U'*A*Q = D1*R` and `V'*B*Q = D2*R`. `D1` has `alpha` on its diagonal and `D2` has `beta` on its diagonal. If `jobu = U`, the orthogonal/unitary matrix `U` is computed. If `jobv = V` the orthogonal/unitary matrix `V` is computed. If `jobq = Q`, the orthogonal/unitary matrix `Q` is computed. If `jobu`, `jobv`, or `jobq` is `N`, that matrix is not computed. This function requires LAPACK 3.6.0. """ ggsvd3! ## Expert driver and generalized eigenvalue problem for (geevx, ggev, ggev3, elty) in ((:dgeevx_,:dggev_,:dggev3_,:Float64), (:sgeevx_,:sggev_,:sggev3_,:Float32)) @eval begin # SUBROUTINE DGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, WR, WI, # VL, LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, # RCONDE, RCONDV, WORK, LWORK, IWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER BALANC, JOBVL, JOBVR, SENSE # INTEGER IHI, ILO, INFO, LDA, LDVL, LDVR, LWORK, N # DOUBLE PRECISION ABNRM # .. # .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), RCONDE( * ), RCONDV( * ), # $ SCALE( * ), VL( LDVL, * ), VR( LDVR, * ), # $ WI( * ), WORK( * ), WR( * ) function geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix{$elty}) n = checksquare(A) chkfinite(A) # balancing routines don't support NaNs and Infs lda = max(1,stride(A,2)) wr = similar(A, $elty, n) wi = similar(A, $elty, n) if balanc โˆ‰ ['N', 'P', 'S', 'B'] throw(ArgumentError("balanc must be 'N', 'P', 'S', or 'B', but $balanc was passed")) end ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 0 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end VL = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 0 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end VR = similar(A, $elty, ldvr, n) ilo = Ref{BlasInt}() ihi = Ref{BlasInt}() scale = similar(A, $elty, n) abnrm = Ref{$elty}() rconde = similar(A, $elty, n) rcondv = similar(A, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iworksize = 0 if sense == 'N' || sense == 'E' iworksize = 0 elseif sense == 'V' || sense == 'B' iworksize = 2*n - 2 else throw(ArgumentError("sense must be 'N', 'E', 'V' or 'B', but $sense was passed")) end iwork = Vector{BlasInt}(undef, iworksize) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong, Clong), balanc, jobvl, jobvr, sense, n, A, lda, wr, wi, VL, max(1,ldvl), VR, max(1,ldvr), ilo, ihi, scale, abnrm, rconde, rcondv, work, lwork, iwork, info, 1, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end A, wr, wi, VL, VR, ilo[], ihi[], scale, abnrm[], rconde, rcondv end # SUBROUTINE DGGEV( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, ALPHAI, # $ BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBVL, JOBVR # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), # $ B( LDB, * ), BETA( * ), VL( LDVL, * ), # $ VR( LDVR, * ), WORK( * ) function ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n, m = checksquare(A,B) if n != m throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) alphar = similar(A, $elty, n) alphai = similar(A, $elty, n) beta = similar(A, $elty, n) ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 1 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end vl = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 1 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end vr = similar(A, $elty, ldvr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alphar, alphai, beta, vl, ldvl, vr, ldvr, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end alphar, alphai, beta, vl, vr end # SUBROUTINE DGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, ALPHAI, # $ BETA, VL, LDVL, VR, LDVR, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBVL, JOBVR # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), # $ B( LDB, * ), BETA( * ), VL( LDVL, * ), # $ VR( LDVR, * ), WORK( * ) function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n, m = checksquare(A,B) if n != m throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) alphar = similar(A, $elty, n) alphai = similar(A, $elty, n) beta = similar(A, $elty, n) ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 1 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end vl = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 1 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end vr = similar(A, $elty, ldvr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alphar, alphai, beta, vl, ldvl, vr, ldvr, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end alphar, alphai, beta, vl, vr end end end for (geevx, ggev, ggev3, elty, relty) in ((:zgeevx_,:zggev_,:zggev3_,:ComplexF64,:Float64), (:cgeevx_,:cggev_,:cggev3_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZGEEVX( BALANC, JOBVL, JOBVR, SENSE, N, A, LDA, W, VL, # LDVL, VR, LDVR, ILO, IHI, SCALE, ABNRM, RCONDE, # RCONDV, WORK, LWORK, RWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER BALANC, JOBVL, JOBVR, SENSE # INTEGER IHI, ILO, INFO, LDA, LDVL, LDVR, LWORK, N # DOUBLE PRECISION ABNRM # .. # .. Array Arguments .. # DOUBLE PRECISION RCONDE( * ), RCONDV( * ), RWORK( * ), # $ SCALE( * ) # COMPLEX*16 A( LDA, * ), VL( LDVL, * ), VR( LDVR, * ), # $ W( * ), WORK( * ) function geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix{$elty}) n = checksquare(A) chkfinite(A) # balancing routines don't support NaNs and Infs lda = max(1,stride(A,2)) w = similar(A, $elty, n) if balanc โˆ‰ ['N', 'P', 'S', 'B'] throw(ArgumentError("balanc must be 'N', 'P', 'S', or 'B', but $balanc was passed")) end ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 0 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end VL = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 0 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end if sense โˆ‰ ['N','E','V','B'] throw(ArgumentError("sense must be 'N', 'E', 'V' or 'B', but $sense was passed")) end VR = similar(A, $elty, ldvr, n) ilo = Ref{BlasInt}() ihi = Ref{BlasInt}() scale = similar(A, $relty, n) abnrm = Ref{$relty}() rconde = similar(A, $relty, n) rcondv = similar(A, $relty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 2n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong, Clong, Clong), balanc, jobvl, jobvr, sense, n, A, lda, w, VL, max(1,ldvl), VR, max(1,ldvr), ilo, ihi, scale, abnrm, rconde, rcondv, work, lwork, rwork, info, 1, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end A, w, VL, VR, ilo[], ihi[], scale, abnrm[], rconde, rcondv end # SUBROUTINE ZGGEV( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, # $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBVL, JOBVR # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), # $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), # $ WORK( * ) function ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) alpha = similar(A, $elty, n) beta = similar(A, $elty, n) ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 1 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end vl = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 1 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end vr = similar(A, $elty, ldvr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 8n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alpha, beta, vl, ldvl, vr, ldvr, work, lwork, rwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end alpha, beta, vl, vr end # SUBROUTINE ZGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, # $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBVL, JOBVR # INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), # $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), # $ WORK( * ) function ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("A has dimensions $(size(A)), and B has dimensions $(size(B)), but A and B must have the same size")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) alpha = similar(A, $elty, n) beta = similar(A, $elty, n) ldvl = 0 if jobvl == 'V' ldvl = n elseif jobvl == 'N' ldvl = 1 else throw(ArgumentError("jobvl must be 'V' or 'N', but $jobvl was passed")) end vl = similar(A, $elty, ldvl, n) ldvr = 0 if jobvr == 'V' ldvr = n elseif jobvr == 'N' ldvr = 1 else throw(ArgumentError("jobvr must be 'V' or 'N', but $jobvr was passed")) end vr = similar(A, $elty, ldvr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 8n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev3), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Clong, Clong), jobvl, jobvr, n, A, lda, B, ldb, alpha, beta, vl, ldvl, vr, ldvr, work, lwork, rwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) end end alpha, beta, vl, vr end end end """ geevx!(balanc, jobvl, jobvr, sense, A) -> (A, w, VL, VR, ilo, ihi, scale, abnrm, rconde, rcondv) Finds the eigensystem of `A` with matrix balancing. If `jobvl = N`, the left eigenvectors of `A` aren't computed. If `jobvr = N`, the right eigenvectors of `A` aren't computed. If `jobvl = V` or `jobvr = V`, the corresponding eigenvectors are computed. If `balanc = N`, no balancing is performed. If `balanc = P`, `A` is permuted but not scaled. If `balanc = S`, `A` is scaled but not permuted. If `balanc = B`, `A` is permuted and scaled. If `sense = N`, no reciprocal condition numbers are computed. If `sense = E`, reciprocal condition numbers are computed for the eigenvalues only. If `sense = V`, reciprocal condition numbers are computed for the right eigenvectors only. If `sense = B`, reciprocal condition numbers are computed for the right eigenvectors and the eigenvectors. If `sense = E,B`, the right and left eigenvectors must be computed. """ geevx!(balanc::AbstractChar, jobvl::AbstractChar, jobvr::AbstractChar, sense::AbstractChar, A::AbstractMatrix) """ ggev!(jobvl, jobvr, A, B) -> (alpha, beta, vl, vr) Finds the generalized eigendecomposition of `A` and `B`. If `jobvl = N`, the left eigenvectors aren't computed. If `jobvr = N`, the right eigenvectors aren't computed. If `jobvl = V` or `jobvr = V`, the corresponding eigenvectors are computed. """ ggev!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) """ ggev3!(jobvl, jobvr, A, B) -> (alpha, beta, vl, vr) Finds the generalized eigendecomposition of `A` and `B` using a blocked algorithm. If `jobvl = N`, the left eigenvectors aren't computed. If `jobvr = N`, the right eigenvectors aren't computed. If `jobvl = V` or `jobvr = V`, the corresponding eigenvectors are computed. This function requires LAPACK 3.6.0. """ ggev3!(jobvl::AbstractChar, jobvr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) # One step incremental condition estimation of max/min singular values for (laic1, elty) in ((:dlaic1_,:Float64), (:slaic1_,:Float32)) @eval begin # SUBROUTINE DLAIC1( JOB, J, X, SEST, W, GAMMA, SESTPR, S, C ) # # .. Scalar Arguments .. # INTEGER J, JOB # DOUBLE PRECISION C, GAMMA, S, SEST, SESTPR # .. # .. Array Arguments .. # DOUBLE PRECISION W( J ), X( J ) function laic1!(job::Integer, x::AbstractVector{$elty}, sest::$elty, w::AbstractVector{$elty}, gamma::$elty) require_one_based_indexing(x, w) j = length(x) if j != length(w) throw(DimensionMismatch("vectors must have same length, but length of x is $j and length of w is $(length(w))")) end sestpr = Vector{$elty}(undef, 1) s = Vector{$elty}(undef, 1) c = Vector{$elty}(undef, 1) ccall((@blasfunc($laic1), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, Ptr{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}), job, j, x, sest, w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end end end for (laic1, elty, relty) in ((:zlaic1_,:ComplexF64,:Float64), (:claic1_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZLAIC1( JOB, J, X, SEST, W, GAMMA, SESTPR, S, C ) # # .. Scalar Arguments .. # INTEGER J, JOB # DOUBLE PRECISION SEST, SESTPR # COMPLEX*16 C, GAMMA, S # .. # .. Array Arguments .. # COMPLEX*16 W( J ), X( J ) function laic1!(job::Integer, x::AbstractVector{$elty}, sest::$relty, w::AbstractVector{$elty}, gamma::$elty) require_one_based_indexing(x, w) j = length(x) if j != length(w) throw(DimensionMismatch("vectors must have same length, but length of x is $j and length of w is $(length(w))")) end sestpr = Vector{$relty}(undef, 1) s = Vector{$elty}(undef, 1) c = Vector{$elty}(undef, 1) ccall((@blasfunc($laic1), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{$relty}, Ptr{$elty}, Ref{$elty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}), job, j, x, sest, w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end end end # (GT) General tridiagonal, decomposition, solver and direct solver for (gtsv, gttrf, gttrs, elty) in ((:dgtsv_,:dgttrf_,:dgttrs_,:Float64), (:sgtsv_,:sgttrf_,:sgttrs_,:Float32), (:zgtsv_,:zgttrf_,:zgttrs_,:ComplexF64), (:cgtsv_,:cgttrf_,:cgttrs_,:ComplexF32)) @eval begin # SUBROUTINE DGTSV( N, NRHS, DL, D, DU, B, LDB, INFO ) # .. Scalar Arguments .. # INTEGER INFO, LDB, N, NRHS # .. Array Arguments .. # DOUBLE PRECISION B( LDB, * ), D( * ), DL( * ), DU( * ) function gtsv!(dl::AbstractVector{$elty}, d::AbstractVector{$elty}, du::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(dl, d, du, B) chkstride1(B, dl, d, du) n = length(d) if !(n >= length(dl) >= n - 1) throw(DimensionMismatch("subdiagonal has length $(length(dl)), but should be $n or $(n - 1)")) end if !(n >= length(du) >= n - 1) throw(DimensionMismatch("superdiagonal has length $(length(du)), but should be $n or $(n - 1)")) end if n != size(B,1) throw(DimensionMismatch("B has leading dimension $(size(B,1)), but should have $n")) end if n == 0 return B # Early exit if possible end info = Ref{BlasInt}() ccall((@blasfunc($gtsv), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, size(B,2), dl, d, du, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end # SUBROUTINE DGTTRF( N, DL, D, DU, DU2, IPIV, INFO ) # .. Scalar Arguments .. # INTEGER INFO, N # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION D( * ), DL( * ), DU( * ), DU2( * ) function gttrf!(dl::AbstractVector{$elty}, d::AbstractVector{$elty}, du::AbstractVector{$elty}) require_one_based_indexing(dl, d, du) chkstride1(dl,d,du) n = length(d) if length(dl) != n - 1 throw(DimensionMismatch("subdiagonal has length $(length(dl)), but should be $(n - 1)")) end if length(du) != n - 1 throw(DimensionMismatch("superdiagonal has length $(length(du)), but should be $(n - 1)")) end du2 = similar(d, $elty, n-2) ipiv = similar(d, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gttrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), n, dl, d, du, du2, ipiv, info) chklapackerror(info[]) dl, d, du, du2, ipiv end # SUBROUTINE DGTTRS( TRANS, N, NRHS, DL, D, DU, DU2, IPIV, B, LDB, INFO ) # .. Scalar Arguments .. # CHARACTER TRANS # INTEGER INFO, LDB, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION B( LDB, * ), D( * ), DL( * ), DU( * ), DU2( * ) function gttrs!(trans::AbstractChar, dl::AbstractVector{$elty}, d::AbstractVector{$elty}, du::AbstractVector{$elty}, du2::AbstractVector{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(dl, d, du, du2, ipiv, B) chktrans(trans) chkstride1(B, ipiv, dl, d, du, du2) n = length(d) if length(dl) != n - 1 throw(DimensionMismatch("subdiagonal has length $(length(dl)), but should be $(n - 1)")) end if length(du) != n - 1 throw(DimensionMismatch("superdiagonal has length $(length(du)), but should be $(n - 1)")) end if n != size(B,1) throw(DimensionMismatch("B has leading dimension $(size(B,1)), but should have $n")) end info = Ref{BlasInt}() ccall((@blasfunc($gttrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), trans, n, size(B,2), dl, d, du, du2, ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end """ gtsv!(dl, d, du, B) Solves the equation `A * X = B` where `A` is a tridiagonal matrix with `dl` on the subdiagonal, `d` on the diagonal, and `du` on the superdiagonal. Overwrites `B` with the solution `X` and returns it. """ gtsv!(dl::AbstractVector, d::AbstractVector, du::AbstractVector, B::AbstractVecOrMat) """ gttrf!(dl, d, du) -> (dl, d, du, du2, ipiv) Finds the `LU` factorization of a tridiagonal matrix with `dl` on the subdiagonal, `d` on the diagonal, and `du` on the superdiagonal. Modifies `dl`, `d`, and `du` in-place and returns them and the second superdiagonal `du2` and the pivoting vector `ipiv`. """ gttrf!(dl::AbstractVector, d::AbstractVector, du::AbstractVector) """ gttrs!(trans, dl, d, du, du2, ipiv, B) Solves the equation `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), or `adjoint(A) * X = B` (`trans = C`) using the `LU` factorization computed by `gttrf!`. `B` is overwritten with the solution `X`. """ gttrs!(trans::AbstractChar, dl::AbstractVector, d::AbstractVector, du::AbstractVector, du2::AbstractVector, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) ## (OR) orthogonal (or UN, unitary) matrices, extractors and multiplication for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in ((:dorglq_,:dorgqr_,:dorgql_,:dorgrq_,:dormlq_,:dormqr_,:dormql_,:dormrq_,:dgemqrt_,:Float64), (:sorglq_,:sorgqr_,:sorgql_,:sorgrq_,:sormlq_,:sormqr_,:sormql_,:sormrq_,:sgemqrt_,:Float32), (:zunglq_,:zungqr_,:zungql_,:zungrq_,:zunmlq_,:zunmqr_,:zunmql_,:zunmrq_,:zgemqrt_,:ComplexF64), (:cunglq_,:cungqr_,:cungql_,:cungrq_,:cunmlq_,:cunmqr_,:cunmql_,:cunmrq_,:cgemqrt_,:ComplexF32)) @eval begin # SUBROUTINE DORGLQ( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, K, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orglq!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) require_one_based_indexing(A, tau) chkstride1(A,tau) n = size(A, 2) m = min(n, size(A, 1)) if k > m throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= m = $m")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orglq), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end if m < size(A,1) A[1:m,:] else A end end # SUBROUTINE DORGQR( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, K, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orgqr!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) require_one_based_indexing(A, tau) chkstride1(A,tau) m = size(A, 1) n = min(m, size(A, 2)) if k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgqr), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end if n < size(A,2) A[:,1:n] else A end end # SUBROUTINE DORGQL( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, K, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orgql!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) require_one_based_indexing(A, tau) chkstride1(A,tau) m = size(A, 1) n = min(m, size(A, 2)) if k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgql), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end if n < size(A,2) A[:,1:n] else A end end # SUBROUTINE DORGRQ( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # INTEGER INFO, K, LDA, LWORK, M, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orgrq!(A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, k::Integer = length(tau)) require_one_based_indexing(A, tau) chkstride1(A,tau) m, n = size(A) if n < m throw(DimensionMismatch("input matrix A has dimensions ($m,$n), but cannot have fewer columns than rows")) end if k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgrq), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), m, n, k, A, max(1,stride(A,2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A end # SUBROUTINE DORMLQ( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, # WORK, LWORK, INFO ) # .. Scalar Arguments .. # CHARACTER SIDE, TRANS # INTEGER INFO, K, LDA, LDC, LWORK, M, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) function ormlq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chktrans(trans) chkside(side) chkstride1(A, C, tau) m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) nA = size(A, 2) k = length(tau) if side == 'L' && m != nA throw(DimensionMismatch("for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $nA")) end if side == 'R' && n != nA throw(DimensionMismatch("for a right-sided multiplication, the second dimension of C, $n, must equal the second dimension of A, $nA")) end if side == 'L' && k > m throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= m = $m")) end if side == 'R' && k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormlq), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1,stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end # SUBROUTINE DORMQR( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, # WORK, INFO ) # .. Scalar Arguments .. # CHARACTER SIDE, TRANS # INTEGER INFO, K, LDA, LDC, M, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) function ormqr!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chktrans(trans) chkside(side) chkstride1(A, C, tau) m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) mA = size(A, 1) k = length(tau) if side == 'L' && m != mA throw(DimensionMismatch("for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $mA")) end if side == 'R' && n != mA throw(DimensionMismatch("for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $mA")) end if side == 'L' && k > m throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= m = $m")) end if side == 'R' && k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormqr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1, stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end # SUBROUTINE DORMQL( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, # WORK, INFO ) # .. Scalar Arguments .. # CHARACTER SIDE, TRANS # INTEGER INFO, K, LDA, LDC, M, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) function ormql!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chktrans(trans) chkside(side) chkstride1(A, C, tau) m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) mA = size(A, 1) k = length(tau) if side == 'L' && m != mA throw(DimensionMismatch("for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $mA")) end if side == 'R' && n != mA throw(DimensionMismatch("for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $mA")) end if side == 'L' && k > m throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= m = $m")) end if side == 'R' && k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormql), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1, stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end # SUBROUTINE DORMRQ( SIDE, TRANS, M, N, K, A, LDA, TAU, C, LDC, # WORK, LWORK, INFO ) # .. Scalar Arguments .. # CHARACTER SIDE, TRANS # INTEGER INFO, K, LDA, LDC, LWORK, M, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), C( LDC, * ), TAU( * ), WORK( * ) function ormrq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chktrans(trans) chkside(side) chkstride1(A, C, tau) m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) nA = size(A, 2) k = length(tau) if side == 'L' && m != nA throw(DimensionMismatch("for a left-sided multiplication, the first dimension of C, $m, must equal the second dimension of A, $nA")) end if side == 'R' && n != nA throw(DimensionMismatch("for a right-sided multiplication, the second dimension of C, $m, must equal the second dimension of A, $nA")) end if side == 'L' && k > m throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= m = $m")) end if side == 'R' && k > n throw(DimensionMismatch("invalid number of reflectors: k = $k should be <= n = $n")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrq), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, A, max(1,stride(A,2)), tau, C, max(1,stride(C,2)), work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end function gemqrt!(side::AbstractChar, trans::AbstractChar, V::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(V, T, C) chktrans(trans) chkside(side) chkstride1(V, T, C) m,n = ndims(C) == 2 ? size(C) : (size(C, 1), 1) nb, k = size(T) if k == 0 return C end if side == 'L' if !(0 <= k <= m) throw(DimensionMismatch("wrong value for k = $k: must be between 0 and $m")) end if m != size(V,1) throw(DimensionMismatch("first dimensions of C, $m, and V, $(size(V,1)) must match")) end ldv = stride(V,2) if ldv < max(1, m) throw(DimensionMismatch("Q and C don't fit! The stride of V, $ldv, is too small")) end wss = n*k elseif side == 'R' if !(0 <= k <= n) throw(DimensionMismatch("wrong value for k = $k: must be between 0 and $n")) end if n != size(V,1) throw(DimensionMismatch("second dimension of C, $n, and first dimension of V, $(size(V,1)) must match")) end ldv = stride(V,2) if ldv < max(1, n) throw(DimensionMismatch("Q and C don't fit! The stride of V, $ldv, is too small")) end wss = m*k end if !(1 <= nb <= k) throw(DimensionMismatch("wrong value for nb = $nb, which must be between 1 and $k")) end ldc = stride(C, 2) work = Vector{$elty}(undef, wss) info = Ref{BlasInt}() ccall((@blasfunc($gemqrt), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), side, trans, m, n, k, nb, V, ldv, T, max(1,stride(T,2)), C, max(1,ldc), work, info, 1, 1) chklapackerror(info[]) return C end end end """ orglq!(A, tau, k = length(tau)) Explicitly finds the matrix `Q` of a `LQ` factorization after calling `gelqf!` on `A`. Uses the output of `gelqf!`. `A` is overwritten by `Q`. """ orglq!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) """ orgqr!(A, tau, k = length(tau)) Explicitly finds the matrix `Q` of a `QR` factorization after calling `geqrf!` on `A`. Uses the output of `geqrf!`. `A` is overwritten by `Q`. """ orgqr!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) """ orgql!(A, tau, k = length(tau)) Explicitly finds the matrix `Q` of a `QL` factorization after calling `geqlf!` on `A`. Uses the output of `geqlf!`. `A` is overwritten by `Q`. """ orgql!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) """ orgrq!(A, tau, k = length(tau)) Explicitly finds the matrix `Q` of a `RQ` factorization after calling `gerqf!` on `A`. Uses the output of `gerqf!`. `A` is overwritten by `Q`. """ orgrq!(A::AbstractMatrix, tau::AbstractVector, k::Integer = length(tau)) """ ormlq!(side, trans, A, tau, C) Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` (`trans = C`) for `side = L` or the equivalent right-sided multiplication for `side = R` using `Q` from a `LQ` factorization of `A` computed using `gelqf!`. `C` is overwritten. """ ormlq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) """ ormqr!(side, trans, A, tau, C) Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` (`trans = C`) for `side = L` or the equivalent right-sided multiplication for `side = R` using `Q` from a `QR` factorization of `A` computed using `geqrf!`. `C` is overwritten. """ ormqr!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) """ ormql!(side, trans, A, tau, C) Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` (`trans = C`) for `side = L` or the equivalent right-sided multiplication for `side = R` using `Q` from a `QL` factorization of `A` computed using `geqlf!`. `C` is overwritten. """ ormql!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) """ ormrq!(side, trans, A, tau, C) Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` (`trans = C`) for `side = L` or the equivalent right-sided multiplication for `side = R` using `Q` from a `RQ` factorization of `A` computed using `gerqf!`. `C` is overwritten. """ ormrq!(side::AbstractChar, trans::AbstractChar, A::AbstractMatrix, tau::AbstractVector, C::AbstractVecOrMat) """ gemqrt!(side, trans, V, T, C) Computes `Q * C` (`trans = N`), `transpose(Q) * C` (`trans = T`), `adjoint(Q) * C` (`trans = C`) for `side = L` or the equivalent right-sided multiplication for `side = R` using `Q` from a `QR` factorization of `A` computed using `geqrt!`. `C` is overwritten. """ gemqrt!(side::AbstractChar, trans::AbstractChar, V::AbstractMatrix, T::AbstractMatrix, C::AbstractVecOrMat) # (PO) positive-definite symmetric matrices, for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in ((:dposv_,:dpotrf_,:dpotri_,:dpotrs_,:dpstrf_,:Float64,:Float64), (:sposv_,:spotrf_,:spotri_,:spotrs_,:spstrf_,:Float32,:Float32), (:zposv_,:zpotrf_,:zpotri_,:zpotrs_,:zpstrf_,:ComplexF64,:Float64), (:cposv_,:cpotrf_,:cpotri_,:cpotrs_,:cpstrf_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE DPOSV( UPLO, N, NRHS, A, LDA, B, LDB, INFO ) #* .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function posv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) n = checksquare(A) chkuplo(uplo) if size(B,1) != n throw(DimensionMismatch("first dimension of B, $(size(B,1)), and size of A, ($n,$n), must match!")) end info = Ref{BlasInt}() ccall((@blasfunc($posv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info, 1) chkargsok(info[]) chkposdef(info[]) A, B end # SUBROUTINE DPOTRF( UPLO, N, A, LDA, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ) function potrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) checksquare(A) chkuplo(uplo) lda = max(1,stride(A,2)) if lda == 0 return A, 0 end info = Ref{BlasInt}() ccall((@blasfunc($potrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, size(A,1), A, lda, info, 1) chkargsok(info[]) #info[] > 0 means the leading minor of order info[] is not positive definite #ordinarily, throw Exception here, but return error code here #this simplifies isposdef! and factorize return A, info[] # info stored in Cholesky end # SUBROUTINE DPOTRI( UPLO, N, A, LDA, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ) function potri!(uplo::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) chkuplo(uplo) info = Ref{BlasInt}() ccall((@blasfunc($potri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, size(A,1), A, max(1,stride(A,2)), info, 1) chkargsok(info[]) chknonsingular(info[]) A end # SUBROUTINE DPOTRS( UPLO, N, NRHS, A, LDA, B, LDB, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function potrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A, B) n = checksquare(A) chkuplo(uplo) nrhs = size(B,2) if size(B,1) != n throw(DimensionMismatch("first dimension of B, $(size(B,1)), and size of A, ($n,$n), must match!")) end lda = max(1,stride(A,2)) if lda == 0 || nrhs == 0 return B end ldb = max(1,stride(B,2)) info = Ref{BlasInt}() ccall((@blasfunc($potrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, nrhs, A, lda, B, ldb, info, 1) chklapackerror(info[]) return B end # SUBROUTINE DPSTRF( UPLO, N, A, LDA, PIV, RANK, TOL, WORK, INFO ) # .. Scalar Arguments .. # DOUBLE PRECISION TOL # INTEGER INFO, LDA, N, RANK # CHARACTER UPLO # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), WORK( 2*N ) # INTEGER PIV( N ) function pstrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}, tol::Real) chkstride1(A) n = checksquare(A) chkuplo(uplo) piv = similar(A, BlasInt, n) rank = Vector{BlasInt}(undef, 1) work = Vector{$rtyp}(undef, 2n) info = Ref{BlasInt}() ccall((@blasfunc($pstrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), piv, rank, tol, work, info, 1) chkargsok(info[]) A, piv, rank[1], info[] #Stored in CholeskyPivoted end end end """ posv!(uplo, A, B) -> (A, B) Finds the solution to `A * X = B` where `A` is a symmetric or Hermitian positive definite matrix. If `uplo = U` the upper Cholesky decomposition of `A` is computed. If `uplo = L` the lower Cholesky decomposition of `A` is computed. `A` is overwritten by its Cholesky decomposition. `B` is overwritten with the solution `X`. """ posv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) """ potrf!(uplo, A) Computes the Cholesky (upper if `uplo = U`, lower if `uplo = L`) decomposition of positive-definite matrix `A`. `A` is overwritten and returned with an info code. """ potrf!(uplo::AbstractChar, A::AbstractMatrix) """ potri!(uplo, A) Computes the inverse of positive-definite matrix `A` after calling `potrf!` to find its (upper if `uplo = U`, lower if `uplo = L`) Cholesky decomposition. `A` is overwritten by its inverse and returned. """ potri!(uplo::AbstractChar, A::AbstractMatrix) """ potrs!(uplo, A, B) Finds the solution to `A * X = B` where `A` is a symmetric or Hermitian positive definite matrix whose Cholesky decomposition was computed by `potrf!`. If `uplo = U` the upper Cholesky decomposition of `A` was computed. If `uplo = L` the lower Cholesky decomposition of `A` was computed. `B` is overwritten with the solution `X`. """ potrs!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) """ pstrf!(uplo, A, tol) -> (A, piv, rank, info) Computes the (upper if `uplo = U`, lower if `uplo = L`) pivoted Cholesky decomposition of positive-definite matrix `A` with a user-set tolerance `tol`. `A` is overwritten by its Cholesky decomposition. Returns `A`, the pivots `piv`, the rank of `A`, and an `info` code. If `info = 0`, the factorization succeeded. If `info = i > 0 `, then `A` is indefinite or rank-deficient. """ pstrf!(uplo::AbstractChar, A::AbstractMatrix, tol::Real) # (PT) positive-definite, symmetric, tri-diagonal matrices # Direct solvers for general tridiagonal and symmetric positive-definite tridiagonal for (ptsv, pttrf, elty, relty) in ((:dptsv_,:dpttrf_,:Float64,:Float64), (:sptsv_,:spttrf_,:Float32,:Float32), (:zptsv_,:zpttrf_,:ComplexF64,:Float64), (:cptsv_,:cpttrf_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE DPTSV( N, NRHS, D, E, B, LDB, INFO ) # .. Scalar Arguments .. # INTEGER INFO, LDB, N, NRHS # .. Array Arguments .. # DOUBLE PRECISION B( LDB, * ), D( * ), E( * ) function ptsv!(D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(D, E, B) chkstride1(B, D, E) n = length(D) if length(E) != n - 1 throw(DimensionMismatch("E has length $(length(E)), but needs $(n - 1)")) end if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)) but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($ptsv), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end # SUBROUTINE DPTTRF( N, D, E, INFO ) # .. Scalar Arguments .. # INTEGER INFO, N # .. Array Arguments .. # DOUBLE PRECISION D( * ), E( * ) function pttrf!(D::AbstractVector{$relty}, E::AbstractVector{$elty}) require_one_based_indexing(D, E) chkstride1(D, E) n = length(D) if length(E) != n - 1 throw(DimensionMismatch("E has length $(length(E)), but needs $(n - 1)")) end info = Ref{BlasInt}() ccall((@blasfunc($pttrf), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), n, D, E, info) chklapackerror(info[]) D, E end end end """ ptsv!(D, E, B) Solves `A * X = B` for positive-definite tridiagonal `A`. `D` is the diagonal of `A` and `E` is the off-diagonal. `B` is overwritten with the solution `X` and returned. """ ptsv!(D::AbstractVector, E::AbstractVector, B::AbstractVecOrMat) """ pttrf!(D, E) Computes the LDLt factorization of a positive-definite tridiagonal matrix with `D` as diagonal and `E` as off-diagonal. `D` and `E` are overwritten and returned. """ pttrf!(D::AbstractVector, E::AbstractVector) for (pttrs, elty, relty) in ((:dpttrs_,:Float64,:Float64), (:spttrs_,:Float32,:Float32)) @eval begin # SUBROUTINE DPTTRS( N, NRHS, D, E, B, LDB, INFO ) # .. Scalar Arguments .. # INTEGER INFO, LDB, N, NRHS # .. Array Arguments .. # DOUBLE PRECISION B( LDB, * ), D( * ), E( * ) function pttrs!(D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(D, E, B) chkstride1(B, D, E) n = length(D) if length(E) != n - 1 throw(DimensionMismatch("E has length $(length(E)), but needs $(n - 1)")) end if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)) but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end end end for (pttrs, elty, relty) in ((:zpttrs_,:ComplexF64,:Float64), (:cpttrs_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZPTTRS( UPLO, N, NRHS, D, E, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDB, N, NRHS # * .. # * .. Array Arguments .. # DOUBLE PRECISION D( * ) # COMPLEX*16 B( LDB, * ), E( * ) function pttrs!(uplo::AbstractChar, D::AbstractVector{$relty}, E::AbstractVector{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(D, E, B) chkstride1(B, D, E) chkuplo(uplo) n = length(D) if length(E) != n - 1 throw(DimensionMismatch("E has length $(length(E)), but needs $(n - 1)")) end if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)) but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), D, E, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end """ pttrs!(D, E, B) Solves `A * X = B` for positive-definite tridiagonal `A` with diagonal `D` and off-diagonal `E` after computing `A`'s LDLt factorization using `pttrf!`. `B` is overwritten with the solution `X`. """ pttrs!(D::AbstractVector, E::AbstractVector, B::AbstractVecOrMat) ## (TR) triangular matrices: solver and inverse for (trtri, trtrs, elty) in ((:dtrtri_,:dtrtrs_,:Float64), (:strtri_,:strtrs_,:Float32), (:ztrtri_,:ztrtrs_,:ComplexF64), (:ctrtri_,:ctrtrs_,:ComplexF32)) @eval begin # SUBROUTINE DTRTRI( UPLO, DIAG, N, A, LDA, INFO ) #* .. Scalar Arguments .. # CHARACTER DIAG, UPLO # INTEGER INFO, LDA, N # .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ) function trtri!(uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) chkdiag(diag) lda = max(1,stride(A, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trtri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), uplo, diag, n, A, lda, info, 1, 1) chklapackerror(info[]) A end # SUBROUTINE DTRTRS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER DIAG, TRANS, UPLO # INTEGER INFO, LDA, LDB, N, NRHS # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function trtrs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chktrans(trans) chkdiag(diag) chkstride1(A) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)) but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($trtrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info, 1, 1, 1) chklapackerror(info[]) B end end end """ trtri!(uplo, diag, A) Finds the inverse of (upper if `uplo = U`, lower if `uplo = L`) triangular matrix `A`. If `diag = N`, `A` has non-unit diagonal elements. If `diag = U`, all diagonal elements of `A` are one. `A` is overwritten with its inverse. """ trtri!(uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix) """ trtrs!(uplo, trans, diag, A, B) Solves `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), or `adjoint(A) * X = B` (`trans = C`) for (upper if `uplo = U`, lower if `uplo = L`) triangular matrix `A`. If `diag = N`, `A` has non-unit diagonal elements. If `diag = U`, all diagonal elements of `A` are one. `B` is overwritten with the solution `X`. """ trtrs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) #Eigenvector computation and condition number estimation for (trcon, trevc, trrfs, elty) in ((:dtrcon_,:dtrevc_,:dtrrfs_,:Float64), (:strcon_,:strevc_,:strrfs_,:Float32)) @eval begin # SUBROUTINE DTRCON( NORM, UPLO, DIAG, N, A, LDA, RCOND, WORK, # IWORK, INFO ) # .. Scalar Arguments .. # CHARACTER DIAG, NORM, UPLO # INTEGER INFO, LDA, N # DOUBLE PRECISION RCOND # .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) chkdiag(diag) n = checksquare(A) chkuplo(uplo) rcond = Ref{$elty}() work = Vector{$elty}(undef, 3n) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), norm, uplo, diag, n, A, max(1,stride(A,2)), rcond, work, iwork, info, 1, 1, 1) chklapackerror(info[]) rcond[] end # SUBROUTINE DTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, # LDVR, MM, M, WORK, INFO ) # # .. Scalar Arguments .. # CHARACTER HOWMNY, SIDE # INTEGER INFO, LDT, LDVL, LDVR, M, MM, N # .. # .. Array Arguments .. # LOGICAL SELECT( * ) # DOUBLE PRECISION T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), #$ WORK( * ) function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty} = similar(T), VR::AbstractMatrix{$elty} = similar(T)) require_one_based_indexing(select, T, VL, VR) # Extract if side โˆ‰ ['L','R','B'] throw(ArgumentError("side argument must be 'L' (left eigenvectors), 'R' (right eigenvectors), or 'B' (both), got $side")) end n, mm = checksquare(T), size(VL, 2) ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) # Check chkstride1(T, select, VL, VR) # Allocate m = Ref{BlasInt}() work = Vector{$elty}(undef, 3n) info = Ref{BlasInt}() ccall((@blasfunc($trevc), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), side, howmny, select, n, T, ldt, VL, ldvl, VR, ldvr, mm, m, work, info, 1, 1) chklapackerror(info[]) #Decide what exactly to return if howmny == 'S' #compute selected eigenvectors if side == 'L' #left eigenvectors only return select, VL[:,1:m[]] elseif side == 'R' #right eigenvectors only return select, VR[:,1:m[]] else #side == 'B' #both eigenvectors return select, VL[:,1:m[]], VR[:,1:m[]] end else #compute all eigenvectors if side == 'L' #left eigenvectors only return VL[:,1:m[]] elseif side == 'R' #right eigenvectors only return VR[:,1:m[]] else #side == 'B' #both eigenvectors return VL[:,1:m[]], VR[:,1:m[]] end end end # SUBROUTINE DTRRFS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, X, # LDX, FERR, BERR, WORK, IWORK, INFO ) # .. Scalar Arguments .. # CHARACTER DIAG, TRANS, UPLO # INTEGER INFO, LDA, LDB, LDX, N, NRHS # .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), BERR( * ), FERR( * ), #$ WORK( * ), X( LDX, * ) function trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, X::AbstractVecOrMat{$elty}, Ferr::AbstractVector{$elty} = similar(B, $elty, size(B,2)), Berr::AbstractVector{$elty} = similar(B, $elty, size(B,2))) require_one_based_indexing(A, B, X, Ferr, Berr) chkstride1(A, B, X, Ferr, Berr) chktrans(trans) chkuplo(uplo) chkdiag(diag) n = size(A,2) nrhs = size(B,2) if nrhs != size(X,2) throw(DimensionMismatch("second dimensions of B, $nrhs, and X, $(size(X,2)), must match")) end work = Vector{$elty}(undef, 3n) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, iwork, info, 1, 1, 1) chklapackerror(info[]) Ferr, Berr end end end for (trcon, trevc, trrfs, elty, relty) in ((:ztrcon_,:ztrevc_,:ztrrfs_,:ComplexF64,:Float64), (:ctrcon_,:ctrevc_,:ctrrfs_,:ComplexF32, :Float32)) @eval begin # SUBROUTINE ZTRCON( NORM, UPLO, DIAG, N, A, LDA, RCOND, WORK, # RWORK, INFO ) # .. Scalar Arguments .. # CHARACTER DIAG, NORM, UPLO # INTEGER INFO, LDA, N # DOUBLE PRECISION RCOND # .. Array Arguments .. # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) chkdiag(diag) rcond = Ref{$relty}(1) work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong), norm, uplo, diag, n, A, max(1,stride(A,2)), rcond, work, rwork, info, 1, 1, 1) chklapackerror(info[]) rcond[] end # SUBROUTINE ZTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, # LDVR, MM, M, WORK, RWORK, INFO ) # # .. Scalar Arguments .. # CHARACTER HOWMNY, SIDE # INTEGER INFO, LDT, LDVL, LDVR, M, MM, N # .. # .. Array Arguments .. # LOGICAL SELECT( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), #$ WORK( * ) function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty} = similar(T), VR::AbstractMatrix{$elty} = similar(T)) require_one_based_indexing(select, T, VL, VR) # Extract n, mm = checksquare(T), size(VL, 2) ldt, ldvl, ldvr = stride(T, 2), stride(VL, 2), stride(VR, 2) # Check chkstride1(T, select, VL, VR) if side โˆ‰ ['L','R','B'] throw(ArgumentError("side argument must be 'L' (left eigenvectors), 'R' (right eigenvectors), or 'B' (both), got $side")) end # Allocate m = Ref{BlasInt}() work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trevc), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), side, howmny, select, n, T, ldt, VL, ldvl, VR, ldvr, mm, m, work, rwork, info, 1, 1) chklapackerror(info[]) #Decide what exactly to return if howmny == 'S' #compute selected eigenvectors if side == 'L' #left eigenvectors only return select, VL[:,1:m[]] elseif side == 'R' #right eigenvectors only return select, VR[:,1:m[]] else #side=='B' #both eigenvectors return select, VL[:,1:m[]], VR[:,1:m[]] end else #compute all eigenvectors if side == 'L' #left eigenvectors only return VL[:,1:m[]] elseif side == 'R' #right eigenvectors only return VR[:,1:m[]] else #side=='B' #both eigenvectors return VL[:,1:m[]], VR[:,1:m[]] end end end # SUBROUTINE ZTRRFS( UPLO, TRANS, DIAG, N, NRHS, A, LDA, B, LDB, X, # LDX, FERR, BERR, WORK, IWORK, INFO ) # .. Scalar Arguments .. # CHARACTER DIAG, TRANS, UPLO # INTEGER INFO, LDA, LDB, LDX, N, NRHS # .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), BERR( * ), FERR( * ), #$ WORK( * ), X( LDX, * ) function trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}, X::AbstractVecOrMat{$elty}, Ferr::AbstractVector{$relty} = similar(B, $relty, size(B,2)), Berr::AbstractVector{$relty} = similar(B, $relty, size(B,2))) require_one_based_indexing(A, B, X, Ferr, Berr) chkstride1(A, B, X, Ferr, Berr) chktrans(trans) chkuplo(uplo) chkdiag(diag) n = size(A,2) nrhs = size(B,2) if nrhs != size(X,2) throw(DimensionMismatch("second dimensions of B, $nrhs, and X, $(size(X,2)), must match")) end work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong, Clong), uplo, trans, diag, n, nrhs, A, max(1,stride(A,2)), B, max(1,stride(B,2)), X, max(1,stride(X,2)), Ferr, Berr, work, rwork, info, 1, 1, 1) chklapackerror(info[]) Ferr, Berr end end end """ trcon!(norm, uplo, diag, A) Finds the reciprocal condition number of (upper if `uplo = U`, lower if `uplo = L`) triangular matrix `A`. If `diag = N`, `A` has non-unit diagonal elements. If `diag = U`, all diagonal elements of `A` are one. If `norm = I`, the condition number is found in the infinity norm. If `norm = O` or `1`, the condition number is found in the one norm. """ trcon!(norm::AbstractChar, uplo::AbstractChar, diag::AbstractChar, A::AbstractMatrix) """ trevc!(side, howmny, select, T, VL = similar(T), VR = similar(T)) Finds the eigensystem of an upper triangular matrix `T`. If `side = R`, the right eigenvectors are computed. If `side = L`, the left eigenvectors are computed. If `side = B`, both sets are computed. If `howmny = A`, all eigenvectors are found. If `howmny = B`, all eigenvectors are found and backtransformed using `VL` and `VR`. If `howmny = S`, only the eigenvectors corresponding to the values in `select` are computed. """ trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix, VL::AbstractMatrix = similar(T), VR::AbstractMatrix = similar(T)) """ trrfs!(uplo, trans, diag, A, B, X, Ferr, Berr) -> (Ferr, Berr) Estimates the error in the solution to `A * X = B` (`trans = N`), `transpose(A) * X = B` (`trans = T`), `adjoint(A) * X = B` (`trans = C`) for `side = L`, or the equivalent equations a right-handed `side = R` `X * A` after computing `X` using `trtrs!`. If `uplo = U`, `A` is upper triangular. If `uplo = L`, `A` is lower triangular. If `diag = N`, `A` has non-unit diagonal elements. If `diag = U`, all diagonal elements of `A` are one. `Ferr` and `Berr` are optional inputs. `Ferr` is the forward error and `Berr` is the backward error, each component-wise. """ trrfs!(uplo::AbstractChar, trans::AbstractChar, diag::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat, X::AbstractVecOrMat, Ferr::AbstractVector, Berr::AbstractVector) ## (ST) Symmetric tridiagonal - eigendecomposition for (stev, stebz, stegr, stein, elty) in ((:dstev_,:dstebz_,:dstegr_,:dstein_,:Float64), (:sstev_,:sstebz_,:sstegr_,:sstein_,:Float32) # , (:zstev_,:ComplexF64) Need to rewrite for ZHEEV, rwork, etc. # , (:cstev_,:ComplexF32) ) @eval begin function stev!(job::AbstractChar, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}) require_one_based_indexing(dv, ev) chkstride1(dv, ev) n = length(dv) if length(ev) != n - 1 && length(ev) != n throw(DimensionMismatch("ev has length $(length(ev)) but needs one less than or equal to dv's length, $n)")) end Zmat = similar(dv, $elty, (n, job != 'N' ? n : 0)) work = Vector{$elty}(undef, max(1, 2n-2)) info = Ref{BlasInt}() ccall((@blasfunc($stev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), job, n, dv, ev, Zmat, n, work, info, 1) chklapackerror(info[]) dv, Zmat end #* DSTEBZ computes the eigenvalues of a symmetric tridiagonal #* matrix T. The user may ask for all eigenvalues, all eigenvalues #* in the half-open interval (VL, VU], or the IL-th through IU-th #* eigenvalues. function stebz!(range::AbstractChar, order::AbstractChar, vl::$elty, vu::$elty, il::Integer, iu::Integer, abstol::Real, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}) require_one_based_indexing(dv, ev) chkstride1(dv, ev) n = length(dv) if length(ev) != n - 1 throw(DimensionMismatch("ev has length $(length(ev)) but needs one less than dv's length, $n)")) end m = Ref{BlasInt}() nsplit = Vector{BlasInt}(undef, 1) w = similar(dv, $elty, n) tmp = 0.0 iblock = similar(dv, BlasInt,n) isplit = similar(dv, BlasInt,n) work = Vector{$elty}(undef, 4*n) iwork = Vector{BlasInt}(undef, 3*n) info = Ref{BlasInt}() ccall((@blasfunc($stebz), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), range, order, n, vl, vu, il, iu, abstol, dv, ev, m, nsplit, w, iblock, isplit, work, iwork, info, 1, 1) chklapackerror(info[]) w[1:m[]], iblock[1:m[]], isplit[1:nsplit[1]] end function stegr!(jobz::AbstractChar, range::AbstractChar, dv::AbstractVector{$elty}, ev::AbstractVector{$elty}, vl::Real, vu::Real, il::Integer, iu::Integer) require_one_based_indexing(dv, ev) chkstride1(dv, ev) n = length(dv) ne = length(ev) if ne == n - 1 eev = [ev; zero($elty)] elseif ne == n eev = copy(ev) eev[n] = zero($elty) else throw(DimensionMismatch("ev has length $ne but needs one less than or equal to dv's length, $n)")) end abstol = Vector{$elty}(undef, 1) m = Ref{BlasInt}() w = similar(dv, $elty, n) ldz = jobz == 'N' ? 1 : n Z = similar(dv, $elty, ldz, range == 'I' ? iu-il+1 : n) isuppz = similar(dv, BlasInt, 2*size(Z, 2)) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($stegr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobz, range, n, dv, eev, vl, vu, il, iu, abstol, m, w, Z, ldz, isuppz, work, lwork, iwork, liwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) liwork = iwork[1] resize!(iwork, liwork) end end m[] == length(w) ? w : w[1:m[]], m[] == size(Z, 2) ? Z : Z[:,1:m[]] end function stein!(dv::AbstractVector{$elty}, ev_in::AbstractVector{$elty}, w_in::AbstractVector{$elty}, iblock_in::AbstractVector{BlasInt}, isplit_in::AbstractVector{BlasInt}) require_one_based_indexing(dv, ev_in, w_in, iblock_in, isplit_in) chkstride1(dv, ev_in, w_in, iblock_in, isplit_in) n = length(dv) ne = length(ev_in) if ne == n - 1 ev = [ev_in; zero($elty)] elseif ne == n ev = copy(ev_in) ev[n] = zero($elty) else throw(DimensionMismatch("ev_in has length $ne but needs one less than or equal to dv's length, $n)")) end ldz = n #Leading dimension #Number of eigenvalues to find if !(1 <= length(w_in) <= n) throw(DimensionMismatch("w_in has length $(length(w_in)), but needs to be between 1 and $n")) end m = length(w_in) #If iblock and isplit are invalid input, assume worst-case block partitioning, # i.e. set the block scheme to be the entire matrix iblock = similar(dv, BlasInt,n) isplit = similar(dv, BlasInt,n) w = similar(dv, $elty,n) if length(iblock_in) < m #Not enough block specifications iblock[1:m] = fill(BlasInt(1), m) w[1:m] = sort(w_in) else iblock[1:m] = iblock_in w[1:m] = w_in #Assume user has sorted the eigenvalues properly end if length(isplit_in) < 1 #Not enough block specifications isplit[1] = n else isplit[1:length(isplit_in)] = isplit_in end z = similar(dv, $elty,(n,m)) work = Vector{$elty}(undef, 5*n) iwork = Vector{BlasInt}(undef, n) ifail = Vector{BlasInt}(undef, m) info = Ref{BlasInt}() ccall((@blasfunc($stein), libblastrampoline), Cvoid, (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), n, dv, ev, m, w, iblock, isplit, z, ldz, work, iwork, ifail, info) chklapackerror(info[]) if any(ifail .!= 0) # TODO: better error message / type error("failed to converge eigenvectors:\n$(findall(!iszero, ifail))") end z end end end stegr!(jobz::AbstractChar, dv::AbstractVector, ev::AbstractVector) = stegr!(jobz, 'A', dv, ev, 0.0, 0.0, 0, 0) # Allow user to skip specification of iblock and isplit stein!(dv::AbstractVector, ev::AbstractVector, w_in::AbstractVector) = stein!(dv, ev, w_in, zeros(BlasInt,0), zeros(BlasInt,0)) # Allow user to specify just one eigenvector to get in stein! stein!(dv::AbstractVector, ev::AbstractVector, eval::Real) = stein!(dv, ev, [eval], zeros(BlasInt,0), zeros(BlasInt,0)) """ stev!(job, dv, ev) -> (dv, Zmat) Computes the eigensystem for a symmetric tridiagonal matrix with `dv` as diagonal and `ev` as off-diagonal. If `job = N` only the eigenvalues are found and returned in `dv`. If `job = V` then the eigenvectors are also found and returned in `Zmat`. """ stev!(job::AbstractChar, dv::AbstractVector, ev::AbstractVector) """ stebz!(range, order, vl, vu, il, iu, abstol, dv, ev) -> (dv, iblock, isplit) Computes the eigenvalues for a symmetric tridiagonal matrix with `dv` as diagonal and `ev` as off-diagonal. If `range = A`, all the eigenvalues are found. If `range = V`, the eigenvalues in the half-open interval `(vl, vu]` are found. If `range = I`, the eigenvalues with indices between `il` and `iu` are found. If `order = B`, eigvalues are ordered within a block. If `order = E`, they are ordered across all the blocks. `abstol` can be set as a tolerance for convergence. """ stebz!(range::AbstractChar, order::AbstractChar, vl, vu, il::Integer, iu::Integer, abstol::Real, dv::AbstractVector, ev::AbstractVector) """ stegr!(jobz, range, dv, ev, vl, vu, il, iu) -> (w, Z) Computes the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) for a symmetric tridiagonal matrix with `dv` as diagonal and `ev` as off-diagonal. If `range = A`, all the eigenvalues are found. If `range = V`, the eigenvalues in the half-open interval `(vl, vu]` are found. If `range = I`, the eigenvalues with indices between `il` and `iu` are found. The eigenvalues are returned in `w` and the eigenvectors in `Z`. """ stegr!(jobz::AbstractChar, range::AbstractChar, dv::AbstractVector, ev::AbstractVector, vl::Real, vu::Real, il::Integer, iu::Integer) """ stein!(dv, ev_in, w_in, iblock_in, isplit_in) Computes the eigenvectors for a symmetric tridiagonal matrix with `dv` as diagonal and `ev_in` as off-diagonal. `w_in` specifies the input eigenvalues for which to find corresponding eigenvectors. `iblock_in` specifies the submatrices corresponding to the eigenvalues in `w_in`. `isplit_in` specifies the splitting points between the submatrix blocks. """ stein!(dv::AbstractVector, ev_in::AbstractVector, w_in::AbstractVector, iblock_in::AbstractVector{BlasInt}, isplit_in::AbstractVector{BlasInt}) ## (SY) symmetric real matrices - Bunch-Kaufman decomposition, ## solvers (direct and factored) and inverse. for (syconv, sysv, sytrf, sytri, sytrs, elty) in ((:dsyconv_,:dsysv_,:dsytrf_,:dsytri_,:dsytrs_,:Float64), (:ssyconv_,:ssysv_,:ssytrf_,:ssytri_,:ssytrs_,:Float32)) @eval begin # SUBROUTINE DSYCONV( UPLO, WAY, N, A, LDA, IPIV, WORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO, WAY # INTEGER INFO, LDA, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function syconv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) chklapackerror(info[]) A, work end # SUBROUTINE DSYSV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # LWORK, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) function sysv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) chknonsingular(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE DSYTRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) if n == 0 return A, ipiv, zero(BlasInt) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end return A, ipiv, info[] end # SUBROUTINE DSYTRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) # function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) # chkstride1(A) # n = checksquare(A) # chkuplo(uplo) # work = Vector{$elty}(undef, 1) # lwork = BlasInt(-1) # info = Ref{BlasInt}() # for i in 1:2 # ccall((@blasfunc($sytri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, # Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # @assertargsok # chknonsingular(info[]) # if lwork < 0 # lwork = BlasInt(real(work[1])) # work = Vector{$elty}(undef, lwork) # end # end # A # end # SUBROUTINE DSYTRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chkargsok(info[]) chknonsingular(info[]) A end # SUBROUTINE DSYTRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function sytrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end # Rook-pivoting variants of symmetric-matrix algorithms for (sysv, sytrf, sytri, sytrs, syconvf, elty) in ((:dsysv_rook_,:dsytrf_rook_,:dsytri_rook_,:dsytrs_rook_,:dsyconvf_rook_,:Float64), (:ssysv_rook_,:ssytrf_rook_,:ssytri_rook_,:ssytrs_rook_,:ssyconvf_rook_,:Float32)) @eval begin # SUBROUTINE DSYSV_ROOK(UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # LWORK, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), WORK( * ) function sysv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) chknonsingular(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE DSYTRF_ROOK(UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function sytrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) if n == 0 return A, ipiv, zero(BlasInt) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, stride(A,2), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end return A, ipiv, info[] end # SUBROUTINE DSYTRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function sytri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chkargsok(info[]) chknonsingular(info[]) A end # SUBROUTINE DSYTRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ) function sytrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end # SUBROUTINE DSYCONVF_ROOK( UPLO, WAY, N, A, LDA, IPIV, E, INFO ) # # .. Scalar Arguments .. # CHARACTER UPLO, WAY # INTEGER INFO, LDA, N # .. # .. Array Arguments .. # INTEGER IPIV( * ) # DOUBLE PRECISION A( LDA, * ), E( * ) function syconvf_rook!(uplo::AbstractChar, way::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, e::AbstractVector{$elty} = Vector{$elty}(undef, length(ipiv))) require_one_based_indexing(A, ipiv, e) # extract n = checksquare(A) lda = max(1, stride(A, 2)) # check chkuplo(uplo) if way != 'C' && way != 'R' throw(ArgumentError("way must be C or R")) end if length(ipiv) != n throw(ArgumentError("length of pivot vector was $(length(ipiv)) but should have been $n")) end if length(e) != n throw(ArgumentError("length of e vector was $(length(e)) but should have been $n")) end # allocate info = Ref{BlasInt}() ccall((@blasfunc($syconvf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), uplo, way, n, A, lda, e, ipiv, info, 1, 1) chklapackerror(info[]) return A, e end end end ## (SY) hermitian matrices - eigendecomposition, Bunch-Kaufman decomposition, ## solvers (direct and factored) and inverse. for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in ((:zsyconv_,:zhesv_,:zhetrf_,:zhetri_,:zhetrs_,:ComplexF64, :Float64), (:csyconv_,:chesv_,:chetrf_,:chetri_,:chetrs_,:ComplexF32, :Float32)) @eval begin # SUBROUTINE ZSYCONV( UPLO, WAY, N, A, LDA, IPIV, WORK, INFO ) # # .. Scalar Arguments .. # CHARACTER UPLO, WAY # INTEGER INFO, LDA, N # .. # .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function syconv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A,ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong, Clong), uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info, 1, 1) chklapackerror(info[]) A, work end # SUBROUTINE ZHESV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function hesv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE ZHETRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function hetrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, ipiv, info[] end # SUBROUTINE ZHETRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) # function hetri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) # chkstride1(A) # n = checksquare(A) # chkuplo(uplo) # work = Vector{$elty}(undef, 1) # lwork = BlasInt(-1) # info = Ref{BlasInt}() # for i in 1:2 # ccall((@blasfunc($hetri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, # Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # chklapackerror(info[]) # if lwork < 0 # lwork = BlasInt(real(work[1])) # work = Vector{$elty}(undef, lwork) # end # end # A # end # SUBROUTINE ZHETRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function hetri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A end # SUBROUTINE ZHETRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ) function hetrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end for (hesv, hetrf, hetri, hetrs, elty, relty) in ((:zhesv_rook_,:zhetrf_rook_,:zhetri_rook_,:zhetrs_rook_,:ComplexF64, :Float64), (:chesv_rook_,:chetrf_rook_,:chetri_rook_,:chetrs_rook_,:ComplexF32, :Float32)) @eval begin # SUBROUTINE ZHESV_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function hesv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE ZHETRF_ROOK( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function hetrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, ipiv, info[] end # SUBROUTINE ZHETRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function hetri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A,ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A end # SUBROUTINE ZHETRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ) function hetrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end for (sysv, sytrf, sytri, sytrs, elty, relty) in ((:zsysv_,:zsytrf_,:zsytri_,:zsytrs_,:ComplexF64, :Float64), (:csysv_,:csytrf_,:csytri_,:csytrs_,:ComplexF32, :Float32)) @eval begin # SUBROUTINE ZSYSV( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # $ LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function sysv!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) chknonsingular(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE ZSYTRF( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function sytrf!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) if n == 0 return A, ipiv, zero(BlasInt) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, ipiv, info[] end # SUBROUTINE ZSYTRI2( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) # function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::Vector{BlasInt}) # chkstride1(A) # n = checksquare(A) # chkuplo(uplo) # work = Vector{$elty}(undef, 1) # lwork = BlasInt(-1) # info = Ref{BlasInt}() # for i in 1:2 # ccall((@blasfunc($sytri), libblastrampoline), Cvoid, # (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, # Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), # &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info, 1) # chklapackerror(info[]) # if lwork < 0 # lwork = BlasInt(real(work[1])) # work = Vector{$elty}(undef, lwork) # end # end # A # end # SUBROUTINE ZSYTRI( UPLO, N, A, LDA, IPIV, WORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function sytri!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A end # SUBROUTINE ZSYTRS( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ) function sytrs!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end end end for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in ((:zsysv_rook_,:zsytrf_rook_,:zsytri_rook_,:zsytrs_rook_,:zsyconvf_rook_,:ComplexF64, :Float64), (:csysv_rook_,:csytrf_rook_,:csytri_rook_,:csytrs_rook_,:csyconvf_rook_,:ComplexF32, :Float32)) @eval begin # SUBROUTINE ZSYSV_ROOK(UPLO, N, NRHS, A, LDA, IPIV, B, LDB, WORK, # $ LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, LWORK, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function sysv_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, B) chkstride1(A,B) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end ipiv = similar(A, BlasInt, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), work, lwork, info, 1) chkargsok(info[]) chknonsingular(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end B, A, ipiv end # SUBROUTINE ZSYTRF_ROOK( UPLO, N, A, LDA, IPIV, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function sytrf_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) ipiv = similar(A, BlasInt, n) if n == 0 return A, ipiv, zero(BlasInt) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, ipiv, info[] end # SUBROUTINE ZSYTRI_ROOK( UPLO, N, A, LDA, IPIV, WORK, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, N # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function sytri_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}) chkstride1(A, ipiv) n = checksquare(A) chkuplo(uplo) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), uplo, n, A, max(1,stride(A,2)), ipiv, work, info, 1) chklapackerror(info[]) A end # SUBROUTINE ZSYTRS_ROOK( UPLO, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LDB, N, NRHS # * .. # * .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ) function sytrs_rook!(uplo::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat{$elty}) require_one_based_indexing(A, ipiv, B) chkstride1(A,B,ipiv) n = checksquare(A) chkuplo(uplo) if n != size(B,1) throw(DimensionMismatch("B has first dimension $(size(B,1)), but needs $n")) end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info, 1) chklapackerror(info[]) B end # SUBROUTINE ZSYCONVF_ROOK( UPLO, WAY, N, A, LDA, IPIV, E, INFO ) # # .. Scalar Arguments .. # CHARACTER UPLO, WAY # INTEGER INFO, LDA, N # .. # .. Array Arguments .. # INTEGER IPIV( * ) # COMPLEX*16 A( LDA, * ), E( * ) function syconvf_rook!(uplo::AbstractChar, way::AbstractChar, A::AbstractMatrix{$elty}, ipiv::AbstractVector{BlasInt}, e::AbstractVector{$elty} = Vector{$elty}(undef, length(ipiv))) require_one_based_indexing(A, ipiv, e) chkstride1(A, ipiv, e) # extract n = checksquare(A) lda = stride(A, 2) # check chkuplo(uplo) if way != 'C' && way != 'R' throw(ArgumentError("way must be 'C' or 'R'")) end if length(ipiv) != n throw(ArgumentError("length of pivot vector was $(length(ipiv)) but should have been $n")) end if length(e) != n throw(ArgumentError("length of e vector was $(length(e)) but should have been $n")) end # allocate info = Ref{BlasInt}() ccall((@blasfunc($syconvf), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), uplo, way, n, A, max(1, lda), e, ipiv, info, 1, 1) chklapackerror(info[]) return A, e end end end """ syconv!(uplo, A, ipiv) -> (A, work) Converts a symmetric matrix `A` (which has been factorized into a triangular matrix) into two matrices `L` and `D`. If `uplo = U`, `A` is upper triangular. If `uplo = L`, it is lower triangular. `ipiv` is the pivot vector from the triangular factorization. `A` is overwritten by `L` and `D`. """ syconv!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) """ sysv!(uplo, A, B) -> (B, A, ipiv) Finds the solution to `A * X = B` for symmetric matrix `A`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `B` is overwritten by the solution `X`. `A` is overwritten by its Bunch-Kaufman factorization. `ipiv` contains pivoting information about the factorization. """ sysv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) """ sytrf!(uplo, A) -> (A, ipiv, info) Computes the Bunch-Kaufman factorization of a symmetric matrix `A`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. Returns `A`, overwritten by the factorization, a pivot vector `ipiv`, and the error code `info` which is a non-negative integer. If `info` is positive the matrix is singular and the diagonal part of the factorization is exactly zero at position `info`. """ sytrf!(uplo::AbstractChar, A::AbstractMatrix) """ sytri!(uplo, A, ipiv) Computes the inverse of a symmetric matrix `A` using the results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `A` is overwritten by its inverse. """ sytri!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) """ sytrs!(uplo, A, ipiv, B) Solves the equation `A * X = B` for a symmetric matrix `A` using the results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `B` is overwritten by the solution `X`. """ sytrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) """ hesv!(uplo, A, B) -> (B, A, ipiv) Finds the solution to `A * X = B` for Hermitian matrix `A`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `B` is overwritten by the solution `X`. `A` is overwritten by its Bunch-Kaufman factorization. `ipiv` contains pivoting information about the factorization. """ hesv!(uplo::AbstractChar, A::AbstractMatrix, B::AbstractVecOrMat) """ hetrf!(uplo, A) -> (A, ipiv, info) Computes the Bunch-Kaufman factorization of a Hermitian matrix `A`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. Returns `A`, overwritten by the factorization, a pivot vector `ipiv`, and the error code `info` which is a non-negative integer. If `info` is positive the matrix is singular and the diagonal part of the factorization is exactly zero at position `info`. """ hetrf!(uplo::AbstractChar, A::AbstractMatrix) """ hetri!(uplo, A, ipiv) Computes the inverse of a Hermitian matrix `A` using the results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `A` is overwritten by its inverse. """ hetri!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}) """ hetrs!(uplo, A, ipiv, B) Solves the equation `A * X = B` for a Hermitian matrix `A` using the results of `sytrf!`. If `uplo = U`, the upper half of `A` is stored. If `uplo = L`, the lower half is stored. `B` is overwritten by the solution `X`. """ hetrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) # Symmetric (real) eigensolvers for (syev, syevr, syevd, sygvd, elty) in ((:dsyev_,:dsyevr_,:dsyevd_,:dsygvd_,:Float64), (:ssyev_,:ssyevr_,:ssyevd_,:ssygvd_,:Float32)) @eval begin # SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, LDA, LWORK, N # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) W = similar(A, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobz, uplo, n, A, max(1,stride(A,2)), W, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end jobz == 'V' ? (W, A) : W end # SUBROUTINE DSYEVR( JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, # $ ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, # $ IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, RANGE, UPLO # INTEGER IL, INFO, IU, LDA, LDZ, LIWORK, LWORK, M, N # DOUBLE PRECISION ABSTOL, VL, VU # * .. # * .. Array Arguments .. # INTEGER ISUPPZ( * ), IWORK( * ) # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ), Z( LDZ, * ) function syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) chkstride1(A) n = checksquare(A) chkuplofinite(A, uplo) if range == 'I' && !(1 <= il <= iu <= n) throw(ArgumentError("illegal choice of eigenvalue indices (il = $il, iu = $iu), which must be between 1 and n = $n")) end if range == 'V' && vl >= vu throw(ArgumentError("lower boundary, $vl, must be less than upper boundary, $vu")) end lda = stride(A,2) m = Ref{BlasInt}() W = similar(A, $elty, n) ldz = n if jobz == 'N' Z = similar(A, $elty, ldz, 0) elseif jobz == 'V' Z = similar(A, $elty, ldz, n) end isuppz = similar(A, BlasInt, 2*n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($syevr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), jobz, range, uplo, n, A, max(1,lda), vl, vu, il, iu, abstol, m, W, Z, max(1,ldz), isuppz, work, lwork, iwork, liwork, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = iwork[1] resize!(iwork, liwork) end end W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) # SUBROUTINE DSYEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, # $ IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, LDA, LIWORK, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplofinite(A, uplo) lda = stride(A,2) m = Ref{BlasInt}() W = similar(A, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($syevd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobz, uplo, n, A, max(1,lda), W, work, lwork, iwork, liwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = iwork[1] resize!(iwork, liwork) end end jobz == 'V' ? (W, A) : W end # Generalized eigenproblem # SUBROUTINE DSYGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, ITYPE, LDA, LDB, LIWORK, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), B( LDB, * ), W( * ), WORK( * ) function sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) w = similar(A, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($sygvd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), itype, jobz, uplo, n, A, lda, B, ldb, w, work, lwork, iwork, liwork, info, 1, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(work[1]) resize!(work, lwork) liwork = iwork[1] resize!(iwork, liwork) end end chkposdef(info[]) w, A, B end end end # Hermitian eigensolvers for (syev, syevr, syevd, sygvd, elty, relty) in ((:zheev_,:zheevr_,:zheevd_,:zhegvd_,:ComplexF64,:Float64), (:cheev_,:cheevr_,:cheevd_,:chegvd_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZHEEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION RWORK( * ), W( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) chkuplofinite(A, uplo) n = checksquare(A) W = similar(A, $relty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, max(1, 3n-2)) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end jobz == 'V' ? (W, A) : W end # SUBROUTINE ZHEEVR( JOBZ, RANGE, UPLO, N, A, LDA, VL, VU, IL, IU, # $ ABSTOL, M, W, Z, LDZ, ISUPPZ, WORK, LWORK, # $ RWORK, LRWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, RANGE, UPLO # INTEGER IL, INFO, IU, LDA, LDZ, LIWORK, LRWORK, LWORK, # $ M, N # DOUBLE PRECISION ABSTOL, VL, VU # * .. # * .. Array Arguments .. # INTEGER ISUPPZ( * ), IWORK( * ) # DOUBLE PRECISION RWORK( * ), W( * ) # COMPLEX*16 A( LDA, * ), WORK( * ), Z( LDZ, * ) function syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) chkstride1(A) chkuplofinite(A, uplo) n = checksquare(A) if range == 'I' && !(1 <= il <= iu <= n) throw(ArgumentError("illegal choice of eigenvalue indices (il = $il, iu=$iu), which must be between 1 and n = $n")) end if range == 'V' && vl >= vu throw(ArgumentError("lower boundary, $vl, must be less than upper boundary, $vu")) end lda = max(1,stride(A,2)) m = Ref{BlasInt}() W = similar(A, $relty, n) if jobz == 'N' ldz = 1 Z = similar(A, $elty, ldz, 0) elseif jobz == 'V' ldz = n Z = similar(A, $elty, ldz, n) end isuppz = similar(A, BlasInt, 2*n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 1) lrwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] ccall((@blasfunc($syevr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), jobz, range, uplo, n, A, lda, vl, vu, il, iu, abstol, m, W, Z, ldz, isuppz, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) lrwork = BlasInt(rwork[1]) resize!(rwork, lrwork) liwork = iwork[1] resize!(iwork, liwork) end end W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) # SUBROUTINE ZHEEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, # $ LRWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, LDA, LIWORK, LRWORK, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) chkuplofinite(A, uplo) n = checksquare(A) lda = max(1, stride(A,2)) m = Ref{BlasInt}() W = similar(A, $relty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 1) lrwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] ccall((@blasfunc($syevd), liblapack), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) lrwork = BlasInt(rwork[1]) resize!(rwork, lrwork) liwork = iwork[1] resize!(iwork, liwork) end end jobz == 'V' ? (W, A) : W end # SUBROUTINE ZHEGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, RWORK, LRWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. # CHARACTER JOBZ, UPLO # INTEGER INFO, ITYPE, LDA, LDB, LIWORK, LRWORK, LWORK, N # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION RWORK( * ), W( * ) # COMPLEX*16 A( LDA, * ), B( LDB, * ), WORK( * ) function sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) chkuplofinite(A, uplo) chkuplofinite(B, uplo) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) w = similar(A, $relty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) rwork = Vector{$relty}(undef, 1) lrwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] ccall((@blasfunc($sygvd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), itype, jobz, uplo, n, A, lda, B, ldb, w, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = iwork[1] resize!(iwork, liwork) lrwork = BlasInt(rwork[1]) resize!(rwork, lrwork) end end chkposdef(info[]) w, A, B end end end """ syev!(jobz, uplo, A) Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle of `A` is used. If `uplo = L`, the lower triangle of `A` is used. """ syev!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) """ syevr!(jobz, range, uplo, A, vl, vu, il, iu, abstol) -> (W, Z) Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle of `A` is used. If `uplo = L`, the lower triangle of `A` is used. If `range = A`, all the eigenvalues are found. If `range = V`, the eigenvalues in the half-open interval `(vl, vu]` are found. If `range = I`, the eigenvalues with indices between `il` and `iu` are found. `abstol` can be set as a tolerance for convergence. The eigenvalues are returned in `W` and the eigenvectors in `Z`. """ syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) """ syevd!(jobz, uplo, A) Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle of `A` is used. If `uplo = L`, the lower triangle of `A` is used. Use the divide-and-conquer method, instead of the QR iteration used by `syev!` or multiple relatively robust representations used by `syevr!`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performatce of different methods. """ syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) """ sygvd!(itype, jobz, uplo, A, B) -> (w, A, B) Finds the generalized eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) of a symmetric matrix `A` and symmetric positive-definite matrix `B`. If `uplo = U`, the upper triangles of `A` and `B` are used. If `uplo = L`, the lower triangles of `A` and `B` are used. If `itype = 1`, the problem to solve is `A * x = lambda * B * x`. If `itype = 2`, the problem to solve is `A * B * x = lambda * x`. If `itype = 3`, the problem to solve is `B * A * x = lambda * x`. """ sygvd!(itype::Integer, jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) ## (BD) Bidiagonal matrices - singular value decomposition for (bdsqr, relty, elty) in ((:dbdsqr_,:Float64,:Float64), (:sbdsqr_,:Float32,:Float32), (:zbdsqr_,:Float64,:ComplexF64), (:cbdsqr_,:Float32,:ComplexF32)) @eval begin function bdsqr!(uplo::AbstractChar, d::AbstractVector{$relty}, e_::AbstractVector{$relty}, Vt::AbstractMatrix{$elty}, U::AbstractMatrix{$elty}, C::AbstractMatrix{$elty}) require_one_based_indexing(d, e_, Vt, U, C) chkstride1(d, e_, Vt, U, C) # Extract number n = length(d) ncvt, nru, ncc = size(Vt, 2), size(U, 1), size(C, 2) ldvt, ldu, ldc = max(1, stride(Vt,2)), max(1, stride(U, 2)), max(1, stride(C,2)) # Do checks chkuplo(uplo) if length(e_) != n - 1 throw(DimensionMismatch("off-diagonal has length $(length(e_)) but should have length $(n - 1)")) end if ncvt > 0 && ldvt < n throw(DimensionMismatch("leading dimension of Vt, $ldvt, must be at least $n")) end if ldu < nru throw(DimensionMismatch("leading dimension of U, $ldu, must be at least $nru")) end if size(U, 2) != n throw(DimensionMismatch("U must have $n columns but has $(size(U, 2))")) end if ncc > 0 && ldc < n throw(DimensionMismatch("leading dimension of C, $ldc, must be at least $n")) end # Allocate work = Vector{$relty}(undef, 4n) info = Ref{BlasInt}() ccall((@blasfunc($bdsqr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong), uplo, n, ncvt, nru, ncc, d, e_, Vt, ldvt, U, ldu, C, ldc, work, info, 1) chklapackerror(info[]) d, Vt, U, C #singular values in descending order, P**T * VT, U * Q, Q**T * C end end end """ bdsqr!(uplo, d, e_, Vt, U, C) -> (d, Vt, U, C) Computes the singular value decomposition of a bidiagonal matrix with `d` on the diagonal and `e_` on the off-diagonal. If `uplo = U`, `e_` is the superdiagonal. If `uplo = L`, `e_` is the subdiagonal. Can optionally also compute the product `Q' * C`. Returns the singular values in `d`, and the matrix `C` overwritten with `Q' * C`. """ bdsqr!(uplo::AbstractChar, d::AbstractVector, e_::AbstractVector, Vt::AbstractMatrix, U::AbstractMatrix, C::AbstractMatrix) #Defined only for real types for (bdsdc, elty) in ((:dbdsdc_,:Float64), (:sbdsdc_,:Float32)) @eval begin #* DBDSDC computes the singular value decomposition (SVD) of a real #* N-by-N (upper or lower) bidiagonal matrix B: B = U * S * VT, #* using a divide and conquer method #* .. Scalar Arguments .. # CHARACTER COMPQ, UPLO # INTEGER INFO, LDU, LDVT, N #* .. #* .. Array Arguments .. # INTEGER IQ( * ), IWORK( * ) # DOUBLE PRECISION D( * ), E( * ), Q( * ), U( LDU, * ), # $ VT( LDVT, * ), WORK( * ) function bdsdc!(uplo::AbstractChar, compq::AbstractChar, d::AbstractVector{$elty}, e_::AbstractVector{$elty}) require_one_based_indexing(d, e_) chkstride1(d, e_) n, ldiq, ldq, ldu, ldvt = length(d), 1, 1, 1, 1 chkuplo(uplo) if compq == 'N' lwork = 6*n elseif compq == 'P' @warn "COMPQ='P' is not tested" #TODO turn this into an actual LAPACK call #smlsiz=ilaenv(9, $elty === :Float64 ? 'dbdsqr' : 'sbdsqr', string(uplo, compq), n,n,n,n) smlsiz=100 #For now, completely overkill ldq = n*(11+2*smlsiz+8*round(Int,log((n/(smlsiz+1)))/log(2))) ldiq = n*(3+3*round(Int,log(n/(smlsiz+1))/log(2))) lwork = 6*n elseif compq == 'I' ldvt=ldu=max(1, n) lwork=3*n^2 + 4*n else throw(ArgumentError("COMPQ argument must be 'N', 'P' or 'I', got $(repr(compq))")) end u = similar(d, $elty, (ldu, n)) vt = similar(d, $elty, (ldvt, n)) q = similar(d, $elty, ldq) iq = similar(d, BlasInt, ldiq) work = Vector{$elty}(undef, lwork) iwork = Vector{BlasInt}(undef, 8n) info = Ref{BlasInt}() ccall((@blasfunc($bdsdc), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), uplo, compq, n, d, e_, u, ldu, vt, ldvt, q, iq, work, iwork, info, 1, 1) chklapackerror(info[]) d, e_, u, vt, q, iq end end end """ bdsdc!(uplo, compq, d, e_) -> (d, e, u, vt, q, iq) Computes the singular value decomposition of a bidiagonal matrix with `d` on the diagonal and `e_` on the off-diagonal using a divide and conqueq method. If `uplo = U`, `e_` is the superdiagonal. If `uplo = L`, `e_` is the subdiagonal. If `compq = N`, only the singular values are found. If `compq = I`, the singular values and vectors are found. If `compq = P`, the singular values and vectors are found in compact form. Only works for real types. Returns the singular values in `d`, and if `compq = P`, the compact singular vectors in `iq`. """ bdsdc!(uplo::AbstractChar, compq::AbstractChar, d::AbstractVector, e_::AbstractVector) for (gecon, elty) in ((:dgecon_,:Float64), (:sgecon_,:Float32)) @eval begin # SUBROUTINE DGECON( NORM, N, A, LDA, ANORM, RCOND, WORK, IWORK, # $ INFO ) # * .. Scalar Arguments .. # CHARACTER NORM # INTEGER INFO, LDA, N # DOUBLE PRECISION ANORM, RCOND # * .. # * .. Array Arguments .. # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), WORK( * ) function gecon!(normtype::AbstractChar, A::AbstractMatrix{$elty}, anorm::$elty) chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) rcond = Ref{$elty}() work = Vector{$elty}(undef, 4n) iwork = Vector{BlasInt}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Clong), normtype, n, A, lda, anorm, rcond, work, iwork, info, 1) chklapackerror(info[]) rcond[] end end end for (gecon, elty, relty) in ((:zgecon_,:ComplexF64,:Float64), (:cgecon_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZGECON( NORM, N, A, LDA, ANORM, RCOND, WORK, RWORK, # $ INFO ) # * .. Scalar Arguments .. # CHARACTER NORM # INTEGER INFO, LDA, N # DOUBLE PRECISION ANORM, RCOND # * .. # * .. Array Arguments .. # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), WORK( * ) function gecon!(normtype::AbstractChar, A::AbstractMatrix{$elty}, anorm::$relty) chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) rcond = Ref{$relty}() work = Vector{$elty}(undef, 2n) rwork = Vector{$relty}(undef, 2n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}, Clong), normtype, n, A, lda, anorm, rcond, work, rwork, info, 1) chklapackerror(info[]) rcond[] end end end """ gecon!(normtype, A, anorm) Finds the reciprocal condition number of matrix `A`. If `normtype = I`, the condition number is found in the infinity norm. If `normtype = O` or `1`, the condition number is found in the one norm. `A` must be the result of `getrf!` and `anorm` is the norm of `A` in the relevant norm. """ gecon!(normtype::AbstractChar, A::AbstractMatrix, anorm) for (gehrd, elty) in ((:dgehrd_,:Float64), (:sgehrd_,:Float32), (:zgehrd_,:ComplexF64), (:cgehrd_,:ComplexF32)) @eval begin # .. Scalar Arguments .. # INTEGER IHI, ILO, INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function gehrd!(ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkfinite(A) # balancing routines don't support NaNs and Infs tau = similar(A, $elty, max(0,n - 1)) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gehrd), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, ilo, ihi, A, max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, tau end end end gehrd!(A::AbstractMatrix) = gehrd!(1, size(A, 1), A) """ gehrd!(ilo, ihi, A) -> (A, tau) Converts a matrix `A` to Hessenberg form. If `A` is balanced with `gebal!` then `ilo` and `ihi` are the outputs of `gebal!`. Otherwise they should be `ilo = 1` and `ihi = size(A,2)`. `tau` contains the elementary reflectors of the factorization. """ gehrd!(ilo::Integer, ihi::Integer, A::AbstractMatrix) for (orghr, elty) in ((:dorghr_,:Float64), (:sorghr_,:Float32), (:zunghr_,:ComplexF64), (:cunghr_,:ComplexF32)) @eval begin # * .. Scalar Arguments .. # INTEGER IHI, ILO, INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orghr!(ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A, tau) n = checksquare(A) if n - length(tau) != 1 throw(DimensionMismatch("tau has length $(length(tau)), needs $(n - 1)")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orghr), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), n, ilo, ihi, A, max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A end end end """ orghr!(ilo, ihi, A, tau) Explicitly finds `Q`, the orthogonal/unitary matrix from `gehrd!`. `ilo`, `ihi`, `A`, and `tau` must correspond to the input/output to `gehrd!`. """ orghr!(ilo::Integer, ihi::Integer, A::AbstractMatrix, tau::AbstractVector) for (ormhr, elty) in ((:dormhr_,:Float64), (:sormhr_,:Float32), (:zunmhr_,:ComplexF64), (:cunmhr_,:ComplexF32)) @eval begin # .. Scalar Arguments .. # CHARACTER side, trans # INTEGER ihi, ilo, info, lda, ldc, lwork, m, n # .. # .. Array Arguments .. # DOUBLE PRECISION a( lda, * ), c( ldc, * ), tau( * ), work( * ) function ormhr!(side::AbstractChar, trans::AbstractChar, ilo::Integer, ihi::Integer, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chkstride1(A, tau, C) n = checksquare(A) mC, nC = size(C, 1), size(C, 2) if n - length(tau) != 1 throw(DimensionMismatch("tau has length $(length(tau)), needs $(n - 1)")) end if (side == 'L' && mC != n) || (side == 'R' && nC != n) throw(DimensionMismatch("A and C matrices are not conformable")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormhr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), side, trans, mC, nC, ilo, ihi, A, max(1, stride(A, 2)), tau, C, max(1, stride(C, 2)), work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end end end for (hseqr, elty) in ((:zhseqr_,:ComplexF64), (:chseqr_,:ComplexF32)) @eval begin # * .. Scalar Arguments .. # CHARACTER JOB, COMPZ # INTEGER N, ILO, IHI, LWORK, LDH, LDZ, INFO # * .. # * .. Array Arguments .. # COMPLEX*16 H( LDH, * ), Z( LDZ, * ), WORK( * ) function hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) require_one_based_indexing(H, Z) chkstride1(H) n = checksquare(H) checksquare(Z) == n || throw(DimensionMismatch()) ldh = max(1, stride(H, 2)) ldz = max(1, stride(Z, 2)) w = similar(H, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hseqr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), job, compz, n, ilo, ihi, H, ldh, w, Z, ldz, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end H, Z, w end end end for (hseqr, elty) in ((:dhseqr_,:Float64), (:shseqr_,:Float32)) @eval begin # * .. Scalar Arguments .. # CHARACTER JOB, COMPZ # INTEGER N, ILO, IHI, LWORK, LDH, LDZ, INFO # * .. # * .. Array Arguments .. # COMPLEX*16 H( LDH, * ), Z( LDZ, * ), WORK( * ) function hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, H::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) require_one_based_indexing(H, Z) chkstride1(H) n = checksquare(H) checksquare(Z) == n || throw(DimensionMismatch()) ldh = max(1, stride(H, 2)) ldz = max(1, stride(Z, 2)) wr = similar(H, $elty, n) wi = similar(H, $elty, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hseqr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), job, compz, n, ilo, ihi, H, ldh, wr, wi, Z, ldz, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end H, Z, complex.(wr, wi) end end end hseqr!(H::StridedMatrix{T}, Z::StridedMatrix{T}) where {T<:BlasFloat} = hseqr!('S', 'V', 1, size(H, 1), H, Z) hseqr!(H::StridedMatrix{T}) where {T<:BlasFloat} = hseqr!('S', 'I', 1, size(H, 1), H, similar(H)) """ hseqr!(job, compz, ilo, ihi, H, Z) -> (H, Z, w) Computes all eigenvalues and (optionally) the Schur factorization of a matrix reduced to Hessenberg form. If `H` is balanced with `gebal!` then `ilo` and `ihi` are the outputs of `gebal!`. Otherwise they should be `ilo = 1` and `ihi = size(H,2)`. `tau` contains the elementary reflectors of the factorization. """ hseqr!(job::AbstractChar, compz::AbstractChar, ilo::Integer, ihi::Integer, H::AbstractMatrix, Z::AbstractMatrix) for (hetrd, elty) in ((:dsytrd_,Float64), (:ssytrd_,Float32), (:zhetrd_,ComplexF64), (:chetrd_,ComplexF32)) relty = real(elty) @eval begin # .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAU( * ), WORK( * ) function hetrd!(uplo::AbstractChar, A::AbstractMatrix{$elty}) chkstride1(A) n = checksquare(A) chkuplo(uplo) chkfinite(A) # balancing routines don't support NaNs and Infs tau = similar(A, $elty, max(0,n - 1)) d = Vector{$relty}(undef, n) e = Vector{$relty}(undef, max(0,n - 1)) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrd), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1, stride(A, 2)), d, e, tau, work, lwork, info, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, tau, d, e end end end """ hetrd!(uplo, A) -> (A, tau, d, e) Converts a Hermitian matrix `A` to real-symmetric tridiagonal Hessenberg form. If `uplo = U`, the upper half of `A` is stored; if `uplo = L`, the lower half is stored. `tau` contains the elementary reflectors of the factorization, `d` contains the diagonal and `e` contains the upper/lower diagonal. """ hetrd!(uplo::AbstractChar, A::AbstractMatrix) for (orgtr, elty) in ((:dorgtr_,:Float64), (:sorgtr_,:Float32), (:zungtr_,:ComplexF64), (:cungtr_,:ComplexF32)) @eval begin # * .. Scalar Arguments .. # CHARACTER UPLO # INTEGER INFO, LDA, LWORK, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) function orgtr!(uplo::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}) require_one_based_indexing(A, tau) chkstride1(A, tau) n = checksquare(A) if n - length(tau) != 1 throw(DimensionMismatch("tau has length $(length(tau)), needs $(n - 1)")) end chkuplo(uplo) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgtr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong), uplo, n, A, max(1, stride(A, 2)), tau, work, lwork, info, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A end end end """ orgtr!(uplo, A, tau) Explicitly finds `Q`, the orthogonal/unitary matrix from `hetrd!`. `uplo`, `A`, and `tau` must correspond to the input/output to `hetrd!`. """ orgtr!(uplo::AbstractChar, A::AbstractMatrix, tau::AbstractVector) for (ormtr, elty) in ((:dormtr_,:Float64), (:sormtr_,:Float32), (:zunmtr_,:ComplexF64), (:cunmtr_,:ComplexF32)) @eval begin # .. Scalar Arguments .. # CHARACTER side, trans, uplo # INTEGER info, lda, ldc, lwork, m, n # .. # .. Array Arguments .. # DOUBLE PRECISION a( lda, * ), c( ldc, * ), tau( * ), work( * ) function ormtr!(side::AbstractChar, uplo::AbstractChar, trans::AbstractChar, A::AbstractMatrix{$elty}, tau::AbstractVector{$elty}, C::AbstractVecOrMat{$elty}) require_one_based_indexing(A, tau, C) chkstride1(A, tau, C) n = checksquare(A) chkuplo(uplo) mC, nC = size(C, 1), size(C, 2) if n - length(tau) != 1 throw(DimensionMismatch("tau has length $(length(tau)), needs $(n - 1)")) end if (side == 'L' && mC != n) || (side == 'R' && nC != n) throw(DimensionMismatch("A and C matrices are not conformable")) end work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormtr), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), side, uplo, trans, mC, nC, A, max(1, stride(A, 2)), tau, C, max(1, stride(C, 2)), work, lwork, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end C end end end for (gees, gges, gges3, elty) in ((:dgees_,:dgges_,:dgges3_,:Float64), (:sgees_,:sgges_,:sgges3_,:Float32)) @eval begin # .. Scalar Arguments .. # CHARACTER JOBVS, SORT # INTEGER INFO, LDA, LDVS, LWORK, N, SDIM # .. # .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION A( LDA, * ), VS( LDVS, * ), WI( * ), WORK( * ), # $ WR( * ) function gees!(jobvs::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) n = checksquare(A) sdim = Vector{BlasInt}(undef, 1) wr = similar(A, $elty, n) wi = similar(A, $elty, n) vs = similar(A, $elty, jobvs == 'V' ? n : 0, n) ldvs = max(size(vs, 1), 1) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), jobvs, 'N', C_NULL, n, A, max(1, stride(A, 2)), sdim, wr, wi, vs, ldvs, work, lwork, C_NULL, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, vs, iszero(wi) ? wr : complex.(wr, wi) end # * .. Scalar Arguments .. # CHARACTER JOBVSL, JOBVSR, SORT # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM # * .. # * .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), # $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), # $ VSR( LDVSR, * ), WORK( * ) function gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end sdim = BlasInt(0) alphar = similar(A, $elty, n) alphai = similar(A, $elty, n) beta = similar(A, $elty, n) ldvsl = jobvsl == 'V' ? max(1, n) : 1 vsl = similar(A, $elty, ldvsl, n) ldvsr = jobvsr == 'V' ? max(1, n) : 1 vsr = similar(A, $elty, ldvsr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1,stride(A, 2)), B, max(1,stride(B, 2)), sdim, alphar, alphai, beta, vsl, ldvsl, vsr, ldvsr, work, lwork, C_NULL, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, B, complex.(alphar, alphai), beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] end # * .. Scalar Arguments .. # CHARACTER JOBVSL, JOBVSR, SORT # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM # * .. # * .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), # $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), # $ VSR( LDVSR, * ), WORK( * ) function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end sdim = BlasInt(0) alphar = similar(A, $elty, n) alphai = similar(A, $elty, n) beta = similar(A, $elty, n) ldvsl = jobvsl == 'V' ? max(1, n) : 1 vsl = similar(A, $elty, ldvsl, n) ldvsr = jobvsr == 'V' ? max(1, n) : 1 vsr = similar(A, $elty, ldvsr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges3), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1,stride(A, 2)), B, max(1,stride(B, 2)), sdim, alphar, alphai, beta, vsl, ldvsl, vsr, ldvsr, work, lwork, C_NULL, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, B, complex.(alphar, alphai), beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] end end end for (gees, gges, gges3, elty, relty) in ((:zgees_,:zgges_,:zgges3_,:ComplexF64,:Float64), (:cgees_,:cgges_,:cgges3_,:ComplexF32,:Float32)) @eval begin # * .. Scalar Arguments .. # CHARACTER JOBVS, SORT # INTEGER INFO, LDA, LDVS, LWORK, N, SDIM # * .. # * .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), VS( LDVS, * ), W( * ), WORK( * ) function gees!(jobvs::AbstractChar, A::AbstractMatrix{$elty}) require_one_based_indexing(A) chkstride1(A) n = checksquare(A) sort = 'N' sdim = BlasInt(0) w = similar(A, $elty, n) vs = similar(A, $elty, jobvs == 'V' ? n : 1, n) ldvs = max(size(vs, 1), 1) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong), jobvs, sort, C_NULL, n, A, max(1, stride(A, 2)), sdim, w, vs, ldvs, work, lwork, rwork, C_NULL, info, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, vs, w end # * .. Scalar Arguments .. # CHARACTER JOBVSL, JOBVSR, SORT # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM # * .. # * .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), # $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), # $ WORK( * ) function gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end sdim = BlasInt(0) alpha = similar(A, $elty, n) beta = similar(A, $elty, n) ldvsl = jobvsl == 'V' ? max(1, n) : 1 vsl = similar(A, $elty, ldvsl, n) ldvsr = jobvsr == 'V' ? max(1, n) : 1 vsr = similar(A, $elty, ldvsr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 8n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1, stride(A, 2)), B, max(1, stride(B, 2)), sdim, alpha, beta, vsl, ldvsl, vsr, ldvsr, work, lwork, rwork, C_NULL, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, B, alpha, beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] end # * .. Scalar Arguments .. # CHARACTER JOBVSL, JOBVSR, SORT # INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM # * .. # * .. Array Arguments .. # LOGICAL BWORK( * ) # DOUBLE PRECISION RWORK( * ) # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), # $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), # $ WORK( * ) function gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}) chkstride1(A, B) n, m = checksquare(A, B) if n != m throw(DimensionMismatch("dimensions of A, ($n,$n), and B, ($m,$m), must match")) end sdim = BlasInt(0) alpha = similar(A, $elty, n) beta = similar(A, $elty, n) ldvsl = jobvsl == 'V' ? max(1, n) : 1 vsl = similar(A, $elty, ldvsl, n) ldvsr = jobvsr == 'V' ? max(1, n) : 1 vsr = similar(A, $elty, ldvsr, n) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) rwork = Vector{$relty}(undef, 8n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges3), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Cvoid}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Cvoid}, Ref{BlasInt}, Clong, Clong, Clong), jobvsl, jobvsr, 'N', C_NULL, n, A, max(1, stride(A, 2)), B, max(1, stride(B, 2)), sdim, alpha, beta, vsl, ldvsl, vsr, ldvsr, work, lwork, rwork, C_NULL, info, 1, 1, 1) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) resize!(work, lwork) end end A, B, alpha, beta, vsl[1:(jobvsl == 'V' ? n : 0),:], vsr[1:(jobvsr == 'V' ? n : 0),:] end end end """ gees!(jobvs, A) -> (A, vs, w) Computes the eigenvalues (`jobvs = N`) or the eigenvalues and Schur vectors (`jobvs = V`) of matrix `A`. `A` is overwritten by its Schur form. Returns `A`, `vs` containing the Schur vectors, and `w`, containing the eigenvalues. """ gees!(jobvs::AbstractChar, A::AbstractMatrix) """ gges!(jobvsl, jobvsr, A, B) -> (A, B, alpha, beta, vsl, vsr) Computes the generalized eigenvalues, generalized Schur form, left Schur vectors (`jobsvl = V`), or right Schur vectors (`jobvsr = V`) of `A` and `B`. The generalized eigenvalues are returned in `alpha` and `beta`. The left Schur vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. """ gges!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) """ gges3!(jobvsl, jobvsr, A, B) -> (A, B, alpha, beta, vsl, vsr) Computes the generalized eigenvalues, generalized Schur form, left Schur vectors (`jobsvl = V`), or right Schur vectors (`jobvsr = V`) of `A` and `B` using a blocked algorithm. This function requires LAPACK 3.6.0. The generalized eigenvalues are returned in `alpha` and `beta`. The left Schur vectors are returned in `vsl` and the right Schur vectors are returned in `vsr`. """ gges3!(jobvsl::AbstractChar, jobvsr::AbstractChar, A::AbstractMatrix, B::AbstractMatrix) for (trexc, trsen, tgsen, elty) in ((:dtrexc_, :dtrsen_, :dtgsen_, :Float64), (:strexc_, :strsen_, :stgsen_, :Float32)) @eval begin # * .. Scalar Arguments .. # CHARACTER COMPQ # INTEGER IFST, ILST, INFO, LDQ, LDT, N # * .. # * .. Array Arguments .. # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WORK( * ) function trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) chkstride1(T, Q) n = checksquare(T) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) work = Vector{$elty}(undef, n) info = Ref{BlasInt}() ccall((@blasfunc($trexc), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Clong), compq, n, T, ldt, Q, ldq, ifst, ilst, work, info, 1) chklapackerror(info[]) T, Q end trexc!(ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = trexc!('V', ifst, ilst, T, Q) # * .. Scalar Arguments .. # CHARACTER COMPQ, JOB # INTEGER INFO, LDQ, LDT, LIWORK, LWORK, M, N # DOUBLE PRECISION S, SEP # * .. # * .. Array Arguments .. # LOGICAL SELECT( * ) # INTEGER IWORK( * ) # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WI( * ), WORK( * ), WR( * ) function trsen!(job::AbstractChar, compq::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) chkstride1(T, Q, select) n = checksquare(T) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) wr = similar(T, $elty, n) wi = similar(T, $elty, n) m = sum(select) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) liwork = BlasInt(-1) info = Ref{BlasInt}() select = convert(Array{BlasInt}, select) s = Ref{$elty}(zero($elty)) sep = Ref{$elty}(zero($elty)) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($trsen), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), job, compq, select, n, T, ldt, Q, ldq, wr, wi, m, s, sep, work, lwork, iwork, liwork, info, 1, 1) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = BlasInt(real(iwork[1])) resize!(iwork, liwork) end end T, Q, iszero(wi) ? wr : complex.(wr, wi), s[], sep[] end trsen!(select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = trsen!('N', 'V', select, T, Q) # .. Scalar Arguments .. # LOGICAL WANTQ, WANTZ # INTEGER IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK, # $ M, N # DOUBLE PRECISION PL, PR # .. # .. Array Arguments .. # LOGICAL SELECT( * ) # INTEGER IWORK( * ) # DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), # $ B( LDB, * ), BETA( * ), DIF( * ), Q( LDQ, * ), # $ WORK( * ), Z( LDZ, * ) # .. function tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) chkstride1(select, S, T, Q, Z) n, nt, nq, nz = checksquare(S, T, Q, Z) if n != nt throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match")) end if n != nq throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match")) end if n != nz throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match")) end lds = max(1, stride(S, 2)) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) ldz = max(1, stride(Z, 2)) m = sum(select) alphai = similar(T, $elty, n) alphar = similar(T, $elty, n) beta = similar(T, $elty, n) lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) liwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) info = Ref{BlasInt}() select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), 0, 1, 1, select, n, S, lds, T, ldt, alphar, alphai, beta, Q, ldq, Z, ldz, m, C_NULL, C_NULL, C_NULL, work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = BlasInt(real(iwork[1])) resize!(iwork, liwork) end end S, T, complex.(alphar, alphai), beta, Q, Z end end end for (trexc, trsen, tgsen, elty, relty) in ((:ztrexc_, :ztrsen_, :ztgsen_, :ComplexF64, :Float64), (:ctrexc_, :ctrsen_, :ctgsen_, :ComplexF32, :Float32)) @eval begin # .. Scalar Arguments .. # CHARACTER COMPQ # INTEGER IFST, ILST, INFO, LDQ, LDT, N # .. # .. Array Arguments .. # DOUBLE PRECISION Q( LDQ, * ), T( LDT, * ), WORK( * ) function trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) chkstride1(T, Q) n = checksquare(T) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trexc), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong), compq, n, T, ldt, Q, ldq, ifst, ilst, info, 1) chklapackerror(info[]) T, Q end trexc!(ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = trexc!('V', ifst, ilst, T, Q) # .. Scalar Arguments .. # CHARACTER COMPQ, JOB # INTEGER INFO, LDQ, LDT, LWORK, M, N # DOUBLE PRECISION S, SEP # .. # .. Array Arguments .. # LOGICAL SELECT( * ) # COMPLEX Q( LDQ, * ), T( LDT, * ), W( * ), WORK( * ) function trsen!(job::AbstractChar, compq::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) chkstride1(select, T, Q) n = checksquare(T) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) w = similar(T, $elty, n) m = sum(select) work = Vector{$elty}(undef, 1) lwork = BlasInt(-1) info = Ref{BlasInt}() select = convert(Array{BlasInt}, select) s = Ref{$relty}(zero($relty)) sep = Ref{$relty}(zero($relty)) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($trsen), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), job, compq, select, n, T, ldt, Q, ldq, w, m, s, sep, work, lwork, info, 1, 1) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) resize!(work, lwork) end end T, Q, w, s[], sep[] end trsen!(select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}) = trsen!('N', 'V', select, T, Q) # .. Scalar Arguments .. # LOGICAL WANTQ, WANTZ # INTEGER IJOB, INFO, LDA, LDB, LDQ, LDZ, LIWORK, LWORK, # $ M, N # DOUBLE PRECISION PL, PR # .. # .. Array Arguments .. # LOGICAL SELECT( * ) # INTEGER IWORK( * ) # DOUBLE PRECISION DIF( * ) # COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), # $ BETA( * ), Q( LDQ, * ), WORK( * ), Z( LDZ, * ) # .. function tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix{$elty}, T::AbstractMatrix{$elty}, Q::AbstractMatrix{$elty}, Z::AbstractMatrix{$elty}) chkstride1(select, S, T, Q, Z) n, nt, nq, nz = checksquare(S, T, Q, Z) if n != nt throw(DimensionMismatch("dimensions of S, ($n,$n), and T, ($nt,$nt), must match")) end if n != nq throw(DimensionMismatch("dimensions of S, ($n,$n), and Q, ($nq,$nq), must match")) end if n != nz throw(DimensionMismatch("dimensions of S, ($n,$n), and Z, ($nz,$nz), must match")) end lds = max(1, stride(S, 2)) ldt = max(1, stride(T, 2)) ldq = max(1, stride(Q, 2)) ldz = max(1, stride(Z, 2)) m = sum(select) alpha = similar(T, $elty, n) beta = similar(T, $elty, n) lwork = BlasInt(-1) work = Vector{$elty}(undef, 1) liwork = BlasInt(-1) iwork = Vector{BlasInt}(undef, 1) info = Ref{BlasInt}() select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), libblastrampoline), Cvoid, (Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), 0, 1, 1, select, n, S, lds, T, ldt, alpha, beta, Q, ldq, Z, ldz, m, C_NULL, C_NULL, C_NULL, work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork lwork = BlasInt(real(work[1])) resize!(work, lwork) liwork = BlasInt(real(iwork[1])) resize!(iwork, liwork) end end S, T, alpha, beta, Q, Z end end end """ trexc!(compq, ifst, ilst, T, Q) -> (T, Q) trexc!(ifst, ilst, T, Q) -> (T, Q) Reorder the Schur factorization `T` of a matrix, such that the diagonal block of `T` with row index `ifst` is moved to row index `ilst`. If `compq = V`, the Schur vectors `Q` are reordered. If `compq = N` they are not modified. The 4-arg method calls the 5-arg method with `compq = V`. """ trexc!(compq::AbstractChar, ifst::BlasInt, ilst::BlasInt, T::AbstractMatrix, Q::AbstractMatrix) """ trsen!(job, compq, select, T, Q) -> (T, Q, w, s, sep) trsen!(select, T, Q) -> (T, Q, w, s, sep) Reorder the Schur factorization of a matrix and optionally finds reciprocal condition numbers. If `job = N`, no condition numbers are found. If `job = E`, only the condition number for this cluster of eigenvalues is found. If `job = V`, only the condition number for the invariant subspace is found. If `job = B` then the condition numbers for the cluster and subspace are found. If `compq = V` the Schur vectors `Q` are updated. If `compq = N` the Schur vectors are not modified. `select` determines which eigenvalues are in the cluster. The 3-arg method calls the 5-arg method with `job = N` and `compq = V`. Returns `T`, `Q`, reordered eigenvalues in `w`, the condition number of the cluster of eigenvalues `s`, and the condition number of the invariant subspace `sep`. """ trsen!(compq::AbstractChar, job::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix, Q::AbstractMatrix) """ tgsen!(select, S, T, Q, Z) -> (S, T, alpha, beta, Q, Z) Reorders the vectors of a generalized Schur decomposition. `select` specifies the eigenvalues in each cluster. """ tgsen!(select::AbstractVector{BlasInt}, S::AbstractMatrix, T::AbstractMatrix, Q::AbstractMatrix, Z::AbstractMatrix) for (fn, elty, relty) in ((:dtrsyl_, :Float64, :Float64), (:strsyl_, :Float32, :Float32), (:ztrsyl_, :ComplexF64, :Float64), (:ctrsyl_, :ComplexF32, :Float32)) @eval begin function trsyl!(transa::AbstractChar, transb::AbstractChar, A::AbstractMatrix{$elty}, B::AbstractMatrix{$elty}, C::AbstractMatrix{$elty}, isgn::Int=1) require_one_based_indexing(A, B, C) chkstride1(A, B, C) m, n = checksquare(A), checksquare(B) lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) m1, n1 = size(C) if m != m1 || n != n1 throw(DimensionMismatch("dimensions of A, ($m,$n), and C, ($m1,$n1), must match")) end ldc = max(1, stride(C, 2)) scale = Ref{$relty}() info = Ref{BlasInt}() ccall((@blasfunc($fn), libblastrampoline), Cvoid, (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, Clong, Clong), transa, transb, isgn, m, n, A, lda, B, ldb, C, ldc, scale, info, 1, 1) chklapackerror(info[]) C, scale[] end end end """ trsyl!(transa, transb, A, B, C, isgn=1) -> (C, scale) Solves the Sylvester matrix equation `A * X +/- X * B = scale*C` where `A` and `B` are both quasi-upper triangular. If `transa = N`, `A` is not modified. If `transa = T`, `A` is transposed. If `transa = C`, `A` is conjugate transposed. Similarly for `transb` and `B`. If `isgn = 1`, the equation `A * X + X * B = scale * C` is solved. If `isgn = -1`, the equation `A * X - X * B = scale * C` is solved. Returns `X` (overwriting `C`) and `scale`. """ trsyl!(transa::AbstractChar, transb::AbstractChar, A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix, isgn::Int=1) end # module v/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/dense.jlว# This file is a part of Julia. License is MIT: https://julialang.org/license # Linear algebra functions for dense matrices in column major format ## BLAS cutoff threshold constants #TODO const DOT_CUTOFF = 128 const ASUM_CUTOFF = 32 const NRM2_CUTOFF = 32 # Generic cross-over constant based on benchmarking on a single thread with an i7 CPU @ 2.5GHz # L1 cache: 32K, L2 cache: 256K, L3 cache: 6144K # This constant should ideally be determined by the actual CPU cache size const ISONE_CUTOFF = 2^21 # 2M function isone(A::AbstractMatrix) m, n = size(A) m != n && return false # only square matrices can satisfy x == one(x) if sizeof(A) < ISONE_CUTOFF _isone_triacheck(A, m) else _isone_cachefriendly(A, m) end end @inline function _isone_triacheck(A::AbstractMatrix, m::Int) @inbounds for i in 1:m, j in i:m if i == j isone(A[i,i]) || return false else iszero(A[i,j]) && iszero(A[j,i]) || return false end end return true end # Inner loop over rows to be friendly to the CPU cache @inline function _isone_cachefriendly(A::AbstractMatrix, m::Int) @inbounds for i in 1:m, j in 1:m if i == j isone(A[i,i]) || return false else iszero(A[j,i]) || return false end end return true end """ isposdef!(A) -> Bool Test whether a matrix is positive definite (and Hermitian) by trying to perform a Cholesky factorization of `A`, overwriting `A` in the process. See also [`isposdef`](@ref). # Examples ```jldoctest julia> A = [1. 2.; 2. 50.]; julia> isposdef!(A) true julia> A 2ร—2 Matrix{Float64}: 1.0 2.0 2.0 6.78233 ``` """ isposdef!(A::AbstractMatrix) = ishermitian(A) && isposdef(cholesky!(Hermitian(A); check = false)) """ isposdef(A) -> Bool Test whether a matrix is positive definite (and Hermitian) by trying to perform a Cholesky factorization of `A`. See also [`isposdef!`](@ref), [`cholesky`](@ref). # Examples ```jldoctest julia> A = [1 2; 2 50] 2ร—2 Matrix{Int64}: 1 2 2 50 julia> isposdef(A) true ``` """ isposdef(A::AbstractMatrix) = ishermitian(A) && isposdef(cholesky(Hermitian(A); check = false)) isposdef(x::Number) = imag(x)==0 && real(x) > 0 function norm(x::StridedVector{T}, rx::Union{UnitRange{TI},AbstractRange{TI}}) where {T<:BlasFloat,TI<:Integer} if minimum(rx) < 1 || maximum(rx) > length(x) throw(BoundsError(x, rx)) end GC.@preserve x BLAS.nrm2(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx)) end norm1(x::Union{Array{T},StridedVector{T}}) where {T<:BlasReal} = length(x) < ASUM_CUTOFF ? generic_norm1(x) : BLAS.asum(x) norm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} = length(x) < NRM2_CUTOFF ? generic_norm2(x) : BLAS.nrm2(x) """ triu!(M, k::Integer) Return the upper triangle of `M` starting from the `k`th superdiagonal, overwriting `M` in the process. # Examples ```jldoctest julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5] 5ร—5 Matrix{Int64}: 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 julia> triu!(M, 1) 5ร—5 Matrix{Int64}: 0 2 3 4 5 0 0 3 4 5 0 0 0 4 5 0 0 0 0 5 0 0 0 0 0 ``` """ function triu!(M::AbstractMatrix, k::Integer) require_one_based_indexing(M) m, n = size(M) for j in 1:min(n, m + k) for i in max(1, j - k + 1):m M[i,j] = zero(M[i,j]) end end M end triu(M::Matrix, k::Integer) = triu!(copy(M), k) """ tril!(M, k::Integer) Return the lower triangle of `M` starting from the `k`th superdiagonal, overwriting `M` in the process. # Examples ```jldoctest julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5] 5ร—5 Matrix{Int64}: 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 julia> tril!(M, 2) 5ร—5 Matrix{Int64}: 1 2 3 0 0 1 2 3 4 0 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 ``` """ function tril!(M::AbstractMatrix, k::Integer) require_one_based_indexing(M) m, n = size(M) for j in max(1, k + 1):n @inbounds for i in 1:min(j - k - 1, m) M[i,j] = zero(M[i,j]) end end M end tril(M::Matrix, k::Integer) = tril!(copy(M), k) """ fillband!(A::AbstractMatrix, x, l, u) Fill the band between diagonals `l` and `u` with the value `x`. """ function fillband!(A::AbstractMatrix{T}, x, l, u) where T require_one_based_indexing(A) m, n = size(A) xT = convert(T, x) for j in 1:n for i in max(1,j-u):min(m,j-l) @inbounds A[i, j] = xT end end return A end diagind(m::Integer, n::Integer, k::Integer=0) = k <= 0 ? range(1-k, step=m+1, length=min(m+k, n)) : range(k*m+1, step=m+1, length=min(m, n-k)) """ diagind(M, k::Integer=0) An `AbstractRange` giving the indices of the `k`th diagonal of the matrix `M`. See also: [`diag`](@ref), [`diagm`](@ref), [`Diagonal`](@ref). # Examples ```jldoctest julia> A = [1 2 3; 4 5 6; 7 8 9] 3ร—3 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 julia> diagind(A,-1) 2:4:6 ``` """ function diagind(A::AbstractMatrix, k::Integer=0) require_one_based_indexing(A) diagind(size(A,1), size(A,2), k) end """ diag(M, k::Integer=0) The `k`th diagonal of a matrix, as a vector. See also [`diagm`](@ref), [`diagind`](@ref), [`Diagonal`](@ref), [`isdiag`](@ref). # Examples ```jldoctest julia> A = [1 2 3; 4 5 6; 7 8 9] 3ร—3 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 julia> diag(A,1) 2-element Vector{Int64}: 2 6 ``` """ diag(A::AbstractMatrix, k::Integer=0) = A[diagind(A,k)] """ diagm(kv::Pair{<:Integer,<:AbstractVector}...) diagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) Construct a matrix from `Pair`s of diagonals and vectors. Vector `kv.second` will be placed on the `kv.first` diagonal. By default the matrix is square and its size is inferred from `kv`, but a non-square size `m`ร—`n` (padded with zeros as needed) can be specified by passing `m,n` as the first arguments. For repeated diagonal indices `kv.first` the values in the corresponding vectors `kv.second` will be added. `diagm` constructs a full matrix; if you want storage-efficient versions with fast arithmetic, see [`Diagonal`](@ref), [`Bidiagonal`](@ref) [`Tridiagonal`](@ref) and [`SymTridiagonal`](@ref). # Examples ```jldoctest julia> diagm(1 => [1,2,3]) 4ร—4 Matrix{Int64}: 0 1 0 0 0 0 2 0 0 0 0 3 0 0 0 0 julia> diagm(1 => [1,2,3], -1 => [4,5]) 4ร—4 Matrix{Int64}: 0 1 0 0 4 0 2 0 0 5 0 3 0 0 0 0 julia> diagm(1 => [1,2,3], 1 => [1,2,3]) 4ร—4 Matrix{Int64}: 0 2 0 0 0 0 4 0 0 0 0 6 0 0 0 0 ``` """ diagm(kv::Pair{<:Integer,<:AbstractVector}...) = _diagm(nothing, kv...) diagm(m::Integer, n::Integer, kv::Pair{<:Integer,<:AbstractVector}...) = _diagm((Int(m),Int(n)), kv...) function _diagm(size, kv::Pair{<:Integer,<:AbstractVector}...) A = diagm_container(size, kv...) for p in kv inds = diagind(A, p.first) for (i, val) in enumerate(p.second) A[inds[i]] += val end end return A end function diagm_size(size::Nothing, kv::Pair{<:Integer,<:AbstractVector}...) mnmax = mapreduce(x -> length(x.second) + abs(Int(x.first)), max, kv; init=0) return mnmax, mnmax end function diagm_size(size::Tuple{Int,Int}, kv::Pair{<:Integer,<:AbstractVector}...) mmax = mapreduce(x -> length(x.second) - min(0,Int(x.first)), max, kv; init=0) nmax = mapreduce(x -> length(x.second) + max(0,Int(x.first)), max, kv; init=0) m, n = size (m โ‰ฅ mmax && n โ‰ฅ nmax) || throw(DimensionMismatch("invalid size=$size")) return m, n end function diagm_container(size, kv::Pair{<:Integer,<:AbstractVector}...) T = promote_type(map(x -> eltype(x.second), kv)...) # For some type `T`, `zero(T)` is not a `T` and `zeros(T, ...)` fails. U = promote_type(T, typeof(zero(T))) return zeros(U, diagm_size(size, kv...)...) end diagm_container(size, kv::Pair{<:Integer,<:BitVector}...) = falses(diagm_size(size, kv...)...) """ diagm(v::AbstractVector) diagm(m::Integer, n::Integer, v::AbstractVector) Construct a matrix with elements of the vector as diagonal elements. By default, the matrix is square and its size is given by `length(v)`, but a non-square size `m`ร—`n` can be specified by passing `m,n` as the first arguments. # Examples ```jldoctest julia> diagm([1,2,3]) 3ร—3 Matrix{Int64}: 1 0 0 0 2 0 0 0 3 ``` """ diagm(v::AbstractVector) = diagm(0 => v) diagm(m::Integer, n::Integer, v::AbstractVector) = diagm(m, n, 0 => v) function tr(A::Matrix{T}) where T n = checksquare(A) t = zero(T) @inbounds @simd for i in 1:n t += A[i,i] end t end _kronsize(A::AbstractMatrix, B::AbstractMatrix) = map(*, size(A), size(B)) _kronsize(A::AbstractMatrix, B::AbstractVector) = (size(A, 1)*length(B), size(A, 2)) _kronsize(A::AbstractVector, B::AbstractMatrix) = (length(A)*size(B, 1), size(B, 2)) """ kron!(C, A, B) Computes the Kronecker product of `A` and `B` and stores the result in `C`, overwriting the existing content of `C`. This is the in-place version of [`kron`](@ref). !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ function kron!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMat) size(C) == _kronsize(A, B) || throw(DimensionMismatch("kron!")) _kron!(C, A, B) end function kron!(c::AbstractVector, a::AbstractVector, b::AbstractVector) length(c) == length(a) * length(b) || throw(DimensionMismatch("kron!")) m = firstindex(c) @inbounds for i in eachindex(a) ai = a[i] for k in eachindex(b) c[m] = ai*b[k] m += 1 end end return c end kron!(c::AbstractVecOrMat, a::AbstractVecOrMat, b::Number) = mul!(c, a, b) kron!(c::AbstractVecOrMat, a::Number, b::AbstractVecOrMat) = mul!(c, a, b) function _kron!(C, A::AbstractMatrix, B::AbstractMatrix) m = firstindex(C) @inbounds for j in axes(A,2), l in axes(B,2), i in axes(A,1) Aij = A[i,j] for k in axes(B,1) C[m] = Aij*B[k,l] m += 1 end end return C end function _kron!(C, A::AbstractMatrix, b::AbstractVector) m = firstindex(C) @inbounds for j in axes(A,2), i in axes(A,1) Aij = A[i,j] for k in eachindex(b) C[m] = Aij*b[k] m += 1 end end return C end function _kron!(C, a::AbstractVector, B::AbstractMatrix) m = firstindex(C) @inbounds for l in axes(B,2), i in eachindex(a) ai = a[i] for k in axes(B,1) C[m] = ai*B[k,l] m += 1 end end return C end """ kron(A, B) Computes the Kronecker product of two vectors, matrices or numbers. For real vectors `v` and `w`, the Kronecker product is related to the outer product by `kron(v,w) == vec(w * transpose(v))` or `w * transpose(v) == reshape(kron(v,w), (length(w), length(v)))`. Note how the ordering of `v` and `w` differs on the left and right of these expressions (due to column-major storage). For complex vectors, the outer product `w * v'` also differs by conjugation of `v`. # Examples ```jldoctest julia> A = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> B = [im 1; 1 -im] 2ร—2 Matrix{Complex{Int64}}: 0+1im 1+0im 1+0im 0-1im julia> kron(A, B) 4ร—4 Matrix{Complex{Int64}}: 0+1im 1+0im 0+2im 2+0im 1+0im 0-1im 2+0im 0-2im 0+3im 3+0im 0+4im 4+0im 3+0im 0-3im 4+0im 0-4im julia> v = [1, 2]; w = [3, 4, 5]; julia> w*transpose(v) 3ร—2 Matrix{Int64}: 3 6 4 8 5 10 julia> reshape(kron(v,w), (length(w), length(v))) 3ร—2 Matrix{Int64}: 3 6 4 8 5 10 ``` """ function kron(A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}) where {T,S} R = Matrix{promote_op(*,T,S)}(undef, _kronsize(A, B)) return kron!(R, A, B) end function kron(a::AbstractVector{T}, b::AbstractVector{S}) where {T,S} c = Vector{promote_op(*,T,S)}(undef, length(a)*length(b)) return kron!(c, a, b) end kron(a::Number, b::Union{Number, AbstractVecOrMat}) = a * b kron(a::AbstractVecOrMat, b::Number) = a * b kron(a::AdjointAbsVec, b::AdjointAbsVec) = adjoint(kron(adjoint(a), adjoint(b))) kron(a::AdjOrTransAbsVec, b::AdjOrTransAbsVec) = transpose(kron(transpose(a), transpose(b))) # Matrix power (^)(A::AbstractMatrix, p::Integer) = p < 0 ? power_by_squaring(inv(A), -p) : power_by_squaring(A, p) function (^)(A::AbstractMatrix{T}, p::Integer) where T<:Integer # make sure that e.g. [1 1;1 0]^big(3) # gets promotes in a similar way as 2^big(3) TT = promote_op(^, T, typeof(p)) return power_by_squaring(convert(AbstractMatrix{TT}, A), p) end function integerpow(A::AbstractMatrix{T}, p) where T TT = promote_op(^, T, typeof(p)) return (TT == T ? A : convert(AbstractMatrix{TT}, A))^Integer(p) end function schurpow(A::AbstractMatrix, p) if istriu(A) # Integer part retmat = A ^ floor(p) # Real part if p - floor(p) == 0.5 # special case: A^0.5 === sqrt(A) retmat = retmat * sqrt(A) else retmat = retmat * powm!(UpperTriangular(float.(A)), real(p - floor(p))) end else S,Q,d = Schur{Complex}(schur(A)) # Integer part R = S ^ floor(p) # Real part if p - floor(p) == 0.5 # special case: A^0.5 === sqrt(A) R = R * sqrt(S) else R = R * powm!(UpperTriangular(float.(S)), real(p - floor(p))) end retmat = Q * R * Q' end # if A has nonpositive real eigenvalues, retmat is a nonprincipal matrix power. if isreal(retmat) return real(retmat) else return retmat end end function (^)(A::AbstractMatrix{T}, p::Real) where T n = checksquare(A) # Quicker return if A is diagonal if isdiag(A) TT = promote_op(^, T, typeof(p)) retmat = copymutable_oftype(A, TT) for i in 1:n retmat[i, i] = retmat[i, i] ^ p end return retmat end # For integer powers, use power_by_squaring isinteger(p) && return integerpow(A, p) # If possible, use diagonalization if issymmetric(A) return (Symmetric(A)^p) end if ishermitian(A) return (Hermitian(A)^p) end # Otherwise, use Schur decomposition return schurpow(A, p) end """ ^(A::AbstractMatrix, p::Number) Matrix power, equivalent to ``\\exp(p\\log(A))`` # Examples ```jldoctest julia> [1 2; 0 3]^3 2ร—2 Matrix{Int64}: 1 26 0 27 ``` """ (^)(A::AbstractMatrix, p::Number) = exp(p*log(A)) # Matrix exponential """ exp(A::AbstractMatrix) Compute the matrix exponential of `A`, defined by ```math e^A = \\sum_{n=0}^{\\infty} \\frac{A^n}{n!}. ``` For symmetric or Hermitian `A`, an eigendecomposition ([`eigen`](@ref)) is used, otherwise the scaling and squaring algorithm (see [^H05]) is chosen. [^H05]: Nicholas J. Higham, "The squaring and scaling method for the matrix exponential revisited", SIAM Journal on Matrix Analysis and Applications, 26(4), 2005, 1179-1193. [doi:10.1137/090768539](https://doi.org/10.1137/090768539) # Examples ```jldoctest julia> A = Matrix(1.0I, 2, 2) 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 1.0 julia> exp(A) 2ร—2 Matrix{Float64}: 2.71828 0.0 0.0 2.71828 ``` """ exp(A::AbstractMatrix) = exp!(copy_similar(A, eigtype(eltype(A)))) exp(A::AdjointAbsMat) = adjoint(exp(parent(A))) exp(A::TransposeAbsMat) = transpose(exp(parent(A))) """ cis(A::AbstractMatrix) More efficient method for `exp(im*A)` of square matrix `A` (especially if `A` is `Hermitian` or real-`Symmetric`). See also [`cispi`](@ref), [`sincos`](@ref), [`exp`](@ref). !!! compat "Julia 1.7" Support for using `cis` with matrices was added in Julia 1.7. # Examples ```jldoctest julia> cis([ฯ€ 0; 0 ฯ€]) โ‰ˆ -I true ``` """ cis(A::AbstractMatrix) = exp(im * A) # fallback cis(A::AbstractMatrix{<:Base.HWNumber}) = exp_maybe_inplace(float.(im .* A)) exp_maybe_inplace(A::StridedMatrix{<:Union{ComplexF32, ComplexF64}}) = exp!(A) exp_maybe_inplace(A) = exp(A) """ ^(b::Number, A::AbstractMatrix) Matrix exponential, equivalent to ``\\exp(\\log(b)A)``. !!! compat "Julia 1.1" Support for raising `Irrational` numbers (like `โ„ฏ`) to a matrix was added in Julia 1.1. # Examples ```jldoctest julia> 2^[1 2; 0 3] 2ร—2 Matrix{Float64}: 2.0 6.0 0.0 8.0 julia> โ„ฏ^[1 2; 0 3] 2ร—2 Matrix{Float64}: 2.71828 17.3673 0.0 20.0855 ``` """ Base.:^(b::Number, A::AbstractMatrix) = exp!(log(b)*A) # method for โ„ฏ to explicitly elide the log(b) multiplication Base.:^(::Irrational{:โ„ฏ}, A::AbstractMatrix) = exp(A) ## Destructive matrix exponential using algorithm from Higham, 2008, ## "Functions of Matrices: Theory and Computation", SIAM function exp!(A::StridedMatrix{T}) where T<:BlasFloat n = checksquare(A) if ishermitian(A) return copytri!(parent(exp(Hermitian(A))), 'U', true) end ilo, ihi, scale = LAPACK.gebal!('B', A) # modifies A nA = opnorm(A, 1) ## For sufficiently small nA, use lower order Padรฉ-Approximations if (nA <= 2.1) if nA > 0.95 C = T[17643225600.,8821612800.,2075673600.,302702400., 30270240., 2162160., 110880., 3960., 90., 1.] elseif nA > 0.25 C = T[17297280.,8648640.,1995840.,277200., 25200., 1512., 56., 1.] elseif nA > 0.015 C = T[30240.,15120.,3360., 420., 30., 1.] else C = T[120.,60.,12.,1.] end A2 = A * A # Compute U and V: Even/odd terms in Padรฉ numerator & denom # Expansion of k=1 in for loop P = A2 U = mul!(C[4]*P, true, C[2]*I, true, true) #U = C[2]*I + C[4]*P V = mul!(C[3]*P, true, C[1]*I, true, true) #V = C[1]*I + C[3]*P for k in 2:(div(length(C), 2) - 1) P *= A2 mul!(U, C[2k + 2], P, true, true) # U += C[2k+2]*P mul!(V, C[2k + 1], P, true, true) # V += C[2k+1]*P end U = A * U # Padรฉ approximant: (V-U)\(V+U) tmp1, tmp2 = A, A2 # Reuse already allocated arrays tmp1 .= V .- U tmp2 .= V .+ U X = LAPACK.gesv!(tmp1, tmp2)[1] else s = log2(nA/5.4) # power of 2 later reversed by squaring if s > 0 si = ceil(Int,s) A ./= convert(T,2^si) end CC = T[64764752532480000.,32382376266240000.,7771770303897600., 1187353796428800., 129060195264000., 10559470521600., 670442572800., 33522128640., 1323241920., 40840800., 960960., 16380., 182., 1.] A2 = A * A A4 = A2 * A2 A6 = A2 * A4 tmp1, tmp2 = similar(A6), similar(A6) # Allocation economical version of: # U = A * (A6 * (CC[14].*A6 .+ CC[12].*A4 .+ CC[10].*A2) .+ # CC[8].*A6 .+ CC[6].*A4 .+ CC[4]*A2+CC[2]*I) tmp1 .= CC[14].*A6 .+ CC[12].*A4 .+ CC[10].*A2 tmp2 .= CC[8].*A6 .+ CC[6].*A4 .+ CC[4].*A2 mul!(tmp2, true,CC[2]*I, true, true) # tmp2 .+= CC[2]*I U = mul!(tmp2, A6, tmp1, true, true) U, tmp1 = mul!(tmp1, A, U), A # U = A * U0 # Allocation economical version of: # V = A6 * (CC[13].*A6 .+ CC[11].*A4 .+ CC[9].*A2) .+ # CC[7].*A6 .+ CC[5].*A4 .+ CC[3]*A2 .+ CC[1]*I tmp1 .= CC[13].*A6 .+ CC[11].*A4 .+ CC[9].*A2 tmp2 .= CC[7].*A6 .+ CC[5].*A4 .+ CC[3].*A2 mul!(tmp2, true, CC[1]*I, true, true) # tmp2 .+= CC[1]*I V = mul!(tmp2, A6, tmp1, true, true) tmp1 .= V .+ U tmp2 .= V .- U # tmp2 already contained V but this seems more readable X = LAPACK.gesv!(tmp2, tmp1)[1] # X now contains r_13 in Higham 2008 if s > 0 # Repeated squaring to compute X = r_13^(2^si) for t=1:si mul!(tmp2, X, X) X, tmp2 = tmp2, X end end end # Undo the balancing for j = ilo:ihi scj = scale[j] for i = 1:n X[j,i] *= scj end for i = 1:n X[i,j] /= scj end end if ilo > 1 # apply lower permutations in reverse order for j in (ilo-1):-1:1; rcswap!(j, Int(scale[j]), X) end end if ihi < n # apply upper permutations in forward order for j in (ihi+1):n; rcswap!(j, Int(scale[j]), X) end end X end ## Swap rows i and j and columns i and j in X function rcswap!(i::Integer, j::Integer, X::AbstractMatrix{<:Number}) for k = 1:size(X,1) X[k,i], X[k,j] = X[k,j], X[k,i] end for k = 1:size(X,2) X[i,k], X[j,k] = X[j,k], X[i,k] end end """ log(A::AbstractMatrix) If `A` has no negative real eigenvalue, compute the principal matrix logarithm of `A`, i.e. the unique matrix ``X`` such that ``e^X = A`` and ``-\\pi < Im(\\lambda) < \\pi`` for all the eigenvalues ``\\lambda`` of ``X``. If `A` has nonpositive eigenvalues, a nonprincipal matrix function is returned whenever possible. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used, if `A` is triangular an improved version of the inverse scaling and squaring method is employed (see [^AH12] and [^AHR13]). If `A` is real with no negative eigenvalues, then the real Schur form is computed. Otherwise, the complex Schur form is computed. Then the upper (quasi-)triangular algorithm in [^AHR13] is used on the upper (quasi-)triangular factor. [^AH12]: Awad H. Al-Mohy and Nicholas J. Higham, "Improved inverse scaling and squaring algorithms for the matrix logarithm", SIAM Journal on Scientific Computing, 34(4), 2012, C153-C169. [doi:10.1137/110852553](https://doi.org/10.1137/110852553) [^AHR13]: Awad H. Al-Mohy, Nicholas J. Higham and Samuel D. Relton, "Computing the Frรฉchet derivative of the matrix logarithm and estimating the condition number", SIAM Journal on Scientific Computing, 35(4), 2013, C394-C410. [doi:10.1137/120885991](https://doi.org/10.1137/120885991) # Examples ```jldoctest julia> A = Matrix(2.7182818*I, 2, 2) 2ร—2 Matrix{Float64}: 2.71828 0.0 0.0 2.71828 julia> log(A) 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 1.0 ``` """ function log(A::AbstractMatrix) # If possible, use diagonalization if ishermitian(A) logHermA = log(Hermitian(A)) return ishermitian(logHermA) ? copytri!(parent(logHermA), 'U', true) : parent(logHermA) elseif istriu(A) return triu!(parent(log(UpperTriangular(A)))) elseif isreal(A) SchurF = schur(real(A)) if istriu(SchurF.T) logA = SchurF.Z * log(UpperTriangular(SchurF.T)) * SchurF.Z' else # real log exists whenever all eigenvalues are positive is_log_real = !any(x -> isreal(x) && real(x) โ‰ค 0, SchurF.values) if is_log_real logA = SchurF.Z * log_quasitriu(SchurF.T) * SchurF.Z' else SchurS = Schur{Complex}(SchurF) logA = SchurS.Z * log(UpperTriangular(SchurS.T)) * SchurS.Z' end end return eltype(A) <: Complex ? complex(logA) : logA else SchurF = schur(A) return SchurF.vectors * log(UpperTriangular(SchurF.T)) * SchurF.vectors' end end log(A::AdjointAbsMat) = adjoint(log(parent(A))) log(A::TransposeAbsMat) = transpose(log(parent(A))) """ sqrt(A::AbstractMatrix) If `A` has no negative real eigenvalues, compute the principal matrix square root of `A`, that is the unique matrix ``X`` with eigenvalues having positive real part such that ``X^2 = A``. Otherwise, a nonprincipal square root is returned. If `A` is real-symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the square root. For such matrices, eigenvalues ฮป that appear to be slightly negative due to roundoff errors are treated as if they were zero. More precisely, matrices with all eigenvalues `โ‰ฅ -rtol*(max |ฮป|)` are treated as semidefinite (yielding a Hermitian square root), with negative eigenvalues taken to be zero. `rtol` is a keyword argument to `sqrt` (in the Hermitian/real-symmetric case only) that defaults to machine precision scaled by `size(A,1)`. Otherwise, the square root is determined by means of the Bjรถrck-Hammarling method [^BH83], which computes the complex Schur form ([`schur`](@ref)) and then the complex square root of the triangular factor. If a real square root exists, then an extension of this method [^H87] that computes the real Schur form and then the real square root of the quasi-triangular factor is instead used. [^BH83]: ร…ke Bjรถrck and Sven Hammarling, "A Schur method for the square root of a matrix", Linear Algebra and its Applications, 52-53, 1983, 127-140. [doi:10.1016/0024-3795(83)80010-X](https://doi.org/10.1016/0024-3795(83)80010-X) [^H87]: Nicholas J. Higham, "Computing real square roots of a real matrix", Linear Algebra and its Applications, 88-89, 1987, 405-430. [doi:10.1016/0024-3795(87)90118-2](https://doi.org/10.1016/0024-3795(87)90118-2) # Examples ```jldoctest julia> A = [4 0; 0 4] 2ร—2 Matrix{Int64}: 4 0 0 4 julia> sqrt(A) 2ร—2 Matrix{Float64}: 2.0 0.0 0.0 2.0 ``` """ sqrt(::AbstractMatrix) function sqrt(A::AbstractMatrix{T}) where {T<:Union{Real,Complex}} if checksquare(A) == 0 return copy(A) elseif ishermitian(A) sqrtHermA = sqrt(Hermitian(A)) return ishermitian(sqrtHermA) ? copytri!(parent(sqrtHermA), 'U', true) : parent(sqrtHermA) elseif istriu(A) return triu!(parent(sqrt(UpperTriangular(A)))) elseif isreal(A) SchurF = schur(real(A)) if istriu(SchurF.T) sqrtA = SchurF.Z * sqrt(UpperTriangular(SchurF.T)) * SchurF.Z' else # real sqrt exists whenever no eigenvalues are negative is_sqrt_real = !any(x -> isreal(x) && real(x) < 0, SchurF.values) # sqrt_quasitriu uses LAPACK functions for non-triu inputs if typeof(sqrt(zero(T))) <: BlasFloat && is_sqrt_real sqrtA = SchurF.Z * sqrt_quasitriu(SchurF.T) * SchurF.Z' else SchurS = Schur{Complex}(SchurF) sqrtA = SchurS.Z * sqrt(UpperTriangular(SchurS.T)) * SchurS.Z' end end return eltype(A) <: Complex ? complex(sqrtA) : sqrtA else SchurF = schur(A) return SchurF.vectors * sqrt(UpperTriangular(SchurF.T)) * SchurF.vectors' end end sqrt(A::AdjointAbsMat) = adjoint(sqrt(parent(A))) sqrt(A::TransposeAbsMat) = transpose(sqrt(parent(A))) function inv(A::StridedMatrix{T}) where T checksquare(A) if istriu(A) Ai = triu!(parent(inv(UpperTriangular(A)))) elseif istril(A) Ai = tril!(parent(inv(LowerTriangular(A)))) else Ai = inv!(lu(A)) Ai = convert(typeof(parent(Ai)), Ai) end return Ai end """ cos(A::AbstractMatrix) Compute the matrix cosine of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the cosine. Otherwise, the cosine is determined by calling [`exp`](@ref). # Examples ```jldoctest julia> cos(fill(1.0, (2,2))) 2ร—2 Matrix{Float64}: 0.291927 -0.708073 -0.708073 0.291927 ``` """ function cos(A::AbstractMatrix{<:Real}) if issymmetric(A) return copytri!(parent(cos(Symmetric(A))), 'U') end T = complex(float(eltype(A))) return real(exp!(T.(im .* A))) end function cos(A::AbstractMatrix{<:Complex}) if ishermitian(A) return copytri!(parent(cos(Hermitian(A))), 'U', true) end T = complex(float(eltype(A))) X = exp!(T.(im .* A)) @. X = (X + $exp!(T(-im*A))) / 2 return X end """ sin(A::AbstractMatrix) Compute the matrix sine of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the sine. Otherwise, the sine is determined by calling [`exp`](@ref). # Examples ```jldoctest julia> sin(fill(1.0, (2,2))) 2ร—2 Matrix{Float64}: 0.454649 0.454649 0.454649 0.454649 ``` """ function sin(A::AbstractMatrix{<:Real}) if issymmetric(A) return copytri!(parent(sin(Symmetric(A))), 'U') end T = complex(float(eltype(A))) return imag(exp!(T.(im .* A))) end function sin(A::AbstractMatrix{<:Complex}) if ishermitian(A) return copytri!(parent(sin(Hermitian(A))), 'U', true) end T = complex(float(eltype(A))) X = exp!(T.(im .* A)) Y = exp!(T.(.-im .* A)) @inbounds for i in eachindex(X) x, y = X[i]/2, Y[i]/2 X[i] = Complex(imag(x)-imag(y), real(y)-real(x)) end return X end """ sincos(A::AbstractMatrix) Compute the matrix sine and cosine of a square matrix `A`. # Examples ```jldoctest julia> S, C = sincos(fill(1.0, (2,2))); julia> S 2ร—2 Matrix{Float64}: 0.454649 0.454649 0.454649 0.454649 julia> C 2ร—2 Matrix{Float64}: 0.291927 -0.708073 -0.708073 0.291927 ``` """ function sincos(A::AbstractMatrix{<:Real}) if issymmetric(A) symsinA, symcosA = sincos(Symmetric(A)) sinA = copytri!(parent(symsinA), 'U') cosA = copytri!(parent(symcosA), 'U') return sinA, cosA end T = complex(float(eltype(A))) c, s = reim(exp!(T.(im .* A))) return s, c end function sincos(A::AbstractMatrix{<:Complex}) if ishermitian(A) hermsinA, hermcosA = sincos(Hermitian(A)) sinA = copytri!(parent(hermsinA), 'U', true) cosA = copytri!(parent(hermcosA), 'U', true) return sinA, cosA end T = complex(float(eltype(A))) X = exp!(T.(im .* A)) Y = exp!(T.(.-im .* A)) @inbounds for i in eachindex(X) x, y = X[i]/2, Y[i]/2 X[i] = Complex(imag(x)-imag(y), real(y)-real(x)) Y[i] = x+y end return X, Y end """ tan(A::AbstractMatrix) Compute the matrix tangent of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the tangent. Otherwise, the tangent is determined by calling [`exp`](@ref). # Examples ```jldoctest julia> tan(fill(1.0, (2,2))) 2ร—2 Matrix{Float64}: -1.09252 -1.09252 -1.09252 -1.09252 ``` """ function tan(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(tan(Hermitian(A))), 'U', true) end S, C = sincos(A) S /= C return S end """ cosh(A::AbstractMatrix) Compute the matrix hyperbolic cosine of a square matrix `A`. """ function cosh(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(cosh(Hermitian(A))), 'U', true) end X = exp(A) @. X = (X + $exp!(float(-A))) / 2 return X end """ sinh(A::AbstractMatrix) Compute the matrix hyperbolic sine of a square matrix `A`. """ function sinh(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(sinh(Hermitian(A))), 'U', true) end X = exp(A) @. X = (X - $exp!(float(-A))) / 2 return X end """ tanh(A::AbstractMatrix) Compute the matrix hyperbolic tangent of a square matrix `A`. """ function tanh(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(tanh(Hermitian(A))), 'U', true) end X = exp(A) Y = exp!(float.(.-A)) @inbounds for i in eachindex(X) x, y = X[i], Y[i] X[i] = x - y Y[i] = x + y end X /= Y return X end """ acos(A::AbstractMatrix) Compute the inverse matrix cosine of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the inverse cosine. Otherwise, the inverse cosine is determined by using [`log`](@ref) and [`sqrt`](@ref). For the theory and logarithmic formulas used to compute this function, see [^AH16_1]. [^AH16_1]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) # Examples ```julia-repl julia> acos(cos([0.5 0.1; -0.2 0.3])) 2ร—2 Matrix{ComplexF64}: 0.5-8.32667e-17im 0.1+0.0im -0.2+2.63678e-16im 0.3-3.46945e-16im ``` """ function acos(A::AbstractMatrix) if ishermitian(A) acosHermA = acos(Hermitian(A)) return isa(acosHermA, Hermitian) ? copytri!(parent(acosHermA), 'U', true) : parent(acosHermA) end SchurF = Schur{Complex}(schur(A)) U = UpperTriangular(SchurF.T) R = triu!(parent(-im * log(U + im * sqrt(I - U^2)))) return SchurF.Z * R * SchurF.Z' end """ asin(A::AbstractMatrix) Compute the inverse matrix sine of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the inverse sine. Otherwise, the inverse sine is determined by using [`log`](@ref) and [`sqrt`](@ref). For the theory and logarithmic formulas used to compute this function, see [^AH16_2]. [^AH16_2]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) # Examples ```julia-repl julia> asin(sin([0.5 0.1; -0.2 0.3])) 2ร—2 Matrix{ComplexF64}: 0.5-4.16334e-17im 0.1-5.55112e-17im -0.2+9.71445e-17im 0.3-1.249e-16im ``` """ function asin(A::AbstractMatrix) if ishermitian(A) asinHermA = asin(Hermitian(A)) return isa(asinHermA, Hermitian) ? copytri!(parent(asinHermA), 'U', true) : parent(asinHermA) end SchurF = Schur{Complex}(schur(A)) U = UpperTriangular(SchurF.T) R = triu!(parent(-im * log(im * U + sqrt(I - U^2)))) return SchurF.Z * R * SchurF.Z' end """ atan(A::AbstractMatrix) Compute the inverse matrix tangent of a square matrix `A`. If `A` is symmetric or Hermitian, its eigendecomposition ([`eigen`](@ref)) is used to compute the inverse tangent. Otherwise, the inverse tangent is determined by using [`log`](@ref). For the theory and logarithmic formulas used to compute this function, see [^AH16_3]. [^AH16_3]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) # Examples ```julia-repl julia> atan(tan([0.5 0.1; -0.2 0.3])) 2ร—2 Matrix{ComplexF64}: 0.5+1.38778e-17im 0.1-2.77556e-17im -0.2+6.93889e-17im 0.3-4.16334e-17im ``` """ function atan(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(atan(Hermitian(A))), 'U', true) end SchurF = Schur{Complex}(schur(A)) U = im * UpperTriangular(SchurF.T) R = triu!(parent(log((I + U) / (I - U)) / 2im)) return SchurF.Z * R * SchurF.Z' end """ acosh(A::AbstractMatrix) Compute the inverse hyperbolic matrix cosine of a square matrix `A`. For the theory and logarithmic formulas used to compute this function, see [^AH16_4]. [^AH16_4]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) """ function acosh(A::AbstractMatrix) if ishermitian(A) acoshHermA = acosh(Hermitian(A)) return isa(acoshHermA, Hermitian) ? copytri!(parent(acoshHermA), 'U', true) : parent(acoshHermA) end SchurF = Schur{Complex}(schur(A)) U = UpperTriangular(SchurF.T) R = triu!(parent(log(U + sqrt(U - I) * sqrt(U + I)))) return SchurF.Z * R * SchurF.Z' end """ asinh(A::AbstractMatrix) Compute the inverse hyperbolic matrix sine of a square matrix `A`. For the theory and logarithmic formulas used to compute this function, see [^AH16_5]. [^AH16_5]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) """ function asinh(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(asinh(Hermitian(A))), 'U', true) end SchurF = Schur{Complex}(schur(A)) U = UpperTriangular(SchurF.T) R = triu!(parent(log(U + sqrt(I + U^2)))) return SchurF.Z * R * SchurF.Z' end """ atanh(A::AbstractMatrix) Compute the inverse hyperbolic matrix tangent of a square matrix `A`. For the theory and logarithmic formulas used to compute this function, see [^AH16_6]. [^AH16_6]: Mary Aprahamian and Nicholas J. Higham, "Matrix Inverse Trigonometric and Inverse Hyperbolic Functions: Theory and Algorithms", MIMS EPrint: 2016.4. [https://doi.org/10.1137/16M1057577](https://doi.org/10.1137/16M1057577) """ function atanh(A::AbstractMatrix) if ishermitian(A) return copytri!(parent(atanh(Hermitian(A))), 'U', true) end SchurF = Schur{Complex}(schur(A)) U = UpperTriangular(SchurF.T) R = triu!(parent(log((I + U) / (I - U)) / 2)) return SchurF.Z * R * SchurF.Z' end for (finv, f, finvh, fh, fn) in ((:sec, :cos, :sech, :cosh, "secant"), (:csc, :sin, :csch, :sinh, "cosecant"), (:cot, :tan, :coth, :tanh, "cotangent")) name = string(finv) hname = string(finvh) @eval begin @doc """ $($name)(A::AbstractMatrix) Compute the matrix $($fn) of a square matrix `A`. """ ($finv)(A::AbstractMatrix{T}) where {T} = inv(($f)(A)) @doc """ $($hname)(A::AbstractMatrix) Compute the matrix hyperbolic $($fn) of square matrix `A`. """ ($finvh)(A::AbstractMatrix{T}) where {T} = inv(($fh)(A)) end end for (tfa, tfainv, hfa, hfainv, fn) in ((:asec, :acos, :asech, :acosh, "secant"), (:acsc, :asin, :acsch, :asinh, "cosecant"), (:acot, :atan, :acoth, :atanh, "cotangent")) tname = string(tfa) hname = string(hfa) @eval begin @doc """ $($tname)(A::AbstractMatrix) Compute the inverse matrix $($fn) of `A`. """ ($tfa)(A::AbstractMatrix{T}) where {T} = ($tfainv)(inv(A)) @doc """ $($hname)(A::AbstractMatrix) Compute the inverse matrix hyperbolic $($fn) of `A`. """ ($hfa)(A::AbstractMatrix{T}) where {T} = ($hfainv)(inv(A)) end end """ factorize(A) Compute a convenient factorization of `A`, based upon the type of the input matrix. `factorize` checks `A` to see if it is symmetric/triangular/etc. if `A` is passed as a generic matrix. `factorize` checks every element of `A` to verify/rule out each property. It will short-circuit as soon as it can rule out symmetry/triangular structure. The return value can be reused for efficient solving of multiple systems. For example: `A=factorize(A); x=A\\b; y=A\\C`. | Properties of `A` | type of factorization | |:---------------------------|:-----------------------------------------------| | Positive-definite | Cholesky (see [`cholesky`](@ref)) | | Dense Symmetric/Hermitian | Bunch-Kaufman (see [`bunchkaufman`](@ref)) | | Sparse Symmetric/Hermitian | LDLt (see [`ldlt`](@ref)) | | Triangular | Triangular | | Diagonal | Diagonal | | Bidiagonal | Bidiagonal | | Tridiagonal | LU (see [`lu`](@ref)) | | Symmetric real tridiagonal | LDLt (see [`ldlt`](@ref)) | | General square | LU (see [`lu`](@ref)) | | General non-square | QR (see [`qr`](@ref)) | If `factorize` is called on a Hermitian positive-definite matrix, for instance, then `factorize` will return a Cholesky factorization. # Examples ```jldoctest julia> A = Array(Bidiagonal(fill(1.0, (5, 5)), :U)) 5ร—5 Matrix{Float64}: 1.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 1.0 julia> factorize(A) # factorize will check to see that A is already factorized 5ร—5 Bidiagonal{Float64, Vector{Float64}}: 1.0 1.0 โ‹… โ‹… โ‹… โ‹… 1.0 1.0 โ‹… โ‹… โ‹… โ‹… 1.0 1.0 โ‹… โ‹… โ‹… โ‹… 1.0 1.0 โ‹… โ‹… โ‹… โ‹… 1.0 ``` This returns a `5ร—5 Bidiagonal{Float64}`, which can now be passed to other linear algebra functions (e.g. eigensolvers) which will use specialized methods for `Bidiagonal` types. """ function factorize(A::AbstractMatrix{T}) where T m, n = size(A) if m == n if m == 1 return A[1] end utri = true utri1 = true herm = true sym = true for j = 1:n-1, i = j+1:m if utri1 if A[i,j] != 0 utri1 = i == j + 1 utri = false end end if sym sym &= A[i,j] == A[j,i] end if herm herm &= A[i,j] == conj(A[j,i]) end if !(utri1|herm|sym) break end end ltri = true ltri1 = true for j = 3:n, i = 1:j-2 ltri1 &= A[i,j] == 0 if !ltri1 break end end if ltri1 for i = 1:n-1 if A[i,i+1] != 0 ltri &= false break end end if ltri if utri return Diagonal(A) end if utri1 return Bidiagonal(diag(A), diag(A, -1), :L) end return LowerTriangular(A) end if utri return Bidiagonal(diag(A), diag(A, 1), :U) end if utri1 # TODO: enable once a specialized, non-dense bunchkaufman method exists # if (herm & (T <: Complex)) | sym # return bunchkaufman(SymTridiagonal(diag(A), diag(A, -1))) # end return lu(Tridiagonal(diag(A, -1), diag(A), diag(A, 1))) end end if utri return UpperTriangular(A) end if herm cf = cholesky(A; check = false) if cf.info == 0 return cf else return factorize(Hermitian(A)) end end if sym return factorize(Symmetric(A)) end return lu(A) end qr(A, ColumnNorm()) end factorize(A::Adjoint) = adjoint(factorize(parent(A))) factorize(A::Transpose) = transpose(factorize(parent(A))) factorize(a::Number) = a # same as how factorize behaves on Diagonal types ## Moore-Penrose pseudoinverse """ pinv(M; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ฯต) pinv(M, rtol::Real) = pinv(M; rtol=rtol) # to be deprecated in Julia 2.0 Computes the Moore-Penrose pseudoinverse. For matrices `M` with floating point elements, it is convenient to compute the pseudoinverse by inverting only singular values greater than `max(atol, rtol*ฯƒโ‚)` where `ฯƒโ‚` is the largest singular value of `M`. The optimal choice of absolute (`atol`) and relative tolerance (`rtol`) varies both with the value of `M` and the intended application of the pseudoinverse. The default relative tolerance is `n*ฯต`, where `n` is the size of the smallest dimension of `M`, and `ฯต` is the [`eps`](@ref) of the element type of `M`. For inverting dense ill-conditioned matrices in a least-squares sense, `rtol = sqrt(eps(real(float(oneunit(eltype(M))))))` is recommended. For more information, see [^issue8859], [^B96], [^S84], [^KY88]. # Examples ```jldoctest julia> M = [1.5 1.3; 1.2 1.9] 2ร—2 Matrix{Float64}: 1.5 1.3 1.2 1.9 julia> N = pinv(M) 2ร—2 Matrix{Float64}: 1.47287 -1.00775 -0.930233 1.16279 julia> M * N 2ร—2 Matrix{Float64}: 1.0 -2.22045e-16 4.44089e-16 1.0 ``` [^issue8859]: Issue 8859, "Fix least squares", [https://github.com/JuliaLang/julia/pull/8859](https://github.com/JuliaLang/julia/pull/8859) [^B96]: ร…ke Bjรถrck, "Numerical Methods for Least Squares Problems", SIAM Press, Philadelphia, 1996, "Other Titles in Applied Mathematics", Vol. 51. [doi:10.1137/1.9781611971484](http://epubs.siam.org/doi/book/10.1137/1.9781611971484) [^S84]: G. W. Stewart, "Rank Degeneracy", SIAM Journal on Scientific and Statistical Computing, 5(2), 1984, 403-413. [doi:10.1137/0905030](http://epubs.siam.org/doi/abs/10.1137/0905030) [^KY88]: Konstantinos Konstantinides and Kung Yao, "Statistical analysis of effective singular values in matrix rank determination", IEEE Transactions on Acoustics, Speech and Signal Processing, 36(5), 1988, 757-763. [doi:10.1109/29.1585](https://doi.org/10.1109/29.1585) """ function pinv(A::AbstractMatrix{T}; atol::Real = 0.0, rtol::Real = (eps(real(float(oneunit(T))))*min(size(A)...))*iszero(atol)) where T m, n = size(A) Tout = typeof(zero(T)/sqrt(oneunit(T) + oneunit(T))) if m == 0 || n == 0 return similar(A, Tout, (n, m)) end if isdiag(A) indA = diagind(A) dA = view(A, indA) maxabsA = maximum(abs, dA) tol = max(rtol * maxabsA, atol) B = fill!(similar(A, Tout, (n, m)), 0) indB = diagind(B) B[indB] .= (x -> abs(x) > tol ? pinv(x) : zero(x)).(dA) return B end SVD = svd(A) tol = max(rtol*maximum(SVD.S), atol) Stype = eltype(SVD.S) Sinv = fill!(similar(A, Stype, length(SVD.S)), 0) index = SVD.S .> tol Sinv[index] .= pinv.(view(SVD.S, index)) return SVD.Vt' * (Diagonal(Sinv) * SVD.U') end function pinv(x::Number) xi = inv(x) return ifelse(isfinite(xi), xi, zero(xi)) end ## Basis for null space """ nullspace(M; atol::Real=0, rtol::Real=atol>0 ? 0 : n*ฯต) nullspace(M, rtol::Real) = nullspace(M; rtol=rtol) # to be deprecated in Julia 2.0 Computes a basis for the nullspace of `M` by including the singular vectors of `M` whose singular values have magnitudes smaller than `max(atol, rtol*ฯƒโ‚)`, where `ฯƒโ‚` is `M`'s largest singular value. By default, the relative tolerance `rtol` is `n*ฯต`, where `n` is the size of the smallest dimension of `M`, and `ฯต` is the [`eps`](@ref) of the element type of `M`. # Examples ```jldoctest julia> M = [1 0 0; 0 1 0; 0 0 0] 3ร—3 Matrix{Int64}: 1 0 0 0 1 0 0 0 0 julia> nullspace(M) 3ร—1 Matrix{Float64}: 0.0 0.0 1.0 julia> nullspace(M, rtol=3) 3ร—3 Matrix{Float64}: 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 julia> nullspace(M, atol=0.95) 3ร—1 Matrix{Float64}: 0.0 0.0 1.0 ``` """ function nullspace(A::AbstractVecOrMat; atol::Real = 0.0, rtol::Real = (min(size(A, 1), size(A, 2))*eps(real(float(oneunit(eltype(A))))))*iszero(atol)) m, n = size(A, 1), size(A, 2) (m == 0 || n == 0) && return Matrix{eigtype(eltype(A))}(I, n, n) SVD = svd(A; full=true) tol = max(atol, SVD.S[1]*rtol) indstart = sum(s -> s .> tol, SVD.S) + 1 return copy((@view SVD.Vt[indstart:end,:])') end """ cond(M, p::Real=2) Condition number of the matrix `M`, computed using the operator `p`-norm. Valid values for `p` are `1`, `2` (default), or `Inf`. """ function cond(A::AbstractMatrix, p::Real=2) if p == 2 v = svdvals(A) maxv = maximum(v) return iszero(maxv) ? oftype(real(maxv), Inf) : maxv / minimum(v) elseif p == 1 || p == Inf checksquare(A) try Ainv = inv(A) return opnorm(A, p)*opnorm(Ainv, p) catch e if isa(e, LAPACKException) || isa(e, SingularException) return convert(float(real(eltype(A))), Inf) else rethrow() end end end throw(ArgumentError("p-norm must be 1, 2 or Inf, got $p")) end ## Lyapunov and Sylvester equation # AX + XB + C = 0 """ sylvester(A, B, C) Computes the solution `X` to the Sylvester equation `AX + XB + C = 0`, where `A`, `B` and `C` have compatible dimensions and `A` and `-B` have no eigenvalues with equal real part. # Examples ```jldoctest julia> A = [3. 4.; 5. 6] 2ร—2 Matrix{Float64}: 3.0 4.0 5.0 6.0 julia> B = [1. 1.; 1. 2.] 2ร—2 Matrix{Float64}: 1.0 1.0 1.0 2.0 julia> C = [1. 2.; -2. 1] 2ร—2 Matrix{Float64}: 1.0 2.0 -2.0 1.0 julia> X = sylvester(A, B, C) 2ร—2 Matrix{Float64}: -4.46667 1.93333 3.73333 -1.8 julia> A*X + X*B โ‰ˆ -C true ``` """ function sylvester(A::AbstractMatrix, B::AbstractMatrix, C::AbstractMatrix) T = promote_type(float(eltype(A)), float(eltype(B)), float(eltype(C))) return sylvester(copy_similar(A, T), copy_similar(B, T), copy_similar(C, T)) end function sylvester(A::AbstractMatrix{T}, B::AbstractMatrix{T}, C::AbstractMatrix{T}) where {T<:BlasFloat} RA, QA = schur(A) RB, QB = schur(B) D = QA' * C * QB D .= .-D Y, scale = LAPACK.trsyl!('N', 'N', RA, RB, D) rmul!(QA * Y * QB', inv(scale)) end Base.@propagate_inbounds function _sylvester_2x1!(A, B, C) b = B[1] a21, a12 = A[2, 1], A[1, 2] m11 = b + A[1, 1] m22 = b + A[2, 2] d = m11 * m22 - a12 * a21 c1, c2 = C C[1] = (a12 * c2 - m22 * c1) / d C[2] = (a21 * c1 - m11 * c2) / d return C end Base.@propagate_inbounds function _sylvester_1x2!(A, B, C) a = A[1] b21, b12 = B[2, 1], B[1, 2] m11 = a + B[1, 1] m22 = a + B[2, 2] d = m11 * m22 - b21 * b12 c1, c2 = C C[1] = (b21 * c2 - m22 * c1) / d C[2] = (b12 * c1 - m11 * c2) / d return C end function _sylvester_2x2!(A, B, C) _, scale = LAPACK.trsyl!('N', 'N', A, B, C) rmul!(C, -inv(scale)) return C end sylvester(a::Union{Real,Complex}, b::Union{Real,Complex}, c::Union{Real,Complex}) = -c / (a + b) # AX + XA' + C = 0 """ lyap(A, C) Computes the solution `X` to the continuous Lyapunov equation `AX + XA' + C = 0`, where no eigenvalue of `A` has a zero real part and no two eigenvalues are negative complex conjugates of each other. # Examples ```jldoctest julia> A = [3. 4.; 5. 6] 2ร—2 Matrix{Float64}: 3.0 4.0 5.0 6.0 julia> B = [1. 1.; 1. 2.] 2ร—2 Matrix{Float64}: 1.0 1.0 1.0 2.0 julia> X = lyap(A, B) 2ร—2 Matrix{Float64}: 0.5 -0.5 -0.5 0.25 julia> A*X + X*A' โ‰ˆ -B true ``` """ function lyap(A::AbstractMatrix, C::AbstractMatrix) T = promote_type(float(eltype(A)), float(eltype(C))) return lyap(copy_similar(A, T), copy_similar(C, T)) end function lyap(A::AbstractMatrix{T}, C::AbstractMatrix{T}) where {T<:BlasFloat} R, Q = schur(A) D = Q' * C * Q D .= .-D Y, scale = LAPACK.trsyl!('N', T <: Complex ? 'C' : 'T', R, R, D) rmul!(Q * Y * Q', inv(scale)) end lyap(a::Union{Real,Complex}, c::Union{Real,Complex}) = -c/(2real(a)) x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/tridiag.jlwr# This file is a part of Julia. License is MIT: https://julialang.org/license #### Specialized matrix types #### ## (complex) symmetric tridiagonal matrices struct SymTridiagonal{T, V<:AbstractVector{T}} <: AbstractMatrix{T} dv::V # diagonal ev::V # superdiagonal function SymTridiagonal{T, V}(dv, ev) where {T, V<:AbstractVector{T}} require_one_based_indexing(dv, ev) if !(length(dv) - 1 <= length(ev) <= length(dv)) throw(DimensionMismatch("subdiagonal has wrong length. Has length $(length(ev)), but should be either $(length(dv) - 1) or $(length(dv)).")) end new{T, V}(dv, ev) end end """ SymTridiagonal(dv::V, ev::V) where V <: AbstractVector Construct a symmetric tridiagonal matrix from the diagonal (`dv`) and first sub/super-diagonal (`ev`), respectively. The result is of type `SymTridiagonal` and provides efficient specialized eigensolvers, but may be converted into a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). For `SymTridiagonal` block matrices, the elements of `dv` are symmetrized. The argument `ev` is interpreted as the superdiagonal. Blocks from the subdiagonal are (materialized) transpose of the corresponding superdiagonal blocks. # Examples ```jldoctest julia> dv = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> ev = [7, 8, 9] 3-element Vector{Int64}: 7 8 9 julia> SymTridiagonal(dv, ev) 4ร—4 SymTridiagonal{Int64, Vector{Int64}}: 1 7 โ‹… โ‹… 7 2 8 โ‹… โ‹… 8 3 9 โ‹… โ‹… 9 4 julia> A = SymTridiagonal(fill([1 2; 3 4], 3), fill([1 2; 3 4], 2)); julia> A[1,1] 2ร—2 Symmetric{Int64, Matrix{Int64}}: 1 2 2 4 julia> A[1,2] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> A[2,1] 2ร—2 Matrix{Int64}: 1 3 2 4 ``` """ SymTridiagonal(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T}(dv, ev) SymTridiagonal{T}(dv::V, ev::V) where {T,V<:AbstractVector{T}} = SymTridiagonal{T,V}(dv, ev) function SymTridiagonal{T}(dv::AbstractVector, ev::AbstractVector) where {T} SymTridiagonal(convert(AbstractVector{T}, dv)::AbstractVector{T}, convert(AbstractVector{T}, ev)::AbstractVector{T}) end """ SymTridiagonal(A::AbstractMatrix) Construct a symmetric tridiagonal matrix from the diagonal and first superdiagonal of the symmetric matrix `A`. # Examples ```jldoctest julia> A = [1 2 3; 2 4 5; 3 5 6] 3ร—3 Matrix{Int64}: 1 2 3 2 4 5 3 5 6 julia> SymTridiagonal(A) 3ร—3 SymTridiagonal{Int64, Vector{Int64}}: 1 2 โ‹… 2 4 5 โ‹… 5 6 julia> B = reshape([[1 2; 2 3], [1 2; 3 4], [1 3; 2 4], [1 2; 2 3]], 2, 2); julia> SymTridiagonal(B) 2ร—2 SymTridiagonal{Matrix{Int64}, Vector{Matrix{Int64}}}: [1 2; 2 3] [1 3; 2 4] [1 2; 3 4] [1 2; 2 3] ``` """ function SymTridiagonal(A::AbstractMatrix) if (diag(A, 1) == transpose.(diag(A, -1))) && all(issymmetric.(diag(A, 0))) SymTridiagonal(diag(A, 0), diag(A, 1)) else throw(ArgumentError("matrix is not symmetric; cannot convert to SymTridiagonal")) end end SymTridiagonal{T,V}(S::SymTridiagonal{T,V}) where {T,V<:AbstractVector{T}} = S SymTridiagonal{T,V}(S::SymTridiagonal) where {T,V<:AbstractVector{T}} = SymTridiagonal(convert(V, S.dv)::V, convert(V, S.ev)::V) SymTridiagonal{T}(S::SymTridiagonal{T}) where {T} = S SymTridiagonal{T}(S::SymTridiagonal) where {T} = SymTridiagonal(convert(AbstractVector{T}, S.dv)::AbstractVector{T}, convert(AbstractVector{T}, S.ev)::AbstractVector{T}) SymTridiagonal(S::SymTridiagonal) = S AbstractMatrix{T}(S::SymTridiagonal) where {T} = SymTridiagonal(convert(AbstractVector{T}, S.dv)::AbstractVector{T}, convert(AbstractVector{T}, S.ev)::AbstractVector{T}) function Matrix{T}(M::SymTridiagonal) where T n = size(M, 1) Mf = Matrix{T}(undef, n, n) n == 0 && return Mf n > 2 && fill!(Mf, zero(T)) @inbounds for i = 1:n-1 Mf[i,i] = symmetric(M.dv[i], :U) Mf[i+1,i] = transpose(M.ev[i]) Mf[i,i+1] = M.ev[i] end Mf[n,n] = symmetric(M.dv[n], :U) return Mf end Matrix(M::SymTridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M) Array(M::SymTridiagonal) = Matrix(M) size(A::SymTridiagonal) = (length(A.dv), length(A.dv)) function size(A::SymTridiagonal, d::Integer) if d < 1 throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) elseif d<=2 return length(A.dv) else return 1 end end similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal(similar(S.dv, T), similar(S.ev, T)) similar(S::SymTridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(S.dv, T, dims) copyto!(dest::SymTridiagonal, src::SymTridiagonal) = (copyto!(dest.dv, src.dv); copyto!(dest.ev, _evview(src)); dest) #Elementary operations for func in (:conj, :copy, :real, :imag) @eval ($func)(M::SymTridiagonal) = SymTridiagonal(($func)(M.dv), ($func)(M.ev)) end transpose(S::SymTridiagonal) = S adjoint(S::SymTridiagonal{<:Real}) = S adjoint(S::SymTridiagonal) = Adjoint(S) permutedims(S::SymTridiagonal) = S function permutedims(S::SymTridiagonal, perm) Base.checkdims_perm(S, S, perm) NTuple{2}(perm) == (2, 1) ? permutedims(S) : S end Base.copy(S::Adjoint{<:Any,<:SymTridiagonal}) = SymTridiagonal(map(x -> copy.(adjoint.(x)), (S.parent.dv, S.parent.ev))...) ishermitian(S::SymTridiagonal) = isreal(S.dv) && isreal(_evview(S)) issymmetric(S::SymTridiagonal) = true tr(S::SymTridiagonal) = sum(S.dv) function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n absn = abs(n) if absn == 0 return copyto!(similar(M.dv, length(M.dv)), M.dv) elseif absn == 1 return copyto!(similar(M.ev, length(M.dv)-1), _evview(M)) elseif absn <= size(M,1) return fill!(similar(M.dv, size(M,1)-absn), zero(T)) else throw(ArgumentError(string("requested diagonal, $n, must be at least $(-size(M, 1)) ", "and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end function diag(M::SymTridiagonal, n::Integer=0) # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n if n == 0 return copyto!(similar(M.dv, length(M.dv)), symmetric.(M.dv, :U)) elseif n == 1 return copyto!(similar(M.ev, length(M.dv)-1), _evview(M)) elseif n == -1 return copyto!(similar(M.ev, length(M.dv)-1), transpose.(_evview(M))) elseif n <= size(M,1) throw(ArgumentError("requested diagonal contains undefined zeros of an array type")) else throw(ArgumentError(string("requested diagonal, $n, must be at least $(-size(M, 1)) ", "and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end +(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv+B.dv, _evview(A)+_evview(B)) -(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv-B.dv, _evview(A)-_evview(B)) -(A::SymTridiagonal) = SymTridiagonal(-A.dv, -A.ev) *(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv*B, A.ev*B) *(B::Number, A::SymTridiagonal) = SymTridiagonal(B*A.dv, B*A.ev) /(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv/B, A.ev/B) \(B::Number, A::SymTridiagonal) = SymTridiagonal(B\A.dv, B\A.ev) ==(A::SymTridiagonal{<:Number}, B::SymTridiagonal{<:Number}) = (A.dv == B.dv) && (_evview(A) == _evview(B)) ==(A::SymTridiagonal, B::SymTridiagonal) = size(A) == size(B) && all(i -> A[i,i] == B[i,i], axes(A, 1)) && (_evview(A) == _evview(B)) function dot(x::AbstractVector, S::SymTridiagonal, y::AbstractVector) require_one_based_indexing(x, y) nx, ny = length(x), length(y) (nx == size(S, 1) == ny) || throw(DimensionMismatch("dot")) if nx โ‰ค 1 nx == 0 && return dot(zero(eltype(x)), zero(eltype(S)), zero(eltype(y))) return dot(x[1], S.dv[1], y[1]) end dv, ev = S.dv, S.ev @inbounds begin xโ‚€ = x[1] xโ‚Š = x[2] sub = transpose(ev[1]) r = dot(adjoint(dv[1])*xโ‚€ + adjoint(sub)*xโ‚Š, y[1]) for j in 2:nx-1 xโ‚‹, xโ‚€, xโ‚Š = xโ‚€, xโ‚Š, x[j+1] sup, sub = transpose(sub), transpose(ev[j]) r += dot(adjoint(sup)*xโ‚‹ + adjoint(dv[j])*xโ‚€ + adjoint(sub)*xโ‚Š, y[j]) end r += dot(adjoint(transpose(sub))*xโ‚€ + adjoint(dv[nx])*xโ‚Š, y[nx]) end return r end (\)(T::SymTridiagonal, B::AbstractVecOrMat) = ldlt(T)\B # division with optional shift for use in shifted-Hessenberg solvers (hessenberg.jl): ldiv!(A::SymTridiagonal, B::AbstractVecOrMat; shift::Number=false) = ldiv!(ldlt(A, shift=shift), B) rdiv!(B::AbstractVecOrMat, A::SymTridiagonal; shift::Number=false) = rdiv!(B, ldlt(A, shift=shift)) eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = Eigen(LAPACK.stegr!('V', A.dv, A.ev)...) eigen(A::SymTridiagonal{T}) where T = eigen!(copymutable_oftype(A, eigtype(T))) eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) = Eigen(LAPACK.stegr!('V', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)...) eigen(A::SymTridiagonal{T}, irange::UnitRange) where T = eigen!(copymutable_oftype(A, eigtype(T)), irange) eigen!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) = Eigen(LAPACK.stegr!('V', 'V', A.dv, A.ev, vl, vu, 0, 0)...) eigen(A::SymTridiagonal{T}, vl::Real, vu::Real) where T = eigen!(copymutable_oftype(A, eigtype(T)), vl, vu) eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}) = LAPACK.stev!('N', A.dv, A.ev)[1] eigvals(A::SymTridiagonal{T}) where T = eigvals!(copymutable_oftype(A, eigtype(T))) eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, irange::UnitRange) = LAPACK.stegr!('N', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)[1] eigvals(A::SymTridiagonal{T}, irange::UnitRange) where T = eigvals!(copymutable_oftype(A, eigtype(T)), irange) eigvals!(A::SymTridiagonal{<:BlasReal,<:StridedVector}, vl::Real, vu::Real) = LAPACK.stegr!('N', 'V', A.dv, A.ev, vl, vu, 0, 0)[1] eigvals(A::SymTridiagonal{T}, vl::Real, vu::Real) where T = eigvals!(copymutable_oftype(A, eigtype(T)), vl, vu) #Computes largest and smallest eigenvalue eigmax(A::SymTridiagonal) = eigvals(A, size(A, 1):size(A, 1))[1] eigmin(A::SymTridiagonal) = eigvals(A, 1:1)[1] #Compute selected eigenvectors only corresponding to particular eigenvalues eigvecs(A::SymTridiagonal) = eigen(A).vectors """ eigvecs(A::SymTridiagonal[, eigvals]) -> Matrix Return a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can be obtained from the slice `M[:, k]`.) If the optional vector of eigenvalues `eigvals` is specified, `eigvecs` returns the specific corresponding eigenvectors. # Examples ```jldoctest julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 1.0 2.0 โ‹… 2.0 2.0 3.0 โ‹… 3.0 1.0 julia> eigvals(A) 3-element Vector{Float64}: -2.1400549446402604 1.0000000000000002 5.140054944640259 julia> eigvecs(A) 3ร—3 Matrix{Float64}: 0.418304 -0.83205 0.364299 -0.656749 -7.39009e-16 0.754109 0.627457 0.5547 0.546448 julia> eigvecs(A, [1.]) 3ร—1 Matrix{Float64}: 0.8320502943378438 4.263514128092366e-17 -0.5547001962252291 ``` """ eigvecs(A::SymTridiagonal{<:BlasFloat,<:StridedVector}, eigvals::Vector{<:Real}) = LAPACK.stein!(A.dv, A.ev, eigvals) function svdvals!(A::SymTridiagonal) vals = eigvals!(A) return sort!(map!(abs, vals, vals); rev=true) end # tril and triu function istriu(M::SymTridiagonal, k::Integer=0) if k <= -1 return true elseif k == 0 return iszero(_evview(M)) else # k >= 1 return iszero(_evview(M)) && iszero(M.dv) end end istril(M::SymTridiagonal, k::Integer) = istriu(M, -k) iszero(M::SymTridiagonal) = iszero(_evview(M)) && iszero(M.dv) isone(M::SymTridiagonal) = iszero(_evview(M)) && all(isone, M.dv) isdiag(M::SymTridiagonal) = iszero(_evview(M)) function tril!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n - 1 <= k <= n - 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.ev, zero(T)) fill!(M.dv, zero(T)) return Tridiagonal(M.ev,M.dv,copy(M.ev)) elseif k == -1 fill!(M.dv, zero(T)) return Tridiagonal(M.ev,M.dv,zero(M.ev)) elseif k == 0 return Tridiagonal(M.ev,M.dv,zero(M.ev)) elseif k >= 1 return Tridiagonal(M.ev,M.dv,copy(M.ev)) end end function triu!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n + 1 <= k <= n + 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.ev, zero(T)) fill!(M.dv, zero(T)) return Tridiagonal(M.ev,M.dv,copy(M.ev)) elseif k == 1 fill!(M.dv, zero(T)) return Tridiagonal(zero(M.ev),M.dv,M.ev) elseif k == 0 return Tridiagonal(zero(M.ev),M.dv,M.ev) elseif k <= -1 return Tridiagonal(M.ev,M.dv,copy(M.ev)) end end ################### # Generic methods # ################### ## structured matrix methods ## function Base.replace_in_print_matrix(A::SymTridiagonal, i::Integer, j::Integer, s::AbstractString) i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s) end # Implements the determinant using principal minors # a, b, c are assumed to be the subdiagonal, diagonal, and superdiagonal of # a tridiagonal matrix. #Reference: # R. Usmani, "Inversion of a tridiagonal Jacobi matrix", # Linear Algebra and its Applications 212-213 (1994), pp.413-414 # doi:10.1016/0024-3795(94)90414-6 function det_usmani(a::V, b::V, c::V, shift::Number=0) where {T,V<:AbstractVector{T}} require_one_based_indexing(a, b, c) n = length(b) ฮธa = oneunit(T)+zero(shift) if n == 0 return ฮธa end ฮธb = b[1]+shift for i in 2:n ฮธb, ฮธa = (b[i]+shift)*ฮธb - a[i-1]*c[i-1]*ฮธa, ฮธb end return ฮธb end # det with optional diagonal shift for use with shifted Hessenberg factorizations det(A::SymTridiagonal; shift::Number=false) = det_usmani(A.ev, A.dv, A.ev, shift) logabsdet(A::SymTridiagonal; shift::Number=false) = logabsdet(ldlt(A; shift=shift)) @inline function Base.isassigned(A::SymTridiagonal, i::Int, j::Int) @boundscheck checkbounds(Bool, A, i, j) || return false if i == j return @inbounds isassigned(A.dv, i) elseif i == j + 1 return @inbounds isassigned(A.ev, j) elseif i + 1 == j return @inbounds isassigned(A.ev, i) else return true end end @inline function getindex(A::SymTridiagonal{T}, i::Integer, j::Integer) where T @boundscheck checkbounds(A, i, j) if i == j return symmetric((@inbounds A.dv[i]), :U)::symmetric_type(eltype(A.dv)) elseif i == j + 1 return copy(transpose(@inbounds A.ev[j])) # materialized for type stability elseif i + 1 == j return @inbounds A.ev[i] else return zero(T) end end @inline function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) if i == j @inbounds A.dv[i] = x else throw(ArgumentError("cannot set off-diagonal entry ($i, $j)")) end return x end ## Tridiagonal matrices ## struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} dl::V # sub-diagonal d::V # diagonal du::V # sup-diagonal du2::V # supsup-diagonal for pivoting in LU function Tridiagonal{T,V}(dl, d, du) where {T,V<:AbstractVector{T}} require_one_based_indexing(dl, d, du) n = length(d) if (length(dl) != n-1 || length(du) != n-1) && !(length(d) == 0 && length(dl) == 0 && length(du) == 0) throw(ArgumentError(string("cannot construct Tridiagonal from incompatible ", "lengths of subdiagonal, diagonal and superdiagonal: ", "($(length(dl)), $(length(d)), $(length(du)))"))) end new{T,V}(dl, d, du) end # constructor used in lu! function Tridiagonal{T,V}(dl, d, du, du2) where {T,V<:AbstractVector{T}} require_one_based_indexing(dl, d, du, du2) # length checks? new{T,V}(dl, d, du, du2) end end """ Tridiagonal(dl::V, d::V, du::V) where V <: AbstractVector Construct a tridiagonal matrix from the first subdiagonal, diagonal, and first superdiagonal, respectively. The result is of type `Tridiagonal` and provides efficient specialized linear solvers, but may be converted into a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). The lengths of `dl` and `du` must be one less than the length of `d`. # Examples ```jldoctest julia> dl = [1, 2, 3]; julia> du = [4, 5, 6]; julia> d = [7, 8, 9, 0]; julia> Tridiagonal(dl, d, du) 4ร—4 Tridiagonal{Int64, Vector{Int64}}: 7 4 โ‹… โ‹… 1 8 5 โ‹… โ‹… 2 9 6 โ‹… โ‹… 3 0 ``` """ Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du) Tridiagonal(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T,V}(dl, d, du, du2) function Tridiagonal{T}(dl::AbstractVector, d::AbstractVector, du::AbstractVector) where {T} Tridiagonal(map(x->convert(AbstractVector{T}, x), (dl, d, du))...) end function Tridiagonal{T,V}(A::Tridiagonal) where {T,V<:AbstractVector{T}} Tridiagonal{T,V}(A.dl, A.d, A.du) end """ Tridiagonal(A) Construct a tridiagonal matrix from the first sub-diagonal, diagonal and first super-diagonal of the matrix `A`. # Examples ```jldoctest julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4] 4ร—4 Matrix{Int64}: 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 julia> Tridiagonal(A) 4ร—4 Tridiagonal{Int64, Vector{Int64}}: 1 2 โ‹… โ‹… 1 2 3 โ‹… โ‹… 2 3 4 โ‹… โ‹… 3 4 ``` """ Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1)) Tridiagonal(A::Tridiagonal) = A Tridiagonal{T}(A::Tridiagonal{T}) where {T} = A function Tridiagonal{T}(A::Tridiagonal) where {T} dl, d, du = map(x->convert(AbstractVector{T}, x)::AbstractVector{T}, (A.dl, A.d, A.du)) if isdefined(A, :du2) Tridiagonal(dl, d, du, convert(AbstractVector{T}, A.du2)::AbstractVector{T}) else Tridiagonal(dl, d, du) end end size(M::Tridiagonal) = (length(M.d), length(M.d)) function size(M::Tridiagonal, d::Integer) if d < 1 throw(ArgumentError("dimension d must be โ‰ฅ 1, got $d")) elseif d <= 2 return length(M.d) else return 1 end end function Matrix{T}(M::Tridiagonal) where {T} A = Matrix{T}(undef, size(M)) n = length(M.d) n == 0 && return A n > 2 && fill!(A, zero(T)) for i in 1:n-1 A[i,i] = M.d[i] A[i+1,i] = M.dl[i] A[i,i+1] = M.du[i] end A[n,n] = M.d[n] A end Matrix(M::Tridiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(M) Array(M::Tridiagonal) = Matrix(M) similar(M::Tridiagonal, ::Type{T}) where {T} = Tridiagonal(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) similar(M::Tridiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(M.d, T, dims) # Operations on Tridiagonal matrices copyto!(dest::Tridiagonal, src::Tridiagonal) = (copyto!(dest.dl, src.dl); copyto!(dest.d, src.d); copyto!(dest.du, src.du); dest) #Elementary operations for func in (:conj, :copy, :real, :imag) @eval function ($func)(M::Tridiagonal) Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du)) end end adjoint(S::Tridiagonal) = Adjoint(S) transpose(S::Tridiagonal) = Transpose(S) adjoint(S::Tridiagonal{<:Real}) = Tridiagonal(S.du, S.d, S.dl) transpose(S::Tridiagonal{<:Number}) = Tridiagonal(S.du, S.d, S.dl) permutedims(T::Tridiagonal) = Tridiagonal(T.du, T.d, T.dl) function permutedims(T::Tridiagonal, perm) Base.checkdims_perm(T, T, perm) NTuple{2}(perm) == (2, 1) ? permutedims(T) : T end Base.copy(aS::Adjoint{<:Any,<:Tridiagonal}) = (S = aS.parent; Tridiagonal(map(x -> copy.(adjoint.(x)), (S.du, S.d, S.dl))...)) Base.copy(tS::Transpose{<:Any,<:Tridiagonal}) = (S = tS.parent; Tridiagonal(map(x -> copy.(transpose.(x)), (S.du, S.d, S.dl))...)) ishermitian(S::Tridiagonal) = all(ishermitian, S.d) && all(Iterators.map((x, y) -> x == y', S.du, S.dl)) issymmetric(S::Tridiagonal) = all(issymmetric, S.d) && all(Iterators.map((x, y) -> x == transpose(y), S.du, S.dl)) \(A::Adjoint{<:Any,<:Tridiagonal}, B::Adjoint{<:Any,<:AbstractVecOrMat}) = copy(A) \ B function diag(M::Tridiagonal{T}, n::Integer=0) where T # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n if n == 0 return copyto!(similar(M.d, length(M.d)), M.d) elseif n == -1 return copyto!(similar(M.dl, length(M.dl)), M.dl) elseif n == 1 return copyto!(similar(M.du, length(M.du)), M.du) elseif abs(n) <= size(M,1) return fill!(similar(M.d, size(M,1)-abs(n)), zero(T)) else throw(ArgumentError(string("requested diagonal, $n, must be at least $(-size(M, 1)) ", "and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end @inline function Base.isassigned(A::Tridiagonal, i::Int, j::Int) @boundscheck checkbounds(Bool, A, i, j) || return false if i == j return @inbounds isassigned(A.d, i) elseif i == j + 1 return @inbounds isassigned(A.dl, j) elseif i + 1 == j return @inbounds isassigned(A.du, i) else return true end end @inline function getindex(A::Tridiagonal{T}, i::Integer, j::Integer) where T @boundscheck checkbounds(A, i, j) if i == j return @inbounds A.d[i] elseif i == j + 1 return @inbounds A.dl[j] elseif i + 1 == j return @inbounds A.du[i] else return zero(T) end end @inline function setindex!(A::Tridiagonal, x, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) if i == j @inbounds A.d[i] = x elseif i - j == 1 @inbounds A.dl[j] = x elseif j - i == 1 @inbounds A.du[i] = x elseif !iszero(x) throw(ArgumentError(string("cannot set entry ($i, $j) off ", "the tridiagonal band to a nonzero value ($x)"))) end return x end ## structured matrix methods ## function Base.replace_in_print_matrix(A::Tridiagonal,i::Integer,j::Integer,s::AbstractString) i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s) end #tril and triu iszero(M::Tridiagonal) = iszero(M.dl) && iszero(M.d) && iszero(M.du) isone(M::Tridiagonal) = iszero(M.dl) && all(isone, M.d) && iszero(M.du) function istriu(M::Tridiagonal, k::Integer=0) if k <= -1 return true elseif k == 0 return iszero(M.dl) elseif k == 1 return iszero(M.dl) && iszero(M.d) else # k >= 2 return iszero(M.dl) && iszero(M.d) && iszero(M.du) end end function istril(M::Tridiagonal, k::Integer=0) if k >= 1 return true elseif k == 0 return iszero(M.du) elseif k == -1 return iszero(M.du) && iszero(M.d) else # k <= -2 return iszero(M.du) && iszero(M.d) && iszero(M.dl) end end isdiag(M::Tridiagonal) = iszero(M.dl) && iszero(M.du) function tril!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n - 1 <= k <= n - 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.dl, zero(T)) fill!(M.d, zero(T)) fill!(M.du, zero(T)) elseif k == -1 fill!(M.d, zero(T)) fill!(M.du, zero(T)) elseif k == 0 fill!(M.du, zero(T)) end return M end function triu!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n + 1 <= k <= n + 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.dl, zero(T)) fill!(M.d, zero(T)) fill!(M.du, zero(T)) elseif k == 1 fill!(M.dl, zero(T)) fill!(M.d, zero(T)) elseif k == 0 fill!(M.dl, zero(T)) end return M end tr(M::Tridiagonal) = sum(M.d) ################### # Generic methods # ################### +(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl+B.dl, A.d+B.d, A.du+B.du) -(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl-B.dl, A.d-B.d, A.du-B.du) -(A::Tridiagonal) = Tridiagonal(-A.dl, -A.d, -A.du) *(A::Tridiagonal, B::Number) = Tridiagonal(A.dl*B, A.d*B, A.du*B) *(B::Number, A::Tridiagonal) = Tridiagonal(B*A.dl, B*A.d, B*A.du) /(A::Tridiagonal, B::Number) = Tridiagonal(A.dl/B, A.d/B, A.du/B) \(B::Number, A::Tridiagonal) = Tridiagonal(B\A.dl, B\A.d, B\A.du) ==(A::Tridiagonal, B::Tridiagonal) = (A.dl==B.dl) && (A.d==B.d) && (A.du==B.du) function ==(A::Tridiagonal, B::SymTridiagonal) iseq = all(Iterators.map((x, y) -> x == transpose(y), A.du, A.dl)) iseq = iseq && A.du == _evview(B) iseq && all(Iterators.map((x, y) -> x == symmetric(y, :U), A.d, B.dv)) end ==(A::SymTridiagonal, B::Tridiagonal) = B == A det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du) AbstractMatrix{T}(M::Tridiagonal) where {T} = Tridiagonal{T}(M) Tridiagonal{T}(M::SymTridiagonal{T}) where {T} = Tridiagonal(M) function SymTridiagonal{T}(M::Tridiagonal) where T if issymmetric(M) return SymTridiagonal{T}(convert(AbstractVector{T},M.d), convert(AbstractVector{T},M.dl)) else throw(ArgumentError("Tridiagonal is not symmetric, cannot convert to SymTridiagonal")) end end Base._sum(A::Tridiagonal, ::Colon) = sum(A.d) + sum(A.dl) + sum(A.du) function Base._sum(A::SymTridiagonal, ::Colon) se = sum(_evview(A)) symmetric(sum(A.dv), :U) + se + transpose(se) end function Base._sum(A::Tridiagonal, dims::Integer) res = Base.reducedim_initarray(A, dims, zero(eltype(A))) n = length(A.d) if n == 0 return res elseif n == 1 res[1] = A.d[1] return res end @inbounds begin if dims == 1 res[1] = A.dl[1] + A.d[1] for i = 2:n-1 res[i] = A.dl[i] + A.d[i] + A.du[i-1] end res[n] = A.d[n] + A.du[n-1] elseif dims == 2 res[1] = A.d[1] + A.du[1] for i = 2:n-1 res[i] = A.dl[i-1] + A.d[i] + A.du[i] end res[n] = A.dl[n-1] + A.d[n] elseif dims >= 3 for i = 1:n-1 res[i,i+1] = A.du[i] res[i,i] = A.d[i] res[i+1,i] = A.dl[i] end res[n,n] = A.d[n] end end res end function Base._sum(A::SymTridiagonal, dims::Integer) res = Base.reducedim_initarray(A, dims, zero(eltype(A))) n = length(A.dv) if n == 0 return res elseif n == 1 res[1] = A.dv[1] return res end @inbounds begin if dims == 1 res[1] = transpose(A.ev[1]) + symmetric(A.dv[1], :U) for i = 2:n-1 res[i] = transpose(A.ev[i]) + symmetric(A.dv[i], :U) + A.ev[i-1] end res[n] = symmetric(A.dv[n], :U) + A.ev[n-1] elseif dims == 2 res[1] = symmetric(A.dv[1], :U) + A.ev[1] for i = 2:n-1 res[i] = transpose(A.ev[i-1]) + symmetric(A.dv[i], :U) + A.ev[i] end res[n] = transpose(A.ev[n-1]) + symmetric(A.dv[n], :U) elseif dims >= 3 for i = 1:n-1 res[i,i+1] = A.ev[i] res[i,i] = symmetric(A.dv[i], :U) res[i+1,i] = transpose(A.ev[i]) end res[n,n] = symmetric(A.dv[n], :U) end end res end function dot(x::AbstractVector, A::Tridiagonal, y::AbstractVector) require_one_based_indexing(x, y) nx, ny = length(x), length(y) (nx == size(A, 1) == ny) || throw(DimensionMismatch()) if nx โ‰ค 1 nx == 0 && return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) return dot(x[1], A.d[1], y[1]) end @inbounds begin xโ‚€ = x[1] xโ‚Š = x[2] dl, d, du = A.dl, A.d, A.du r = dot(adjoint(d[1])*xโ‚€ + adjoint(dl[1])*xโ‚Š, y[1]) for j in 2:nx-1 xโ‚‹, xโ‚€, xโ‚Š = xโ‚€, xโ‚Š, x[j+1] r += dot(adjoint(du[j-1])*xโ‚‹ + adjoint(d[j])*xโ‚€ + adjoint(dl[j])*xโ‚Š, y[j]) end r += dot(adjoint(du[nx-1])*xโ‚€ + adjoint(d[nx])*xโ‚Š, y[nx]) end return r end function cholesky(S::SymTridiagonal, ::NoPivot = NoPivot(); check::Bool = true) if !ishermitian(S) check && checkpositivedefinite(-1) return Cholesky(S, 'U', convert(BlasInt, -1)) end T = choltype(eltype(S)) cholesky!(Hermitian(Bidiagonal{T}(diag(S, 0), diag(S, 1), :U)), NoPivot(); check = check) end {/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/triangular.jl\# This file is a part of Julia. License is MIT: https://julialang.org/license ## Triangular # could be renamed to Triangular when that name has been fully deprecated abstract type AbstractTriangular{T,S<:AbstractMatrix} <: AbstractMatrix{T} end # First loop through all methods that don't need special care for upper/lower and unit diagonal for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular) @eval begin struct $t{T,S<:AbstractMatrix{T}} <: AbstractTriangular{T,S} data::S function $t{T,S}(data) where {T,S<:AbstractMatrix{T}} require_one_based_indexing(data) checksquare(data) new{T,S}(data) end end $t(A::$t) = A $t{T}(A::$t{T}) where {T} = A function $t(A::AbstractMatrix) return $t{eltype(A), typeof(A)}(A) end function $t{T}(A::AbstractMatrix) where T $t(convert(AbstractMatrix{T}, A)) end function $t{T}(A::$t) where T Anew = convert(AbstractMatrix{T}, A.data) $t(Anew) end Matrix(A::$t{T}) where {T} = Matrix{T}(A) AbstractMatrix{T}(A::$t) where {T} = $t{T}(A) size(A::$t, d) = size(A.data, d) size(A::$t) = size(A.data) # For A<:AbstractTriangular, similar(A[, neweltype]) should yield a matrix with the same # triangular type and underlying storage type as A. The following method covers these cases. similar(A::$t, ::Type{T}) where {T} = $t(similar(parent(A), T)) # On the other hand, similar(A, [neweltype,] shape...) should yield a matrix of the underlying # storage type of A (not wrapped in a triangular type). The following method covers these cases. similar(A::$t, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(A), T, dims) copy(A::$t) = $t(copy(A.data)) real(A::$t{<:Real}) = A real(A::$t{<:Complex}) = (B = real(A.data); $t(B)) end end similar(A::UpperTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} = UpperTriangular(similar(parent(parent(A)), T)) similar(A::UnitUpperTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} = UnitUpperTriangular(similar(parent(parent(A)), T)) similar(A::LowerTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} = LowerTriangular(similar(parent(parent(A)), T)) similar(A::UnitLowerTriangular{<:Any,<:Union{Adjoint{Ti}, Transpose{Ti}}}, ::Type{T}) where {T,Ti} = UnitLowerTriangular(similar(parent(parent(A)), T)) """ LowerTriangular(A::AbstractMatrix) Construct a `LowerTriangular` view of the matrix `A`. # Examples ```jldoctest julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] 3ร—3 Matrix{Float64}: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 julia> LowerTriangular(A) 3ร—3 LowerTriangular{Float64, Matrix{Float64}}: 1.0 โ‹… โ‹… 4.0 5.0 โ‹… 7.0 8.0 9.0 ``` """ LowerTriangular """ UpperTriangular(A::AbstractMatrix) Construct an `UpperTriangular` view of the matrix `A`. # Examples ```jldoctest julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] 3ร—3 Matrix{Float64}: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 julia> UpperTriangular(A) 3ร—3 UpperTriangular{Float64, Matrix{Float64}}: 1.0 2.0 3.0 โ‹… 5.0 6.0 โ‹… โ‹… 9.0 ``` """ UpperTriangular """ UnitLowerTriangular(A::AbstractMatrix) Construct a `UnitLowerTriangular` view of the matrix `A`. Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref) of `A` on its diagonal. # Examples ```jldoctest julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] 3ร—3 Matrix{Float64}: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 julia> UnitLowerTriangular(A) 3ร—3 UnitLowerTriangular{Float64, Matrix{Float64}}: 1.0 โ‹… โ‹… 4.0 1.0 โ‹… 7.0 8.0 1.0 ``` """ UnitLowerTriangular """ UnitUpperTriangular(A::AbstractMatrix) Construct an `UnitUpperTriangular` view of the matrix `A`. Such a view has the [`oneunit`](@ref) of the [`eltype`](@ref) of `A` on its diagonal. # Examples ```jldoctest julia> A = [1.0 2.0 3.0; 4.0 5.0 6.0; 7.0 8.0 9.0] 3ร—3 Matrix{Float64}: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 julia> UnitUpperTriangular(A) 3ร—3 UnitUpperTriangular{Float64, Matrix{Float64}}: 1.0 2.0 3.0 โ‹… 1.0 6.0 โ‹… โ‹… 1.0 ``` """ UnitUpperTriangular const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}} const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}} const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}} imag(A::UpperTriangular) = UpperTriangular(imag(A.data)) imag(A::LowerTriangular) = LowerTriangular(imag(A.data)) imag(A::UnitLowerTriangular) = LowerTriangular(tril!(imag(A.data),-1)) imag(A::UnitUpperTriangular) = UpperTriangular(triu!(imag(A.data),1)) Array(A::AbstractTriangular) = Matrix(A) parent(A::UpperOrLowerTriangular) = A.data # then handle all methods that requires specific handling of upper/lower and unit diagonal function Matrix{T}(A::LowerTriangular) where T B = Matrix{T}(undef, size(A, 1), size(A, 1)) copyto!(B, A.data) tril!(B) B end function Matrix{T}(A::UnitLowerTriangular) where T B = Matrix{T}(undef, size(A, 1), size(A, 1)) copyto!(B, A.data) tril!(B) for i = 1:size(B,1) B[i,i] = oneunit(T) end B end function Matrix{T}(A::UpperTriangular) where T B = Matrix{T}(undef, size(A, 1), size(A, 1)) copyto!(B, A.data) triu!(B) B end function Matrix{T}(A::UnitUpperTriangular) where T B = Matrix{T}(undef, size(A, 1), size(A, 1)) copyto!(B, A.data) triu!(B) for i = 1:size(B,1) B[i,i] = oneunit(T) end B end function full!(A::LowerTriangular) B = A.data tril!(B) B end function full!(A::UnitLowerTriangular) B = A.data tril!(B) for i = 1:size(A,1) B[i,i] = oneunit(eltype(B)) end B end function full!(A::UpperTriangular) B = A.data triu!(B) B end function full!(A::UnitUpperTriangular) B = A.data triu!(B) for i = 1:size(A,1) B[i,i] = oneunit(eltype(B)) end B end Base.isassigned(A::UnitLowerTriangular, i::Int, j::Int) = i > j ? isassigned(A.data, i, j) : true Base.isassigned(A::LowerTriangular, i::Int, j::Int) = i >= j ? isassigned(A.data, i, j) : true Base.isassigned(A::UnitUpperTriangular, i::Int, j::Int) = i < j ? isassigned(A.data, i, j) : true Base.isassigned(A::UpperTriangular, i::Int, j::Int) = i <= j ? isassigned(A.data, i, j) : true getindex(A::UnitLowerTriangular{T}, i::Integer, j::Integer) where {T} = i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) getindex(A::LowerTriangular, i::Integer, j::Integer) = i >= j ? A.data[i,j] : zero(A.data[j,i]) getindex(A::UnitUpperTriangular{T}, i::Integer, j::Integer) where {T} = i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T)) getindex(A::UpperTriangular, i::Integer, j::Integer) = i <= j ? A.data[i,j] : zero(A.data[j,i]) function setindex!(A::UpperTriangular, x, i::Integer, j::Integer) if i > j iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * "($i, $j) of an UpperTriangular matrix to a nonzero value ($x)")) else A.data[i,j] = x end return A end function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer) if i > j iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * "($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)")) elseif i == j x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * "of a UnitUpperTriangular matrix to a non-unit value ($x)")) else A.data[i,j] = x end return A end function setindex!(A::LowerTriangular, x, i::Integer, j::Integer) if i < j iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * "($i, $j) of a LowerTriangular matrix to a nonzero value ($x)")) else A.data[i,j] = x end return A end function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer) if i < j iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * "($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)")) elseif i == j x == oneunit(x) || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " * "of a UnitLowerTriangular matrix to a non-unit value ($x)")) else A.data[i,j] = x end return A end ## structured matrix methods ## function Base.replace_in_print_matrix(A::Union{UpperTriangular,UnitUpperTriangular}, i::Integer, j::Integer, s::AbstractString) return i <= j ? s : Base.replace_with_centered_mark(s) end function Base.replace_in_print_matrix(A::Union{LowerTriangular,UnitLowerTriangular}, i::Integer, j::Integer, s::AbstractString) return i >= j ? s : Base.replace_with_centered_mark(s) end function istril(A::Union{LowerTriangular,UnitLowerTriangular}, k::Integer=0) k >= 0 && return true return _istril(A, k) end function istriu(A::Union{UpperTriangular,UnitUpperTriangular}, k::Integer=0) k <= 0 && return true return _istriu(A, k) end istril(A::Adjoint, k::Integer=0) = istriu(A.parent, -k) istril(A::Transpose, k::Integer=0) = istriu(A.parent, -k) istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k) istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k) function tril!(A::UpperTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k < 0 fill!(A.data, zero(T)) return A elseif k == 0 for j in 1:n, i in 1:j-1 A.data[i,j] = zero(T) end return A else return UpperTriangular(tril!(A.data,k)) end end triu!(A::UpperTriangular, k::Integer=0) = UpperTriangular(triu!(A.data, k)) function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k < 0 fill!(A.data, zero(T)) return UpperTriangular(A.data) elseif k == 0 fill!(A.data, zero(T)) for i in diagind(A) A.data[i] = oneunit(T) end return UpperTriangular(A.data) else for i in diagind(A) A.data[i] = oneunit(T) end return UpperTriangular(tril!(A.data,k)) end end function triu!(A::UnitUpperTriangular, k::Integer=0) for i in diagind(A) A.data[i] = oneunit(eltype(A)) end return triu!(UpperTriangular(A.data), k) end function triu!(A::LowerTriangular{T}, k::Integer=0) where {T} n = size(A,1) if k > 0 fill!(A.data, zero(T)) return A elseif k == 0 for j in 1:n, i in j+1:n A.data[i,j] = zero(T) end return A else return LowerTriangular(triu!(A.data, k)) end end tril!(A::LowerTriangular, k::Integer=0) = LowerTriangular(tril!(A.data, k)) function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T n = size(A,1) if k > 0 fill!(A.data, zero(T)) return LowerTriangular(A.data) elseif k == 0 fill!(A.data, zero(T)) for i in diagind(A) A.data[i] = oneunit(T) end return LowerTriangular(A.data) else for i in diagind(A) A.data[i] = oneunit(T) end return LowerTriangular(triu!(A.data, k)) end end function tril!(A::UnitLowerTriangular, k::Integer=0) for i in diagind(A) A.data[i] = oneunit(eltype(A)) end return tril!(LowerTriangular(A.data), k) end adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data)) adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data)) adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data)) adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data)) transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data)) transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data)) transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data)) transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data)) transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L', false, true)) transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L', false, false)) transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U', false, true)) transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U', false, false)) adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true, true)) adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true, false)) adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true, true)) adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true, false)) diag(A::LowerTriangular) = diag(A.data) diag(A::UnitLowerTriangular) = fill(oneunit(eltype(A)), size(A,1)) diag(A::UpperTriangular) = diag(A.data) diag(A::UnitUpperTriangular) = fill(oneunit(eltype(A)), size(A,1)) # Unary operations -(A::LowerTriangular) = LowerTriangular(-A.data) -(A::UpperTriangular) = UpperTriangular(-A.data) function -(A::UnitLowerTriangular) Anew = -A.data for i = 1:size(A, 1) Anew[i, i] = -A[i, i] end LowerTriangular(Anew) end function -(A::UnitUpperTriangular) Anew = -A.data for i = 1:size(A, 1) Anew[i, i] = -A[i, i] end UpperTriangular(Anew) end tr(A::LowerTriangular) = tr(A.data) tr(A::UnitLowerTriangular) = size(A, 1) * oneunit(eltype(A)) tr(A::UpperTriangular) = tr(A.data) tr(A::UnitUpperTriangular) = size(A, 1) * oneunit(eltype(A)) # copy and scale function copyto!(A::T, B::T) where {T<:Union{UpperTriangular,UnitUpperTriangular}} n = size(B,1) for j = 1:n for i = 1:(isa(B, UnitUpperTriangular) ? j-1 : j) @inbounds A[i,j] = B[i,j] end end return A end function copyto!(A::T, B::T) where {T<:Union{LowerTriangular,UnitLowerTriangular}} n = size(B,1) for j = 1:n for i = (isa(B, UnitLowerTriangular) ? j+1 : j):n @inbounds A[i,j] = B[i,j] end end return A end # Define `mul!` for (Unit){Upper,Lower}Triangular matrices times a number. # be permissive here and require compatibility later in _triscale! @inline mul!(A::AbstractTriangular, B::AbstractTriangular, C::Number, alpha::Number, beta::Number) = _triscale!(A, B, C, MulAddMul(alpha, beta)) @inline mul!(A::AbstractTriangular, B::Number, C::AbstractTriangular, alpha::Number, beta::Number) = _triscale!(A, B, C, MulAddMul(alpha, beta)) function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n for i = 1:j @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n for i = 1:j @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = 1:(j - 1) @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = 1:(j - 1) @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n for i = j:n @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n for i = j:n @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = (j + 1):n @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add) n = checksquare(B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) for j = 1:n @inbounds _modify!(_add, c, A, (j,j)) for i = (j + 1):n @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end rmul!(A::UpperOrLowerTriangular, c::Number) = @inline _triscale!(A, A, c, MulAddMul()) lmul!(c::Number, A::UpperOrLowerTriangular) = @inline _triscale!(A, c, A, MulAddMul()) function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector) require_one_based_indexing(x, y) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end xโ‚ = x[1] r = dot(xโ‚, A[1,1], y[1]) @inbounds for j in 2:m yj = y[j] if !iszero(yj) temp = adjoint(A[1,j]) * xโ‚ @simd for i in 2:j temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) end end return r end function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector) require_one_based_indexing(x, y) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end xโ‚ = first(x) r = dot(xโ‚, y[1]) @inbounds for j in 2:m yj = y[j] if !iszero(yj) temp = adjoint(A[1,j]) * xโ‚ @simd for i in 2:j-1 temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) r += dot(x[j], yj) end end return r end function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector) require_one_based_indexing(x, y) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end r = zero(typeof(dot(first(x), first(A), first(y)))) @inbounds for j in 1:m yj = y[j] if !iszero(yj) temp = adjoint(A[j,j]) * x[j] @simd for i in j+1:m temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) end end return r end function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector) require_one_based_indexing(x, y) m = size(A, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end r = zero(typeof(dot(first(x), first(y)))) @inbounds for j in 1:m yj = y[j] if !iszero(yj) temp = x[j] @simd for i in j+1:m temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) end end return r end fillstored!(A::LowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), 0); A) fillstored!(A::UnitLowerTriangular, x) = (fillband!(A.data, x, 1-size(A,1), -1); A) fillstored!(A::UpperTriangular, x) = (fillband!(A.data, x, 0, size(A,2)-1); A) fillstored!(A::UnitUpperTriangular, x) = (fillband!(A.data, x, 1, size(A,2)-1); A) # Binary operations +(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data) +(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data) +(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I) +(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I) +(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I) +(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I) +(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I) +(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I) +(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) + copyto!(similar(parent(B)), B) -(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data) -(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data) -(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I) -(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I) -(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I) -(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I) -(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1)) -(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1)) -(A::AbstractTriangular, B::AbstractTriangular) = copyto!(similar(parent(A)), A) - copyto!(similar(parent(B)), B) ###################### # BlasFloat routines # ###################### # which triangle to use of the underlying data uplo_char(::UpperOrUnitUpperTriangular) = 'U' uplo_char(::LowerOrUnitLowerTriangular) = 'L' uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:AdjOrTrans}) = 'L' uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:AdjOrTrans}) = 'U' uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'U' uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Adjoint{<:Any,<:Transpose}}) = 'L' uplo_char(::UpperOrUnitUpperTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'U' uplo_char(::LowerOrUnitLowerTriangular{<:Any,<:Transpose{<:Any,<:Adjoint}}) = 'L' isunit_char(::UpperTriangular) = 'N' isunit_char(::UnitUpperTriangular) = 'U' isunit_char(::LowerTriangular) = 'N' isunit_char(::UnitLowerTriangular) = 'U' lmul!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) mul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) = _trimul!(C, A, B) mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) = _trimul!(C, A, B) mul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = _trimul!(C, A, B) mul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) = _trimul!(C, A, B) # generic fallback for AbstractTriangular matrices outside of the four subtypes provided here _trimul!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVector) = lmul!(A, copyto!(C, B)) _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractMatrix) = lmul!(A, inplace_adj_or_trans(B)(C, _unwrap_at(B))) _trimul!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = rmul!(inplace_adj_or_trans(A)(C, _unwrap_at(A)), B) _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractTriangular) = lmul!(A, copyto!(C, B)) # redirect for UpperOrLowerTriangular _trimul!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVector) = generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractMatrix) = generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) _trimul!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::UpperOrLowerTriangular) = generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) # disambiguation with AbstractTriangular _trimul!(C::AbstractMatrix, A::UpperOrLowerTriangular, B::AbstractTriangular) = generic_trimatmul!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) _trimul!(C::AbstractMatrix, A::AbstractTriangular, B::UpperOrLowerTriangular) = generic_mattrimul!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) lmul!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _trimul!(B, A, B) rmul!(A::AbstractMatrix, B::AbstractTriangular) = @inline _trimul!(A, A, B) for TC in (:AbstractVector, :AbstractMatrix) @eval @inline function mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) if isone(alpha) && iszero(beta) return mul!(C, A, B) else return generic_matvecmul!(C, 'N', A, B, MulAddMul(alpha, beta)) end end end for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix), (:AbstractMatrix, :AbstractTriangular), (:AbstractTriangular, :AbstractTriangular) ) @eval @inline function mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number) if isone(alpha) && iszero(beta) return mul!(C, A, B) else return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) end end end ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = _ldiv!(C, A, B) # generic fallback for AbstractTriangular, directs to 2-arg [l/r]div! _ldiv!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = ldiv!(A, inplace_adj_or_trans(B)(C, _unwrap_at(B))) _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractTriangular) = rdiv!(inplace_adj_or_trans(A)(C, _unwrap_at(A)), B) # redirect for UpperOrLowerTriangular to generic_*div! _ldiv!(C::AbstractVecOrMat, A::UpperOrLowerTriangular, B::AbstractVecOrMat) = generic_trimatdiv!(C, uplo_char(A), isunit_char(A), wrapperop(parent(A)), _unwrap_at(parent(A)), B) _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::UpperOrLowerTriangular) = generic_mattridiv!(C, uplo_char(B), isunit_char(B), wrapperop(parent(B)), A, _unwrap_at(parent(B))) ldiv!(A::AbstractTriangular, B::AbstractVecOrMat) = @inline _ldiv!(B, A, B) rdiv!(A::AbstractMatrix, B::AbstractTriangular) = @inline _rdiv!(A, A, B) # preserve triangular structure in in-place multiplication/division for (cty, aty, bty) in ((:UpperTriangular, :UpperTriangular, :UpperTriangular), (:UpperTriangular, :UpperTriangular, :UnitUpperTriangular), (:UpperTriangular, :UnitUpperTriangular, :UpperTriangular), (:UnitUpperTriangular, :UnitUpperTriangular, :UnitUpperTriangular), (:LowerTriangular, :LowerTriangular, :LowerTriangular), (:LowerTriangular, :LowerTriangular, :UnitLowerTriangular), (:LowerTriangular, :UnitLowerTriangular, :LowerTriangular), (:UnitLowerTriangular, :UnitLowerTriangular, :UnitLowerTriangular)) @eval begin function _trimul!(C::$cty, A::$aty, B::$bty) _trimul!(parent(C), A, B) return C end function _ldiv!(C::$cty, A::$aty, B::$bty) _ldiv!(parent(C), A, B) return C end function _rdiv!(C::$cty, A::$aty, B::$bty) _rdiv!(parent(C), A, B) return C end end end for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'), (:UnitLowerTriangular, 'L', 'U'), (:UpperTriangular, 'U', 'N'), (:UnitUpperTriangular, 'U', 'U')) @eval begin # Matrix inverse inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = $t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data)) function inv(A::$t{T}) where {T} S = typeof(inv(oneunit(T))) if S <: BlasFloat || S === T # i.e. A is unitless $t(ldiv!(convert(AbstractArray{S}, A), Matrix{S}(I, size(A)))) else J = (one(T)*I)(size(A, 1)) $t(ldiv!(similar(A, S, size(A)), A, J)) end end # Error bounds for triangular solve errorbounds(A::$t{T,<:StridedMatrix}, X::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.trrfs!($uploc, 'N', $isunitc, A.data, B, X) # Condition numbers function cond(A::$t{<:BlasFloat,<:StridedMatrix}, p::Real=2) checksquare(A) if p == 1 return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data)) elseif p == Inf return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data)) else # use fallback return cond(copyto!(similar(parent(A)), A), p) end end end end # multiplication generic_trimatmul!(c::StridedVector{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, b::AbstractVector{T}) where {T<:BlasFloat} = BLAS.trmv!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, c === b ? c : copyto!(c, b)) generic_trimatmul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} = BLAS.trmm!('L', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), A, C === B ? C : copyto!(C, B)) generic_mattrimul!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = BLAS.trmm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A)) # division generic_trimatdiv!(C::StridedVecOrMat{T}, uploc, isunitc, tfun::Function, A::StridedMatrix{T}, B::AbstractVecOrMat{T}) where {T<:BlasFloat} = LAPACK.trtrs!(uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, A, C === B ? C : copyto!(C, B)) generic_mattridiv!(C::StridedMatrix{T}, uploc, isunitc, tfun::Function, A::AbstractMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = BLAS.trsm!('R', uploc, tfun === identity ? 'N' : tfun === transpose ? 'T' : 'C', isunitc, one(T), B, C === A ? C : copyto!(C, A)) errorbounds(A::AbstractTriangular{T,<:AbstractMatrix}, X::AbstractVecOrMat{T}, B::AbstractVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} = error("not implemented yet! Please submit a pull request.") function errorbounds(A::AbstractTriangular{TA,<:AbstractMatrix}, X::AbstractVecOrMat{TX}, B::AbstractVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number} TAXB = promote_type(TA, TB, TX, Float32) errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B)) end # Eigensystems ## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix}) LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) end function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix}) for i = 1:size(A, 1) A.data[i,i] = 1 end LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) end function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix}) LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) end function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix}) for i = 1:size(A, 1) A.data[i,i] = 1 end LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) end #################### # Generic routines # #################### for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), (LowerTriangular, UnitLowerTriangular)) @eval begin (*)(A::$t, x::Number) = $t(A.data*x) function (*)(A::$unitt, x::Number) B = A.data*x for i = 1:size(A, 1) B[i,i] = x end $t(B) end (*)(x::Number, A::$t) = $t(x*A.data) function (*)(x::Number, A::$unitt) B = x*A.data for i = 1:size(A, 1) B[i,i] = x end $t(B) end (/)(A::$t, x::Number) = $t(A.data/x) function (/)(A::$unitt, x::Number) B = A.data/x invx = inv(x) for i = 1:size(A, 1) B[i,i] = invx end $t(B) end (\)(x::Number, A::$t) = $t(x\A.data) function (\)(x::Number, A::$unitt) B = x\A.data invx = inv(x) for i = 1:size(A, 1) B[i,i] = invx end $t(B) end end end ## Generic triangular multiplication function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) N = size(A, 1) if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end mc, nc = size(C, 1), size(C, 2) if mc != N || nc != n throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) end oA = oneunit(eltype(A)) unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity for j in 1:n for i in 1:m Cij = (unit ? oA : A[i,i]) * B[i,j] for k in i + 1:m Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) for j in 1:n for i in m:-1:1 Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] for k in 1:i - 1 Cij += tfun(A[k,i]) * B[k,j] end C[i,j] = Cij end end end else # uploc == 'L' if tfun === identity for j in 1:n for i in m:-1:1 Cij = (unit ? oA : A[i,i]) * B[i,j] for k in 1:i - 1 Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) for j in 1:n for i in 1:m Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] for k in i + 1:m Cij += tfun(A[k,i]) * B[k,j] end C[i,j] = Cij end end end end return C end # conjugate cases function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat) A = parent(xA) require_one_based_indexing(C, A, B) m, n = size(B, 1), size(B, 2) N = size(A, 1) if m != N throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m")) end mc, nc = size(C, 1), size(C, 2) if mc != N || nc != n throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($N,$n)")) end oA = oneunit(eltype(A)) unit = isunitc == 'U' @inbounds if uploc == 'U' for j in 1:n for i in 1:m Cij = (unit ? oA : conj(A[i,i])) * B[i,j] for k in i + 1:m Cij += conj(A[i,k]) * B[k,j] end C[i,j] = Cij end end else # uploc == 'L' for j in 1:n for i in m:-1:1 Cij = (unit ? oA : conj(A[i,i])) * B[i,j] for k in 1:i - 1 Cij += conj(A[i,k]) * B[k,j] end C[i,j] = Cij end end end return C end function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) require_one_based_indexing(C, A, B) m, n = size(A, 1), size(A, 2) N = size(B, 1) if n != N throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) end mc, nc = size(C, 1), size(C, 2) if mc != m || nc != N throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) end oB = oneunit(eltype(B)) unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity for i in 1:m for j in n:-1:1 Cij = A[i,j] * (unit ? oB : B[j,j]) for k in 1:j - 1 Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) for i in 1:m for j in 1:n Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) for k in j + 1:n Cij += A[i,k] * tfun(B[j,k]) end C[i,j] = Cij end end end else # uploc == 'L' if tfun === identity for i in 1:m for j in 1:n Cij = A[i,j] * (unit ? oB : B[j,j]) for k in j + 1:n Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) for i in 1:m for j in n:-1:1 Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) for k in 1:j - 1 Cij += A[i,k] * tfun(B[j,k]) end C[i,j] = Cij end end end end return C end # conjugate cases function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) B = parent(xB) require_one_based_indexing(C, A, B) m, n = size(A, 1), size(A, 2) N = size(B, 1) if n != N throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $N")) end mc, nc = size(C, 1), size(C, 2) if mc != m || nc != N throw(DimensionMismatch("output has dimensions ($mc,$nc), should have ($m,$N)")) end oB = oneunit(eltype(B)) unit = isunitc == 'U' @inbounds if uploc == 'U' for i in 1:m for j in n:-1:1 Cij = A[i,j] * (unit ? oB : conj(B[j,j])) for k in 1:j - 1 Cij += A[i,k] * conj(B[k,j]) end C[i,j] = Cij end end else # uploc == 'L' for i in 1:m for j in 1:n Cij = A[i,j] * (unit ? oB : conj(B[j,j])) for k in j + 1:n Cij += A[i,k] * conj(B[k,j]) end C[i,j] = Cij end end end return C end #Generic solver using naive substitution @inline _ustrip(a) = oneunit(a) \ a @inline _ustrip(a::Union{AbstractFloat,Integer,Complex,Rational}) = a # manually hoisting b[j] significantly improves performance as of Dec 2015 # manually eliding bounds checking significantly improves performance as of Dec 2015 # replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj] # does not significantly impact performance as of Dec 2015 # in the transpose and conjugate transpose naive substitution variants, # accumulating in z rather than b[j,k] significantly improves performance as of Dec 2015 function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) require_one_based_indexing(C, A, B) mA, nA = size(A) m, n = size(B, 1), size(B,2) if nA != m throw(DimensionMismatch("second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) end if size(C) != size(B) throw(DimensionMismatch("size of output, $(size(C)), does not match size of right hand side, $(size(B))")) end oA = oneunit(eltype(A)) @inbounds if uploc == 'U' if isunitc == 'N' if tfun === identity for k in 1:n amm = A[m,m] iszero(amm) && throw(SingularException(m)) Cm = C[m,k] = amm \ B[m,k] # fill C-column for i in m-1:-1:1 C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm end for j in m-1:-1:1 ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] for i in j-1:-1:1 C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) for k in 1:n for j in 1:m ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Bj = B[j,k] for i in 1:j-1 Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = tfun(ajj) \ Bj end end end else # isunitc == 'U' if tfun === identity for k in 1:n Cm = C[m,k] = oA \ B[m,k] # fill C-column for i in m-1:-1:1 C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm end for j in m-1:-1:1 Cj = C[j,k] for i in 1:j-1 C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) for k in 1:n for j in 1:m Bj = B[j,k] for i in 1:j-1 Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = oA \ Bj end end end end else # uploc == 'L' if isunitc == 'N' if tfun === identity for k in 1:n a11 = A[1,1] iszero(a11) && throw(SingularException(1)) C1 = C[1,k] = a11 \ B[1,k] # fill C-column for i in 2:m C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 end for j in 2:m ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] for i in j+1:m C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) for k in 1:n for j in m:-1:1 ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Bj = B[j,k] for i in j+1:m Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = tfun(ajj) \ Bj end end end else # isunitc == 'U' if tfun === identity for k in 1:n C1 = C[1,k] = oA \ B[1,k] # fill C-column for i in 2:m C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 end for j in 2:m Cj = C[j,k] for i in j+1:m C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) for k in 1:n for j in m:-1:1 Bj = B[j,k] for i in j+1:m Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = oA \ Bj end end end end end return C end # conjugate cases function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat) A = parent(xA) require_one_based_indexing(C, A, B) mA, nA = size(A) m, n = size(B, 1), size(B,2) if nA != m throw(DimensionMismatch("second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) end if size(C) != size(B) throw(DimensionMismatch("size of output, $(size(C)), does not match size of right hand side, $(size(B))")) end oA = oneunit(eltype(A)) @inbounds if uploc == 'U' if isunitc == 'N' for k in 1:n amm = conj(A[m,m]) iszero(amm) && throw(SingularException(m)) Cm = C[m,k] = amm \ B[m,k] # fill C-column for i in m-1:-1:1 C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm end for j in m-1:-1:1 ajj = conj(A[j,j]) iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] for i in j-1:-1:1 C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end else # isunitc == 'U' for k in 1:n Cm = C[m,k] = oA \ B[m,k] # fill C-column for i in m-1:-1:1 C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm end for j in m-1:-1:1 Cj = C[j,k] for i in 1:j-1 C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end end else # uploc == 'L' if isunitc == 'N' for k in 1:n a11 = conj(A[1,1]) iszero(a11) && throw(SingularException(1)) C1 = C[1,k] = a11 \ B[1,k] # fill C-column for i in 2:m C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 end for j in 2:m ajj = conj(A[j,j]) iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] for i in j+1:m C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end else # isunitc == 'U' for k in 1:n C1 = C[1,k] = oA \ B[1,k] # fill C-column for i in 2:m C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 end for j in 1:m Cj = C[j,k] for i in j+1:m C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end end end return C end function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end if size(C) != size(A) throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) end oB = oneunit(eltype(B)) unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity for i in 1:m for j in 1:n Aij = A[i,j] for k in 1:j - 1 Aij -= C[i,k]*B[k,j] end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : B[j,j]) end end else # tfun in (adjoint, transpose) for i in 1:m for j in n:-1:1 Aij = A[i,j] for k in j + 1:n Aij -= C[i,k]*tfun(B[j,k]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : tfun(B[j,j])) end end end else # uploc == 'L' if tfun === identity for i in 1:m for j in n:-1:1 Aij = A[i,j] for k in j + 1:n Aij -= C[i,k]*B[k,j] end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : B[j,j]) end end else # tfun in (adjoint, transpose) for i in 1:m for j in 1:n Aij = A[i,j] for k in 1:j - 1 Aij -= C[i,k]*tfun(B[j,k]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : tfun(B[j,j])) end end end end return C end function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) B = parent(xB) require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end if size(C) != size(A) throw(DimensionMismatch("size of output, $(size(C)), does not match size of left hand side, $(size(A))")) end oB = oneunit(eltype(B)) unit = isunitc == 'U' if uploc == 'U' @inbounds for i in 1:m for j in 1:n Aij = A[i,j] for k in 1:j - 1 Aij -= C[i,k]*conj(B[k,j]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : conj(B[j,j])) end end else # uploc == 'L' @inbounds for i in 1:m for j in n:-1:1 Aij = A[i,j] for k in j + 1:n Aij -= C[i,k]*conj(B[k,j]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) C[i,j] = Aij / (unit ? oB : conj(B[j,j])) end end end return C end # these are needed because we don't keep track of left- and right-multiplication in tritrimul! rmul!(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B)) rmul!(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(rmul!(triu!(A.data), B)) rmul!(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B)) rmul!(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(rmul!(tril!(A.data), B)) # Promotion ## Promotion methods in matmul don't apply to triangular multiplication since ## it is inplace. Hence we have to make very similar definitions, but without ## allocation of a result array. For multiplication and unit diagonal division ## the element type doesn't have to be stable under division whereas that is ## necessary in the general triangular solve problem. _inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA<:Integer,TB<:Integer} = _init_eltype(*, TA, TB) _inner_type_promotion(op, ::Type{TA}, ::Type{TB}) where {TA,TB} = _init_eltype(op, TA, TB) ## The general promotion methods function *(A::AbstractTriangular, B::AbstractTriangular) TAB = _init_eltype(*, eltype(A), eltype(B)) mul!(similar(B, TAB, size(B)), A, B) end for mat in (:AbstractVector, :AbstractMatrix) ### Multiplication with triangle to the left and hence rhs cannot be transposed. @eval function *(A::AbstractTriangular, B::$mat) require_one_based_indexing(B) TAB = _init_eltype(*, eltype(A), eltype(B)) mul!(similar(B, TAB, size(B)), A, B) end ### Left division with triangle to the left hence rhs cannot be transposed. No quotients. @eval function \(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat) require_one_based_indexing(B) TAB = _inner_type_promotion(\, eltype(A), eltype(B)) ldiv!(similar(B, TAB, size(B)), A, B) end ### Left division with triangle to the left hence rhs cannot be transposed. Quotients. @eval function \(A::Union{UpperTriangular,LowerTriangular}, B::$mat) require_one_based_indexing(B) TAB = _init_eltype(\, eltype(A), eltype(B)) ldiv!(similar(B, TAB, size(B)), A, B) end ### Right division with triangle to the right hence lhs cannot be transposed. No quotients. @eval function /(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular}) require_one_based_indexing(A) TAB = _inner_type_promotion(/, eltype(A), eltype(B)) _rdiv!(similar(A, TAB, size(A)), A, B) end ### Right division with triangle to the right hence lhs cannot be transposed. Quotients. @eval function /(A::$mat, B::Union{UpperTriangular,LowerTriangular}) require_one_based_indexing(A) TAB = _init_eltype(/, eltype(A), eltype(B)) _rdiv!(similar(A, TAB, size(A)), A, B) end end ### Multiplication with triangle to the right and hence lhs cannot be transposed. # Only for AbstractMatrix, hence outside the above loop. function *(A::AbstractMatrix, B::AbstractTriangular) require_one_based_indexing(A) TAB = _init_eltype(*, eltype(A), eltype(B)) mul!(similar(A, TAB, size(A)), A, B) end # ambiguity resolution with definitions in matmul.jl *(v::AdjointAbsVec, A::AbstractTriangular) = adjoint(adjoint(A) * v.parent) *(v::TransposeAbsVec, A::AbstractTriangular) = transpose(transpose(A) * v.parent) ## Some Triangular-Triangular cases. We might want to write tailored methods ## for these cases, but I'm not sure it is worth it. for f in (:*, :\) @eval begin ($f)(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) ($f)(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(@invoke $f(A::LowerTriangular, B::AbstractMatrix)) ($f)(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) ($f)(A::UnitLowerTriangular, B::UnitLowerTriangular) = UnitLowerTriangular(@invoke $f(A::UnitLowerTriangular, B::AbstractMatrix)) ($f)(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) ($f)(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(@invoke $f(A::UpperTriangular, B::AbstractMatrix)) ($f)(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) ($f)(A::UnitUpperTriangular, B::UnitUpperTriangular) = UnitUpperTriangular(@invoke $f(A::UnitUpperTriangular, B::AbstractMatrix)) end end (/)(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) (/)(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) (/)(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(@invoke /(A::AbstractMatrix, B::LowerTriangular)) (/)(A::UnitLowerTriangular, B::UnitLowerTriangular) = UnitLowerTriangular(@invoke /(A::AbstractMatrix, B::UnitLowerTriangular)) (/)(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) (/)(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) (/)(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(@invoke /(A::AbstractMatrix, B::UpperTriangular)) (/)(A::UnitUpperTriangular, B::UnitUpperTriangular) = UnitUpperTriangular(@invoke /(A::AbstractMatrix, B::UnitUpperTriangular)) # Complex matrix power for upper triangular factor, see: # Higham and Lin, "A Schur-Padรฉ algorithm for fractional powers of a Matrix", # SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 1056โ€“1078. # Higham and Lin, "An improved Schur-Padรฉ algorithm for fractional powers of # a matrix and their Frรฉchet derivatives", SIAM. J. Matrix Anal. & Appl., # 34(3), (2013) 1341โ€“1360. function powm!(A0::UpperTriangular, p::Real) if abs(p) >= 1 throw(ArgumentError("p must be a real number in (-1,1), got $p")) end normA0 = opnorm(A0, 1) rmul!(A0, 1/normA0) theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1] n = checksquare(A0) A, m, s = invsquaring(A0, theta) A = I - A # Compute accurate diagonal of I - T sqrt_diag!(A0, A, s) for i = 1:n A[i, i] = -A[i, i] end # Compute the Padรฉ approximant c = 0.5 * (p - m) / (2 * m - 1) triu!(A) S = c * A Stmp = similar(S) for j = m-1:-1:1 j4 = 4 * j c = (-p - j) / (j4 + 2) for i = 1:n @inbounds S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) mul!(S, A, c) ldiv!(Stmp, S) c = (p - j) / (j4 - 2) for i = 1:n @inbounds S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) mul!(S, A, c) ldiv!(Stmp, S) end for i = 1:n S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) mul!(S, A, -p) ldiv!(Stmp, S) for i = 1:n @inbounds S[i, i] = S[i, i] + 1 end blockpower!(A0, S, p/(2^s)) for m = 1:s mul!(Stmp.data, S, S) copyto!(S, Stmp) blockpower!(A0, S, p/(2^(s-m))) end rmul!(S, normA0^p) return S end powm(A::LowerTriangular, p::Real) = copy(transpose(powm!(copy(transpose(A)), p::Real))) # Complex matrix logarithm for the upper triangular factor, see: # Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for # the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153โ€“C169. # Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix # logarithm and estimating the condition number", SIAM J. Sci. Comput., # 35(4), (2013), C394โ€“C410. # # Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip, # Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham # Julia version relicensed with permission from original authors log(A::UpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A) log(A::UnitUpperTriangular{T}) where {T<:BlasFloat} = log_quasitriu(A) log(A::LowerTriangular) = copy(transpose(log(copy(transpose(A))))) log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A))))) function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat # allocate real A if log(A) will be real and complex A otherwise n = checksquare(A0) if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0))) A = T <: Complex ? real(A0) : copy(A0) else A = T <: Complex ? copy(A0) : complex(A0) end if A0 isa UnitUpperTriangular A = UpperTriangular(parent(A)) @inbounds for i in 1:n A[i,i] = 1 end end Y0 = _log_quasitriu!(A0, A) # return complex result for complex input Y = T <: Complex ? complex(Y0) : Y0 if A0 isa UpperTriangular || A0 isa UnitUpperTriangular return UpperTriangular(Y) else return Y end end # type-stable implementation of log_quasitriu # A is a copy of A0 that is overwritten while computing the result. It has the same eltype # as the result. function _log_quasitriu!(A0, A) # Find Padรฉ degree m and s while replacing A with A^(1/2^s) m, s = _find_params_log_quasitriu!(A) # Compute accurate superdiagonal of A _pow_superdiag_quasitriu!(A, A0, 0.5^s) # Compute accurate block diagonal of A _sqrt_pow_diag_quasitriu!(A, A0, s) # Get the Gauss-Legendre quadrature points and weights R = zeros(Float64, m, m) for i = 1:m - 1 R[i,i+1] = i / sqrt((2 * i)^2 - 1) R[i+1,i] = R[i,i+1] end x,V = eigen(R) w = Vector{Float64}(undef, m) for i = 1:m x[i] = (x[i] + 1) / 2 w[i] = V[1,i]^2 end # Compute the Padรฉ approximation t = eltype(A) n = size(A, 1) Y = zeros(t, n, n) B = similar(A) for k = 1:m B .= t(x[k]) .* A @inbounds for i in 1:n B[i,i] += 1 end Y .+= t(w[k]) .* rdiv_quasitriu!(A, B) end # Scale back lmul!(2.0^s, Y) # Compute accurate diagonal and superdiagonal of log(A) _log_diag_quasitriu!(Y, A0) return Y end # Auxiliary functions for matrix logarithm and matrix power # Find Padรฉ degree m and s while replacing A with A^(1/2^s) # Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for # the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153โ€“C169. # from Algorithm 4.1 function _find_params_log_quasitriu!(A) maxsqrt = 100 theta = [1.586970738772063e-005, 2.313807884242979e-003, 1.938179313533253e-002, 6.209171588994762e-002, 1.276404810806775e-001, 2.060962623452836e-001, 2.879093714241194e-001] tmax = size(theta, 1) n = size(A, 1) p = 0 m = 0 # Find s0, the smallest s such that the ฯ(triu(A)^(1/2^s) - I) โ‰ค theta[tmax], where ฯ(X) # is the spectral radius of X d = complex.(@view(A[diagind(A)])) dm1 = d .- 1 s = 0 while norm(dm1, Inf) > theta[tmax] && s < maxsqrt d .= sqrt.(d) dm1 .= d .- 1 s = s + 1 end s0 = s # Compute repeated roots for k = 1:min(s, maxsqrt) _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) end # these three never needed at the same time, so reuse the same temporary AmI = AmI4 = AmI5 = A - I AmI2 = AmI * AmI AmI3 = AmI2 * AmI d2 = sqrt(opnorm(AmI2, 1)) d3 = cbrt(opnorm(AmI3, 1)) alpha2 = max(d2, d3) foundm = false if alpha2 <= theta[2] m = alpha2 <= theta[1] ? 1 : 2 foundm = true end while !foundm more_sqrt = false mul!(AmI4, AmI2, AmI2) d4 = opnorm(AmI4, 1)^(1/4) alpha3 = max(d3, d4) if alpha3 <= theta[tmax] local j for outer j = 3:tmax if alpha3 <= theta[j] break end end if j <= 6 m = j break elseif alpha3 / 2 <= theta[5] && p < 2 more_sqrt = true p = p + 1 end end if !more_sqrt mul!(AmI5, AmI3, AmI2) d5 = opnorm(AmI5, 1)^(1/5) alpha4 = max(d4, d5) eta = min(alpha3, alpha4) if eta <= theta[tmax] j = 0 for outer j = 6:tmax if eta <= theta[j] m = j break end end break end end if s == maxsqrt m = tmax break end _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) copyto!(AmI, A) for i in 1:n @inbounds AmI[i,i] -= 1 end mul!(AmI2, AmI, AmI) mul!(AmI3, AmI2, AmI) d3 = cbrt(opnorm(AmI3, 1)) s = s + 1 end return m, s end # Compute accurate diagonal of A = A0^s - I function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s) n = checksquare(A0) T = eltype(A) @inbounds for i = 1:n a = complex(A0[i,i]) A[i,i] = _sqrt_pow(a, s) end end # Compute accurate block diagonal of A = A0^s - I for upper quasi-triangular A0 produced # by the Schur decomposition. Diagonal is made of 1x1 and 2x2 blocks. # 2x2 blocks are real with non-negative conjugate pair eigenvalues function _sqrt_pow_diag_quasitriu!(A, A0, s) n = checksquare(A0) t = typeof(sqrt(zero(eltype(A)))) i = 1 @inbounds while i < n if iszero(A0[i+1,i]) # 1x1 block A[i,i] = _sqrt_pow(t(A0[i,i]), s) i += 1 else # real 2x2 block @views _sqrt_pow_diag_block_2x2!(A[i:i+1,i:i+1], A0[i:i+1,i:i+1], s) i += 2 end end if i == n # last block is 1x1 @inbounds A[n,n] = _sqrt_pow(t(A0[n,n]), s) end return A end # compute a^(1/2^s)-1 # Al-Mohy, "A more accurate Briggs method for the logarithm", # Numer. Algorithms, 59, (2012), 393โ€“402. # Algorithm 2 function _sqrt_pow(a::Number, s) T = typeof(sqrt(zero(a))) s == 0 && return T(a) - 1 s0 = s if imag(a) >= 0 && real(a) <= 0 && !iszero(a) # angle(a) โ‰ฅ ฯ€ / 2 a = sqrt(a) s0 = s - 1 end z0 = a - 1 a = sqrt(a) r = 1 + a for j = 1:s0-1 a = sqrt(a) r = r * (1 + a) end return z0 / r end # compute A0 = A^(1/2^s)-I for 2x2 real matrices A and A0 # A has non-negative conjugate pair eigenvalues # "Improved Inverse Scaling and Squaring Algorithms for the Matrix Logarithm" # SIAM J. Sci. Comput., 34(4), (2012) C153โ€“C169. doi: 10.1137/110852553 # Algorithm 5.1 Base.@propagate_inbounds function _sqrt_pow_diag_block_2x2!(A, A0, s) _sqrt_real_2x2!(A, A0) if isone(s) A[1,1] -= 1 A[2,2] -= 1 else # Z = A - I z11, z21, z12, z22 = A[1,1] - 1, A[2,1], A[1,2], A[2,2] - 1 # A = sqrt(A) _sqrt_real_2x2!(A, A) # P = A + I p11, p21, p12, p22 = A[1,1] + 1, A[2,1], A[1,2], A[2,2] + 1 for i in 1:(s - 2) # A = sqrt(A) _sqrt_real_2x2!(A, A) a11, a21, a12, a22 = A[1,1], A[2,1], A[1,2], A[2,2] # P += P * A r11 = p11*(1 + a11) + p12*a21 r22 = p21*a12 + p22*(1 + a22) p21 = p21*(1 + a11) + p22*a21 p12 = p11*a12 + p12*(1 + a22) p11 = r11 p22 = r22 end # A = Z / P c = inv(p11*p22 - p21*p12) A[1,1] = (p22*z11 - p21*z12) * c A[2,1] = (p22*z21 - p21*z22) * c A[1,2] = (p11*z12 - p12*z11) * c A[2,2] = (p11*z22 - p12*z21) * c end return A end # Compute accurate superdiagonal of A = A0^s - I for upper quasi-triangular A0 produced # by a Schur decomposition. # Higham and Lin, "A Schurโ€“Padรฉ Algorithm for Fractional Powers of a Matrix" # SIAM J. Matrix Anal. Appl., 32(3), (2011), 1056โ€“1078. # Equation 5.6 # see also blockpower for when A0 is upper triangular function _pow_superdiag_quasitriu!(A, A0, p) n = checksquare(A0) t = eltype(A) k = 1 @inbounds while k < n if !iszero(A[k+1,k]) k += 2 continue end if !(k == n - 1 || iszero(A[k+2,k+1])) k += 3 continue end Ak = t(A0[k,k]) Akp1 = t(A0[k+1,k+1]) Akp = Ak^p Akp1p = Akp1^p if Ak == Akp1 A[k,k+1] = p * A0[k,k+1] * Ak^(p-1) elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) else logAk = log(Ak) logAkp1 = log(Akp1) z = (Akp1 - Ak)/(Akp1 + Ak) if abs(z) > 1 A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) else w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak); A[k,k+1] = A0[k,k+1] * dd end end k += 1 end end # Compute accurate block diagonal and superdiagonal of A = log(A0) for upper # quasi-triangular A0 produced by the Schur decomposition. function _log_diag_quasitriu!(A, A0) n = checksquare(A0) t = eltype(A) k = 1 @inbounds while k < n if iszero(A0[k+1,k]) # 1x1 block Ak = t(A0[k,k]) logAk = log(Ak) A[k,k] = logAk if k < n - 2 && iszero(A0[k+2,k+1]) Akp1 = t(A0[k+1,k+1]) logAkp1 = log(Akp1) A[k+1,k+1] = logAkp1 if Ak == Akp1 A[k,k+1] = A0[k,k+1] / Ak elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak) else z = (Akp1 - Ak)/(Akp1 + Ak) if abs(z) > 1 A[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak) else w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) A[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak) end end k += 2 else k += 1 end else # real 2x2 block @views _log_diag_block_2x2!(A[k:k+1,k:k+1], A0[k:k+1,k:k+1]) k += 2 end end if k == n # last 1x1 block @inbounds A[n,n] = log(t(A0[n,n])) end return A end # compute A0 = log(A) for 2x2 real matrices A and A0, where A0 is a diagonal 2x2 block # produced by real Schur decomposition. # Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix # logarithm and estimating the condition number", SIAM J. Sci. Comput., # 35(4), (2013), C394โ€“C410. # Eq. 6.1 Base.@propagate_inbounds function _log_diag_block_2x2!(A, A0) a, b, c = A0[1,1], A0[1,2], A0[2,1] # avoid underflow/overflow for large/small b and c s = sqrt(abs(b)) * sqrt(abs(c)) ฮธ = atan(s, a) t = ฮธ / s au = abs(a) if au > s a1 = log1p((s / au)^2) / 2 + log(au) else a1 = log1p((au / s)^2) / 2 + log(s) end A[1,1] = a1 A[2,1] = c*t A[1,2] = b*t A[2,2] = a1 return A end # Used only by powm at the moment # Repeatedly compute the square roots of A so that in the end its # eigenvalues are close enough to the positive real line function invsquaring(A0::UpperTriangular, theta) require_one_based_indexing(theta) # assumes theta is in ascending order maxsqrt = 100 tmax = size(theta, 1) n = checksquare(A0) A = complex(copy(A0)) p = 0 m = 0 # Compute repeated roots d = complex(diag(A)) dm1 = d .- 1 s = 0 while norm(dm1, Inf) > theta[tmax] && s < maxsqrt d .= sqrt.(d) dm1 .= d .- 1 s = s + 1 end s0 = s for k = 1:min(s, maxsqrt) A = sqrt(A) end AmI = A - I d2 = sqrt(opnorm(AmI^2, 1)) d3 = cbrt(opnorm(AmI^3, 1)) alpha2 = max(d2, d3) foundm = false if alpha2 <= theta[2] m = alpha2 <= theta[1] ? 1 : 2 foundm = true end while !foundm more = false if s > s0 d3 = cbrt(opnorm(AmI^3, 1)) end d4 = opnorm(AmI^4, 1)^(1/4) alpha3 = max(d3, d4) if alpha3 <= theta[tmax] local j for outer j = 3:tmax if alpha3 <= theta[j] break elseif alpha3 / 2 <= theta[5] && p < 2 more = true p = p + 1 end end if j <= 6 m = j foundm = true break elseif alpha3 / 2 <= theta[5] && p < 2 more = true p = p + 1 end end if !more d5 = opnorm(AmI^5, 1)^(1/5) alpha4 = max(d4, d5) eta = min(alpha3, alpha4) if eta <= theta[tmax] j = 0 for outer j = 6:tmax if eta <= theta[j] m = j break end break end end if s == maxsqrt m = tmax break end A = sqrt(A) AmI = A - I s = s + 1 end end # Compute accurate superdiagonal of T p = 1 / 2^s A = complex(A) blockpower!(A, A0, p) return A,m,s end # Compute accurate diagonal and superdiagonal of A = A0^p function blockpower!(A::UpperTriangular, A0::UpperTriangular, p) n = checksquare(A0) @inbounds for k = 1:n-1 Ak = complex(A0[k,k]) Akp1 = complex(A0[k+1,k+1]) Akp = Ak^p Akp1p = Akp1^p A[k,k] = Akp A[k+1,k+1] = Akp1p if Ak == Akp1 A[k,k+1] = p * A0[k,k+1] * Ak^(p-1) elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak) A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) else logAk = log(Ak) logAkp1 = log(Akp1) z = (Akp1 - Ak)/(Akp1 + Ak) if abs(z) > 1 A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak) else w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z))) dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak); A[k,k+1] = A0[k,k+1] * dd end end end end # Unwinding number unw(x::Real) = 0 unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) # compute A / B for upper quasi-triangular B, possibly overwriting B function rdiv_quasitriu!(A, B) n = checksquare(A) AG = copy(A) # use Givens rotations to annihilate 2x2 blocks @inbounds for k in 1:(n-1) s = B[k+1,k] iszero(s) && continue # 1x1 block G = first(givens(B[k+1,k+1], s, k, k+1)) rmul!(B, G) rmul!(AG, G) end return rdiv!(AG, UpperTriangular(B)) end # End of auxiliary functions for matrix logarithm and matrix power sqrt(A::UpperTriangular) = sqrt_quasitriu(A) function sqrt(A::UnitUpperTriangular{T}) where T B = A.data n = checksquare(B) t = typeof(sqrt(zero(T))) R = Matrix{t}(I, n, n) tt = typeof(oneunit(t)*oneunit(t)) half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214 @inbounds for j = 1:n for i = j-1:-1:1 r::tt = B[i,j] @simd for k = i+1:j-1 r -= R[i,k]*R[k,j] end iszero(r) || (R[i,j] = half*r) end end return UnitUpperTriangular(R) end sqrt(A::LowerTriangular) = copy(transpose(sqrt(copy(transpose(A))))) sqrt(A::UnitLowerTriangular) = copy(transpose(sqrt(copy(transpose(A))))) # Auxiliary functions for matrix square root # square root of upper triangular or real upper quasitriangular matrix function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256) n = checksquare(A0) T = eltype(A0) Tr = typeof(sqrt(real(zero(T)))) Tc = typeof(sqrt(complex(zero(T)))) if isreal(A0) is_sqrt_real = true if istriu(A0) for i in 1:n Aii = real(A0[i,i]) if Aii < zero(Aii) is_sqrt_real = false break end end end if is_sqrt_real R = zeros(Tr, n, n) A = real(A0) else R = zeros(Tc, n, n) A = A0 end else A = A0 R = zeros(Tc, n, n) end _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n) Rc = eltype(A0) <: Real ? R : complex(R) if A0 isa UpperTriangular return UpperTriangular(Rc) elseif A0 isa UnitUpperTriangular return UnitUpperTriangular(Rc) else return Rc end end # in-place recursive sqrt of upper quasi-triangular matrix A from # Deadman E., Higham N.J., Ralha R. (2013) Blocked Schur Algorithms for Computing the Matrix # Square Root. Applied Parallel and Scientific Computing. PARA 2012. Lecture Notes in # Computer Science, vol 7782. https://doi.org/10.1007/978-3-642-36803-5_12 function _sqrt_quasitriu!(R, A; blockwidth=64, n=checksquare(A)) if n โ‰ค blockwidth || !(eltype(R) <: BlasFloat) # base case, perform "point" algorithm _sqrt_quasitriu_block!(R, A) else # compute blockwise recursion split = div(n, 2) iszero(A[split+1, split]) || (split += 1) # don't split 2x2 diagonal block r1 = 1:split r2 = (split + 1):n n1, n2 = split, n - split A11, A12, A22 = @views A[r1,r1], A[r1,r2], A[r2,r2] R11, R12, R22 = @views R[r1,r1], R[r1,r2], R[r2,r2] # solve diagonal blocks recursively _sqrt_quasitriu!(R11, A11; blockwidth=blockwidth, n=n1) _sqrt_quasitriu!(R22, A22; blockwidth=blockwidth, n=n2) # solve off-diagonal block R12 .= .- A12 _sylvester_quasitriu!(R11, R22, R12; blockwidth=blockwidth, nA=n1, nB=n2, raise=false) end return R end function _sqrt_quasitriu_block!(R, A) _sqrt_quasitriu_diag_block!(R, A) _sqrt_quasitriu_offdiag_block!(R, A) return R end function _sqrt_quasitriu_diag_block!(R, A) n = size(R, 1) ta = eltype(R) <: Complex ? complex(eltype(A)) : eltype(A) i = 1 @inbounds while i < n if iszero(A[i + 1, i]) R[i, i] = sqrt(ta(A[i, i])) i += 1 else # this branch is never reached when A is complex triangular @views _sqrt_real_2x2!(R[i:(i + 1), i:(i + 1)], A[i:(i + 1), i:(i + 1)]) i += 2 end end if i == n R[n, n] = sqrt(ta(A[n, n])) end return R end function _sqrt_quasitriu_offdiag_block!(R, A) n = size(R, 1) j = 1 @inbounds while j โ‰ค n jsize_is_2 = j < n && !iszero(A[j + 1, j]) i = j - 1 while i > 0 isize_is_2 = i > 1 && !iszero(A[i, i - 1]) if isize_is_2 if jsize_is_2 _sqrt_quasitriu_offdiag_block_2x2!(R, A, i - 1, j) else _sqrt_quasitriu_offdiag_block_2x1!(R, A, i - 1, j) end i -= 2 else if jsize_is_2 _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j) else _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j) end i -= 1 end end j += 2 - !jsize_is_2 end return R end # real square root of 2x2 diagonal block of quasi-triangular matrix from real Schur # decomposition. Eqs 6.8-6.9 and Algorithm 6.5 of # Higham, 2008, "Functions of Matrices: Theory and Computation", SIAM. Base.@propagate_inbounds function _sqrt_real_2x2!(R, A) # in the real Schur form, A[1, 1] == A[2, 2], and A[2, 1] * A[1, 2] < 0 ฮธ, a21, a12 = A[1, 1], A[2, 1], A[1, 2] # avoid overflow/underflow of ฮผ # for real sqrt, |d| โ‰ค 2 max(|a12|,|a21|) ฮผ = sqrt(abs(a12)) * sqrt(abs(a21)) ฮฑ = _real_sqrt(ฮธ, ฮผ) c = 2ฮฑ R[1, 1] = ฮฑ R[2, 1] = a21 / c R[1, 2] = a12 / c R[2, 2] = ฮฑ return R end # real part of square root of ฮธ+im*ฮผ @inline function _real_sqrt(ฮธ, ฮผ) t = sqrt((abs(ฮธ) + hypot(ฮธ, ฮผ)) / 2) return ฮธ โ‰ฅ 0 ? t : ฮผ / 2t end Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x1!(R, A, i, j) Rii = R[i, i] Rjj = R[j, j] iszero(Rii) && iszero(Rjj) && return R t = eltype(R) tt = typeof(zero(t)*zero(t)) r = tt(-A[i, j]) @simd for k in (i + 1):(j - 1) r += R[i, k] * R[k, j] end iszero(r) && return R R[i, j] = sylvester(Rii, Rjj, r) return R end Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_1x2!(R, A, i, j) jrange = j:(j + 1) t = eltype(R) tt = typeof(zero(t)*zero(t)) r1 = tt(-A[i, j]) r2 = tt(-A[i, j + 1]) @simd for k in (i + 1):(j - 1) rik = R[i, k] r1 += rik * R[k, j] r2 += rik * R[k, j + 1] end Rjj = @view R[jrange, jrange] Rij = @view R[i, jrange] Rij[1] = r1 Rij[2] = r2 _sylvester_1x2!(R[i, i], Rjj, Rij) return R end Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x1!(R, A, i, j) irange = i:(i + 1) t = eltype(R) tt = typeof(zero(t)*zero(t)) r1 = tt(-A[i, j]) r2 = tt(-A[i + 1, j]) @simd for k in (i + 2):(j - 1) rkj = R[k, j] r1 += R[i, k] * rkj r2 += R[i + 1, k] * rkj end Rii = @view R[irange, irange] Rij = @view R[irange, j] Rij[1] = r1 Rij[2] = r2 @views _sylvester_2x1!(Rii, R[j, j], Rij) return R end Base.@propagate_inbounds function _sqrt_quasitriu_offdiag_block_2x2!(R, A, i, j) irange = i:(i + 1) jrange = j:(j + 1) t = eltype(R) tt = typeof(zero(t)*zero(t)) for iโ€ฒ in irange, jโ€ฒ in jrange Cij = tt(-A[iโ€ฒ, jโ€ฒ]) @simd for k in (i + 2):(j - 1) Cij += R[iโ€ฒ, k] * R[k, jโ€ฒ] end R[iโ€ฒ, jโ€ฒ] = Cij end Rii = @view R[irange, irange] Rjj = @view R[jrange, jrange] Rij = @view R[irange, jrange] if !iszero(Rij) && !all(isnan, Rij) _sylvester_2x2!(Rii, Rjj, Rij) end return R end # solve Sylvester's equation AX + XB = -C using blockwise recursion until the dimension of # A and B are no greater than blockwidth, based on Algorithm 1 from # Jonsson I, Kรฅgstrรถm B. Recursive blocked algorithms for solving triangular systemsโ€” # Part I: one-sided and coupled Sylvester-type matrix equations. (2002) ACM Trans Math Softw. # 28(4), https://doi.org/10.1145/592843.592845. # specify raise=false to avoid breaking the recursion if a LAPACKException is thrown when # computing one of the blocks. function _sylvester_quasitriu!(A, B, C; blockwidth=64, nA=checksquare(A), nB=checksquare(B), raise=true) if 1 โ‰ค nA โ‰ค blockwidth && 1 โ‰ค nB โ‰ค blockwidth _sylvester_quasitriu_base!(A, B, C; raise=raise) elseif nA โ‰ฅ 2nB โ‰ฅ 2 _sylvester_quasitriu_split1!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) elseif nB โ‰ฅ 2nA โ‰ฅ 2 _sylvester_quasitriu_split2!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) else _sylvester_quasitriu_splitall!(A, B, C; blockwidth=blockwidth, nA=nA, nB=nB, raise=raise) end return C end function _sylvester_quasitriu_base!(A, B, C; raise=true) try _, scale = LAPACK.trsyl!('N', 'N', A, B, C) rmul!(C, -inv(scale)) catch e if !(e isa LAPACKException) || raise throw(e) end end return C end function _sylvester_quasitriu_split1!(A, B, C; nA=checksquare(A), kwargs...) iA = div(nA, 2) iszero(A[iA + 1, iA]) || (iA += 1) # don't split 2x2 diagonal block rA1, rA2 = 1:iA, (iA + 1):nA nA1, nA2 = iA, nA-iA A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2] C1, C2 = @views C[rA1,:], C[rA2,:] _sylvester_quasitriu!(A22, B, C2; nA=nA2, kwargs...) mul!(C1, A12, C2, true, true) _sylvester_quasitriu!(A11, B, C1; nA=nA1, kwargs...) return C end function _sylvester_quasitriu_split2!(A, B, C; nB=checksquare(B), kwargs...) iB = div(nB, 2) iszero(B[iB + 1, iB]) || (iB += 1) # don't split 2x2 diagonal block rB1, rB2 = 1:iB, (iB + 1):nB nB1, nB2 = iB, nB-iB B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2] C1, C2 = @views C[:,rB1], C[:,rB2] _sylvester_quasitriu!(A, B11, C1; nB=nB1, kwargs...) mul!(C2, C1, B12, true, true) _sylvester_quasitriu!(A, B22, C2; nB=nB2, kwargs...) return C end function _sylvester_quasitriu_splitall!(A, B, C; nA=checksquare(A), nB=checksquare(B), kwargs...) iA = div(nA, 2) iszero(A[iA + 1, iA]) || (iA += 1) # don't split 2x2 diagonal block iB = div(nB, 2) iszero(B[iB + 1, iB]) || (iB += 1) # don't split 2x2 diagonal block rA1, rA2 = 1:iA, (iA + 1):nA nA1, nA2 = iA, nA-iA rB1, rB2 = 1:iB, (iB + 1):nB nB1, nB2 = iB, nB-iB A11, A12, A22 = @views A[rA1,rA1], A[rA1,rA2], A[rA2,rA2] B11, B12, B22 = @views B[rB1,rB1], B[rB1,rB2], B[rB2,rB2] C11, C21, C12, C22 = @views C[rA1,rB1], C[rA2,rB1], C[rA1,rB2], C[rA2,rB2] _sylvester_quasitriu!(A22, B11, C21; nA=nA2, nB=nB1, kwargs...) mul!(C11, A12, C21, true, true) _sylvester_quasitriu!(A11, B11, C11; nA=nA1, nB=nB1, kwargs...) mul!(C22, C21, B12, true, true) _sylvester_quasitriu!(A22, B22, C22; nA=nA2, nB=nB2, kwargs...) mul!(C12, A12, C22, true, true) mul!(C12, C11, B12, true, true) _sylvester_quasitriu!(A11, B22, C12; nA=nA1, nB=nB2, kwargs...) return C end # End of auxiliary functions for matrix square root # Generic eigensystems eigvals(A::AbstractTriangular) = diag(A) function eigvecs(A::AbstractTriangular{T}) where T TT = promote_type(T, Float32) if TT <: BlasFloat return eigvecs(convert(AbstractMatrix{TT}, A)) else throw(ArgumentError("eigvecs type $(typeof(A)) not supported. Please submit a pull request.")) end end det(A::UnitUpperTriangular{T}) where {T} = one(T) det(A::UnitLowerTriangular{T}) where {T} = one(T) logdet(A::UnitUpperTriangular{T}) where {T} = zero(T) logdet(A::UnitLowerTriangular{T}) where {T} = zero(T) logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T) logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T) det(A::UpperTriangular) = prod(diag(A.data)) det(A::LowerTriangular) = prod(diag(A.data)) function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T sgn = one(T) abs_det = zero(real(T)) @inbounds for i in 1:size(A,1) diag_i = A.data[i,i] sgn *= sign(diag_i) abs_det += log(abs(diag_i)) end return abs_det, sgn end eigen(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A)) # Generic singular systems for func in (:svd, :svd!, :svdvals) @eval begin ($func)(A::AbstractTriangular; kwargs...) = ($func)(copyto!(similar(parent(A)), A); kwargs...) end end factorize(A::AbstractTriangular) = A # disambiguation methods: /(Adjoint of AbsVec, <:AbstractTriangular) /(u::AdjointAbsVec, A::Union{LowerTriangular,UpperTriangular}) = adjoint(adjoint(A) \ u.parent) /(u::AdjointAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = adjoint(adjoint(A) \ u.parent) # disambiguation methods: /(Transpose of AbsVec, <:AbstractTriangular) /(u::TransposeAbsVec, A::Union{LowerTriangular,UpperTriangular}) = transpose(transpose(A) \ u.parent) /(u::TransposeAbsVec, A::Union{UnitLowerTriangular,UnitUpperTriangular}) = transpose(transpose(A) \ u.parent) # disambiguation methods: /(Transpose of AbsVec, Adj/Trans of <:AbstractTriangular) for (tritype, comptritype) in ((:LowerTriangular, :UpperTriangular), (:UnitLowerTriangular, :UnitUpperTriangular), (:UpperTriangular, :LowerTriangular), (:UnitUpperTriangular, :UnitLowerTriangular)) @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Adjoint}) = transpose($comptritype(conj(parent(parent(A)))) \ u.parent) @eval /(u::TransposeAbsVec, A::$tritype{<:Any,<:Transpose}) = transpose(transpose(A) \ u.parent) end ~/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/factorization.jlย# This file is a part of Julia. License is MIT: https://julialang.org/license ## Matrix factorizations and decompositions """ LinearAlgebra.Factorization Abstract type for [matrix factorizations](https://en.wikipedia.org/wiki/Matrix_decomposition) a.k.a. matrix decompositions. See [online documentation](@ref man-linalg-factorizations) for a list of available matrix factorizations. """ abstract type Factorization{T} end """ AdjointFactorization Lazy wrapper type for the adjoint of the underlying `Factorization` object. Usually, the `AdjointFactorization` constructor should not be called directly, use [`adjoint(:: Factorization)`](@ref) instead. """ struct AdjointFactorization{T,S<:Factorization} <: Factorization{T} parent::S end AdjointFactorization(F::Factorization) = AdjointFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) """ TransposeFactorization Lazy wrapper type for the transpose of the underlying `Factorization` object. Usually, the `TransposeFactorization` constructor should not be called directly, use [`transpose(:: Factorization)`](@ref) instead. """ struct TransposeFactorization{T,S<:Factorization} <: Factorization{T} parent::S end TransposeFactorization(F::Factorization) = TransposeFactorization{Base.promote_op(adjoint,eltype(F)),typeof(F)}(F) eltype(::Type{<:Factorization{T}}) where {T} = T size(F::AdjointFactorization) = reverse(size(parent(F))) size(F::TransposeFactorization) = reverse(size(parent(F))) size(F::Union{AdjointFactorization,TransposeFactorization}, d::Integer) = d in (1, 2) ? size(F)[d] : 1 parent(F::Union{AdjointFactorization,TransposeFactorization}) = F.parent """ adjoint(F::Factorization) Lazy adjoint of the factorization `F`. By default, returns an [`AdjointFactorization`](@ref) wrapper. """ adjoint(F::Factorization) = AdjointFactorization(F) """ transpose(F::Factorization) Lazy transpose of the factorization `F`. By default, returns a [`TransposeFactorization`](@ref), except for `Factorization`s with real `eltype`, in which case returns an [`AdjointFactorization`](@ref). """ transpose(F::Factorization) = TransposeFactorization(F) transpose(F::Factorization{<:Real}) = AdjointFactorization(F) adjoint(F::AdjointFactorization) = F.parent transpose(F::TransposeFactorization) = F.parent transpose(F::AdjointFactorization{<:Real}) = F.parent conj(A::TransposeFactorization) = adjoint(A.parent) conj(A::AdjointFactorization) = transpose(A.parent) checkpositivedefinite(info) = info == 0 || throw(PosDefException(info)) checknonsingular(info, ::RowMaximum) = info == 0 || throw(SingularException(info)) checknonsingular(info, ::RowNonZero) = info == 0 || throw(SingularException(info)) checknonsingular(info, ::NoPivot) = info == 0 || throw(ZeroPivotException(info)) checknonsingular(info) = checknonsingular(info, RowMaximum()) """ issuccess(F::Factorization) Test that a factorization of a matrix succeeded. !!! compat "Julia 1.6" `issuccess(::CholeskyPivoted)` requires Julia 1.6 or later. ```jldoctest julia> F = cholesky([1 0; 0 1]); julia> issuccess(F) true julia> F = lu([1 0; 0 0]; check = false); julia> issuccess(F) false ``` """ issuccess(F::Factorization) function logdet(F::Factorization) d, s = logabsdet(F) return d + log(s) end function det(F::Factorization) d, s = logabsdet(F) return exp(d)*s end convert(::Type{T}, f::T) where {T<:Factorization} = f convert(::Type{T}, f::Factorization) where {T<:Factorization} = T(f)::T convert(::Type{T}, f::Factorization) where {T<:AbstractArray} = T(f)::T ### General promotion rules Factorization{T}(F::Factorization{T}) where {T} = F # This no longer looks odd since the return _is_ a Factorization! Factorization{T}(A::AdjointFactorization) where {T} = adjoint(Factorization{T}(parent(A))) Factorization{T}(A::TransposeFactorization) where {T} = transpose(Factorization{T}(parent(A))) inv(F::Factorization{T}) where {T} = (n = size(F, 1); ldiv!(F, Matrix{T}(I, n, n))) Base.hash(F::Factorization, h::UInt) = mapreduce(f -> hash(getfield(F, f)), hash, 1:nfields(F); init=h) Base.:(==)( F::T, G::T) where {T<:Factorization} = all(f -> getfield(F, f) == getfield(G, f), 1:nfields(F)) Base.isequal(F::T, G::T) where {T<:Factorization} = all(f -> isequal(getfield(F, f), getfield(G, f)), 1:nfields(F))::Bool function Base.show(io::IO, x::AdjointFactorization) print(io, "adjoint of ") show(io, parent(x)) end function Base.show(io::IO, x::TransposeFactorization) print(io, "transpose of ") show(io, parent(x)) end function Base.show(io::IO, ::MIME"text/plain", x::AdjointFactorization) print(io, "adjoint of ") show(io, MIME"text/plain"(), parent(x)) end function Base.show(io::IO, ::MIME"text/plain", x::TransposeFactorization) print(io, "transpose of ") show(io, MIME"text/plain"(), parent(x)) end function (\)(F::Factorization, B::AbstractVecOrMat) require_one_based_indexing(B) TFB = typeof(oneunit(eltype(F)) \ oneunit(eltype(B))) ldiv!(F, copy_similar(B, TFB)) end (\)(F::TransposeFactorization, B::AbstractVecOrMat) = conj!(adjoint(F.parent) \ conj.(B)) # With a real lhs and complex rhs with the same precision, we can reinterpret # the complex rhs as a real rhs with twice the number of columns or rows function (\)(F::Factorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} require_one_based_indexing(B) c2r = reshape(copy(transpose(reinterpret(T, reshape(B, (1, length(B)))))), size(B, 1), 2*size(B, 2)) x = ldiv!(F, c2r) return reshape(copy(reinterpret(Complex{T}, copy(transpose(reshape(x, div(length(x), 2), 2))))), _ret_size(F, B)) end # don't do the reinterpretation for [Adjoint/Transpose]Factorization (\)(F::TransposeFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = conj!(adjoint(parent(F)) \ conj.(B)) (\)(F::AdjointFactorization{T}, B::VecOrMat{Complex{T}}) where {T<:BlasReal} = @invoke \(F::typeof(F), B::VecOrMat) function ldiv!(Y::AbstractVector, A::Factorization, B::AbstractVector) require_one_based_indexing(Y, B) m, n = size(A) if m > n Bc = copy(B) ldiv!(A, Bc) return copyto!(Y, 1, Bc, 1, n) else return ldiv!(A, copyto!(Y, B)) end end function ldiv!(Y::AbstractMatrix, A::Factorization, B::AbstractMatrix) require_one_based_indexing(Y, B) m, n = size(A) if m > n Bc = copy(B) ldiv!(A, Bc) return copyto!(Y, view(Bc, 1:n, :)) else copyto!(view(Y, 1:m, :), view(B, 1:m, :)) return ldiv!(A, Y) end end function (/)(B::AbstractMatrix, F::Factorization) require_one_based_indexing(B) TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F))) rdiv!(copy_similar(B, TFB), F) end # reinterpretation trick for complex lhs and real factorization function (/)(B::Union{Matrix{Complex{T}},AdjOrTrans{Complex{T},Vector{Complex{T}}}}, F::Factorization{T}) where {T<:BlasReal} require_one_based_indexing(B) x = rdiv!(copy(reinterpret(T, B)), F) return copy(reinterpret(Complex{T}, x)) end # don't do the reinterpretation for [Adjoint/Transpose]Factorization (/)(B::Union{Matrix{Complex{T}},AdjOrTrans{Complex{T},Vector{Complex{T}}}}, F::TransposeFactorization{T}) where {T<:BlasReal} = @invoke /(B::AbstractMatrix, F::Factorization) (/)(B::Matrix{Complex{T}}, F::AdjointFactorization{T}) where {T<:BlasReal} = @invoke /(B::AbstractMatrix, F::Factorization) (/)(B::Adjoint{Complex{T},Vector{Complex{T}}}, F::AdjointFactorization{T}) where {T<:BlasReal} = (F' \ B')' (/)(B::Transpose{Complex{T},Vector{Complex{T}}}, F::TransposeFactorization{T}) where {T<:BlasReal} = transpose(transpose(F) \ transpose(B)) rdiv!(B::AbstractMatrix, A::TransposeFactorization) = transpose(ldiv!(A.parent, transpose(B))) rdiv!(B::AbstractMatrix, A::AdjointFactorization) = adjoint(ldiv!(A.parent, adjoint(B))) v/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/eigen.jl}P# This file is a part of Julia. License is MIT: https://julialang.org/license # Eigendecomposition """ Eigen <: Factorization Matrix factorization type of the eigenvalue/spectral decomposition of a square matrix `A`. This is the return type of [`eigen`](@ref), the corresponding matrix factorization function. If `F::Eigen` is the factorization object, the eigenvalues can be obtained via `F.values` and the eigenvectors as the columns of the matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. # Examples ```jldoctest julia> F = eigen([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) Eigen{Float64, Float64, Matrix{Float64}, Vector{Float64}} values: 3-element Vector{Float64}: 1.0 3.0 18.0 vectors: 3ร—3 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 julia> F.values 3-element Vector{Float64}: 1.0 3.0 18.0 julia> F.vectors 3ร—3 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 julia> vals, vecs = F; # destructuring via iteration julia> vals == F.values && vecs == F.vectors true ``` """ struct Eigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T} values::U vectors::S Eigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} = new(values, vectors) end Eigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} = Eigen{T,V,typeof(vectors),typeof(values)}(values, vectors) # Generalized eigenvalue problem. """ GeneralizedEigen <: Factorization Matrix factorization type of the generalized eigenvalue/spectral decomposition of `A` and `B`. This is the return type of [`eigen`](@ref), the corresponding matrix factorization function, when called with two matrix arguments. If `F::GeneralizedEigen` is the factorization object, the eigenvalues can be obtained via `F.values` and the eigenvectors as the columns of the matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. # Examples ```jldoctest julia> A = [1 0; 0 -1] 2ร—2 Matrix{Int64}: 1 0 0 -1 julia> B = [0 1; 1 0] 2ร—2 Matrix{Int64}: 0 1 1 0 julia> F = eigen(A, B) GeneralizedEigen{ComplexF64, ComplexF64, Matrix{ComplexF64}, Vector{ComplexF64}} values: 2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im vectors: 2ร—2 Matrix{ComplexF64}: 0.0+1.0im 0.0-1.0im -1.0+0.0im -1.0-0.0im julia> F.values 2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im julia> F.vectors 2ร—2 Matrix{ComplexF64}: 0.0+1.0im 0.0-1.0im -1.0+0.0im -1.0-0.0im julia> vals, vecs = F; # destructuring via iteration julia> vals == F.values && vecs == F.vectors true ``` """ struct GeneralizedEigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T} values::U vectors::S GeneralizedEigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} = new(values, vectors) end GeneralizedEigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} = GeneralizedEigen{T,V,typeof(vectors),typeof(values)}(values, vectors) # iteration for destructuring into components Base.iterate(S::Union{Eigen,GeneralizedEigen}) = (S.values, Val(:vectors)) Base.iterate(S::Union{Eigen,GeneralizedEigen}, ::Val{:vectors}) = (S.vectors, Val(:done)) Base.iterate(S::Union{Eigen,GeneralizedEigen}, ::Val{:done}) = nothing isposdef(A::Union{Eigen,GeneralizedEigen}) = isreal(A.values) && all(x -> x > 0, A.values) # pick a canonical ordering to avoid returning eigenvalues in "random" order # as is the LAPACK default (for complex ฮป โ€” LAPACK sorts by ฮป for the Hermitian/Symmetric case) eigsortby(ฮป::Real) = ฮป eigsortby(ฮป::Complex) = (real(ฮป),imag(ฮป)) function sorteig!(ฮป::AbstractVector, X::AbstractMatrix, sortby::Union{Function,Nothing}=eigsortby) if sortby !== nothing && !issorted(ฮป, by=sortby) p = sortperm(ฮป; alg=QuickSort, by=sortby) permute!(ฮป, p) Base.permutecols!!(X, p) end return ฮป, X end sorteig!(ฮป::AbstractVector, sortby::Union{Function,Nothing}=eigsortby) = sortby === nothing ? ฮป : sort!(ฮป, by=sortby) """ eigen!(A; permute, scale, sortby) eigen!(A, B; sortby) Same as [`eigen`](@ref), but saves space by overwriting the input `A` (and `B`), instead of creating a copy. """ function eigen!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal n = size(A, 2) n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0)) issymmetric(A) && return eigen!(Symmetric(A), sortby=sortby) A, WR, WI, VL, VR, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A) iszero(WI) && return Eigen(sorteig!(WR, VR, sortby)...) evec = zeros(Complex{T}, n, n) j = 1 while j <= n if WI[j] == 0 evec[:,j] = view(VR, :, j) else for i = 1:n evec[i,j] = VR[i,j] + im*VR[i,j+1] evec[i,j+1] = VR[i,j] - im*VR[i,j+1] end j += 1 end j += 1 end return Eigen(sorteig!(complex.(WR, WI), evec, sortby)...) end function eigen!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex n = size(A, 2) n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0)) ishermitian(A) && return eigen!(Hermitian(A), sortby=sortby) eval, evec = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A)[[2,4]] return Eigen(sorteig!(eval, evec, sortby)...) end """ eigen(A; permute::Bool=true, scale::Bool=true, sortby) -> Eigen Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the matrix `F.vectors`. This corresponds to solving an eigenvalue problem of the form `Ax = ฮปx`, where `A` is a matrix, `x` is an eigenvector, and `ฮป` is an eigenvalue. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). For general nonsymmetric matrices it is possible to specify how the matrix is balanced before the eigenvector calculation. The option `permute=true` permutes the matrix to become closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to make rows and columns more equal in norm. The default is `true` for both options. By default, the eigenvalues and vectors are sorted lexicographically by `(real(ฮป),imag(ฮป))`. A different comparison function `by(ฮป)` can be passed to `sortby`, or you can pass `sortby=nothing` to leave the eigenvalues in an arbitrary order. Some special matrix types (e.g. [`Diagonal`](@ref) or [`SymTridiagonal`](@ref)) may implement their own sorting convention and not accept a `sortby` keyword. # Examples ```jldoctest julia> F = eigen([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) Eigen{Float64, Float64, Matrix{Float64}, Vector{Float64}} values: 3-element Vector{Float64}: 1.0 3.0 18.0 vectors: 3ร—3 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 julia> F.values 3-element Vector{Float64}: 1.0 3.0 18.0 julia> F.vectors 3ร—3 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 julia> vals, vecs = F; # destructuring via iteration julia> vals == F.values && vecs == F.vectors true ``` """ function eigen(A::AbstractMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where T isdiag(A) && return eigen(Diagonal{eigtype(T)}(diag(A)); sortby) ishermitian(A) && return eigen!(eigencopy_oftype(Hermitian(A), eigtype(T)); sortby) AA = eigencopy_oftype(A, eigtype(T)) return eigen!(AA; permute, scale, sortby) end function eigen(A::AbstractMatrix{T}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) where {T <: Union{Float16,Complex{Float16}}} isdiag(A) && return eigen(Diagonal{eigtype(T)}(diag(A)); sortby) E = if ishermitian(A) eigen!(eigencopy_oftype(Hermitian(A), eigtype(T)); sortby) else eigen!(eigencopy_oftype(A, eigtype(T)); permute, scale, sortby) end values = convert(AbstractVector{isreal(E.values) ? Float16 : Complex{Float16}}, E.values) vectors = convert(AbstractMatrix{isreal(E.vectors) ? Float16 : Complex{Float16}}, E.vectors) return Eigen(values, vectors) end eigen(x::Number) = Eigen([x], fill(one(x), 1, 1)) """ eigvecs(A; permute::Bool=true, scale::Bool=true, `sortby`) -> Matrix Return a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can be obtained from the slice `M[:, k]`.) The `permute`, `scale`, and `sortby` keywords are the same as for [`eigen`](@ref). # Examples ```jldoctest julia> eigvecs([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0]) 3ร—3 Matrix{Float64}: 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 ``` """ eigvecs(A::Union{Number, AbstractMatrix}; kws...) = eigvecs(eigen(A; kws...)) eigvecs(F::Union{Eigen, GeneralizedEigen}) = F.vectors eigvals(F::Union{Eigen, GeneralizedEigen}) = F.values """ eigvals!(A; permute::Bool=true, scale::Bool=true, sortby) -> values Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. The `permute`, `scale`, and `sortby` keywords are the same as for [`eigen`](@ref). !!! note The input matrix `A` will not contain its eigenvalues after `eigvals!` is called on it - `A` is used as a workspace. # Examples ```jldoctest julia> A = [1. 2.; 3. 4.] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> eigvals!(A) 2-element Vector{Float64}: -0.3722813232690143 5.372281323269014 julia> A 2ร—2 Matrix{Float64}: -0.372281 -1.0 0.0 5.37228 ``` """ function eigvals!(A::StridedMatrix{<:BlasReal}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) issymmetric(A) && return sorteig!(eigvals!(Symmetric(A)), sortby) _, valsre, valsim, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A) return sorteig!(iszero(valsim) ? valsre : complex.(valsre, valsim), sortby) end function eigvals!(A::StridedMatrix{<:BlasComplex}; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=eigsortby) ishermitian(A) && return sorteig!(eigvals(Hermitian(A)), sortby) return sorteig!(LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A)[2], sortby) end # promotion type to use for eigenvalues of a Matrix{T} eigtype(T) = promote_type(Float32, typeof(zero(T)/sqrt(abs2(one(T))))) """ eigvals(A; permute::Bool=true, scale::Bool=true, sortby) -> values Return the eigenvalues of `A`. For general non-symmetric matrices it is possible to specify how the matrix is balanced before the eigenvalue calculation. The `permute`, `scale`, and `sortby` keywords are the same as for [`eigen`](@ref). # Examples ```jldoctest julia> diag_matrix = [1 0; 0 4] 2ร—2 Matrix{Int64}: 1 0 0 4 julia> eigvals(diag_matrix) 2-element Vector{Float64}: 1.0 4.0 ``` """ eigvals(A::AbstractMatrix{T}; kws...) where T = eigvals!(eigencopy_oftype(A, eigtype(T)); kws...) """ For a scalar input, `eigvals` will return a scalar. # Example ```jldoctest julia> eigvals(-2) -2 ``` """ eigvals(x::Number; kwargs...) = imag(x) == 0 ? real(x) : x """ eigmax(A; permute::Bool=true, scale::Bool=true) Return the largest eigenvalue of `A`. The option `permute=true` permutes the matrix to become closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to make rows and columns more equal in norm. Note that if the eigenvalues of `A` are complex, this method will fail, since complex numbers cannot be sorted. # Examples ```jldoctest julia> A = [0 im; -im 0] 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+1im 0-1im 0+0im julia> eigmax(A) 1.0 julia> A = [0 im; -1 0] 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+1im -1+0im 0+0im julia> eigmax(A) ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: `A` cannot have complex eigenvalues. Stacktrace: [...] ``` """ function eigmax(A::Union{Number, AbstractMatrix}; permute::Bool=true, scale::Bool=true) v = eigvals(A, permute = permute, scale = scale) if eltype(v)<:Complex throw(DomainError(A, "`A` cannot have complex eigenvalues.")) end maximum(v) end """ eigmin(A; permute::Bool=true, scale::Bool=true) Return the smallest eigenvalue of `A`. The option `permute=true` permutes the matrix to become closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to make rows and columns more equal in norm. Note that if the eigenvalues of `A` are complex, this method will fail, since complex numbers cannot be sorted. # Examples ```jldoctest julia> A = [0 im; -im 0] 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+1im 0-1im 0+0im julia> eigmin(A) -1.0 julia> A = [0 im; -1 0] 2ร—2 Matrix{Complex{Int64}}: 0+0im 0+1im -1+0im 0+0im julia> eigmin(A) ERROR: DomainError with Complex{Int64}[0+0im 0+1im; -1+0im 0+0im]: `A` cannot have complex eigenvalues. Stacktrace: [...] ``` """ function eigmin(A::Union{Number, AbstractMatrix}; permute::Bool=true, scale::Bool=true) v = eigvals(A, permute = permute, scale = scale) if eltype(v)<:Complex throw(DomainError(A, "`A` cannot have complex eigenvalues.")) end minimum(v) end inv(A::Eigen) = A.vectors * inv(Diagonal(A.values)) / A.vectors det(A::Eigen) = prod(A.values) # Generalized eigenproblem function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal issymmetric(A) && isposdef(B) && return eigen!(Symmetric(A), Symmetric(B), sortby=sortby) n = size(A, 1) if LAPACK.version() < v"3.6.0" alphar, alphai, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) else alphar, alphai, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) end iszero(alphai) && return GeneralizedEigen(sorteig!(alphar ./ beta, vr, sortby)...) vecs = zeros(Complex{T}, n, n) j = 1 while j <= n if alphai[j] == 0 vecs[:,j] = view(vr, :, j) else for i = 1:n vecs[i,j ] = vr[i,j] + im*vr[i,j+1] vecs[i,j+1] = vr[i,j] - im*vr[i,j+1] end j += 1 end j += 1 end return GeneralizedEigen(sorteig!(complex.(alphar, alphai)./beta, vecs, sortby)...) end function eigen!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex ishermitian(A) && isposdef(B) && return eigen!(Hermitian(A), Hermitian(B), sortby=sortby) if LAPACK.version() < v"3.6.0" alpha, beta, _, vr = LAPACK.ggev!('N', 'V', A, B) else alpha, beta, _, vr = LAPACK.ggev3!('N', 'V', A, B) end return GeneralizedEigen(sorteig!(alpha./beta, vr, sortby)...) end """ eigen(A, B; sortby) -> GeneralizedEigen Compute the generalized eigenvalue decomposition of `A` and `B`, returning a [`GeneralizedEigen`](@ref) factorization object `F` which contains the generalized eigenvalues in `F.values` and the generalized eigenvectors in the columns of the matrix `F.vectors`. This corresponds to solving a generalized eigenvalue problem of the form `Ax = ฮปBx`, where `A, B` are matrices, `x` is an eigenvector, and `ฮป` is an eigenvalue. (The `k`th generalized eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. By default, the eigenvalues and vectors are sorted lexicographically by `(real(ฮป),imag(ฮป))`. A different comparison function `by(ฮป)` can be passed to `sortby`, or you can pass `sortby=nothing` to leave the eigenvalues in an arbitrary order. # Examples ```jldoctest julia> A = [1 0; 0 -1] 2ร—2 Matrix{Int64}: 1 0 0 -1 julia> B = [0 1; 1 0] 2ร—2 Matrix{Int64}: 0 1 1 0 julia> F = eigen(A, B); julia> F.values 2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im julia> F.vectors 2ร—2 Matrix{ComplexF64}: 0.0+1.0im 0.0-1.0im -1.0+0.0im -1.0-0.0im julia> vals, vecs = F; # destructuring via iteration julia> vals == F.values && vecs == F.vectors true ``` """ function eigen(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}; kws...) where {TA,TB} S = promote_type(eigtype(TA), TB) eigen!(copy_similar(A, S), copy_similar(B, S); kws...) end eigen(A::Number, B::Number) = eigen(fill(A,1,1), fill(B,1,1)) """ LinearAlgebra.eigencopy_oftype(A::AbstractMatrix, ::Type{S}) Creates a dense copy of `A` with eltype `S` by calling `copy_similar(A, S)`. In the case of `Hermitian` or `Symmetric` matrices additionally retains the wrapper, together with the `uplo` field. """ eigencopy_oftype(A, S) = copy_similar(A, S) """ eigvals!(A, B; sortby) -> values Same as [`eigvals`](@ref), but saves space by overwriting the input `A` (and `B`), instead of creating copies. !!! note The input matrices `A` and `B` will not contain their eigenvalues after `eigvals!` is called. They are used as workspaces. # Examples ```jldoctest julia> A = [1. 0.; 0. -1.] 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 -1.0 julia> B = [0. 1.; 1. 0.] 2ร—2 Matrix{Float64}: 0.0 1.0 1.0 0.0 julia> eigvals!(A, B) 2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im julia> A 2ร—2 Matrix{Float64}: -0.0 -1.0 1.0 -0.0 julia> B 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 1.0 ``` """ function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasReal issymmetric(A) && isposdef(B) && return sorteig!(eigvals!(Symmetric(A), Symmetric(B)), sortby) if LAPACK.version() < v"3.6.0" alphar, alphai, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) else alphar, alphai, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) end return sorteig!((iszero(alphai) ? alphar : complex.(alphar, alphai))./beta, sortby) end function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}; sortby::Union{Function,Nothing}=eigsortby) where T<:BlasComplex ishermitian(A) && isposdef(B) && return sorteig!(eigvals!(Hermitian(A), Hermitian(B)), sortby) if LAPACK.version() < v"3.6.0" alpha, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B) else alpha, beta, vl, vr = LAPACK.ggev3!('N', 'N', A, B) end return sorteig!(alpha./beta, sortby) end """ eigvals(A, B) -> values Compute the generalized eigenvalues of `A` and `B`. # Examples ```jldoctest julia> A = [1 0; 0 -1] 2ร—2 Matrix{Int64}: 1 0 0 -1 julia> B = [0 1; 1 0] 2ร—2 Matrix{Int64}: 0 1 1 0 julia> eigvals(A,B) 2-element Vector{ComplexF64}: 0.0 - 1.0im 0.0 + 1.0im ``` """ function eigvals(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}; kws...) where {TA,TB} S = promote_type(eigtype(TA), TB) return eigvals!(copy_similar(A, S), copy_similar(B, S); kws...) end """ eigvecs(A, B) -> Matrix Return a matrix `M` whose columns are the generalized eigenvectors of `A` and `B`. (The `k`th eigenvector can be obtained from the slice `M[:, k]`.) # Examples ```jldoctest julia> A = [1 0; 0 -1] 2ร—2 Matrix{Int64}: 1 0 0 -1 julia> B = [0 1; 1 0] 2ร—2 Matrix{Int64}: 0 1 1 0 julia> eigvecs(A, B) 2ร—2 Matrix{ComplexF64}: 0.0+1.0im 0.0-1.0im -1.0+0.0im -1.0-0.0im ``` """ eigvecs(A::AbstractMatrix, B::AbstractMatrix; kws...) = eigvecs(eigen(A, B; kws...)) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Union{Eigen,GeneralizedEigen}) summary(io, F); println(io) println(io, "values:") show(io, mime, F.values) println(io, "\nvectors:") show(io, mime, F.vectors) end function Base.hash(F::Eigen, h::UInt) return hash(F.values, hash(F.vectors, hash(Eigen, h))) end function Base.:(==)(A::Eigen, B::Eigen) return A.values == B.values && A.vectors == B.vectors end function Base.isequal(A::Eigen, B::Eigen) return isequal(A.values, B.values) && isequal(A.vectors, B.vectors) end # Conversion methods ## Can we determine the source/result is Real? This is not stored in the type Eigen AbstractMatrix(F::Eigen) = F.vectors * Diagonal(F.values) / F.vectors AbstractArray(F::Eigen) = AbstractMatrix(F) Matrix(F::Eigen) = Array(AbstractArray(F)) Array(F::Eigen) = Matrix(F) t/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/svd.jlืG# This file is a part of Julia. License is MIT: https://julialang.org/license # Singular Value Decomposition """ SVD <: Factorization Matrix factorization type of the singular value decomposition (SVD) of a matrix `A`. This is the return type of [`svd(_)`](@ref), the corresponding matrix factorization function. If `F::SVD` is the factorization object, `U`, `S`, `V` and `Vt` can be obtained via `F.U`, `F.S`, `F.V` and `F.Vt`, such that `A = U * Diagonal(S) * Vt`. The singular values in `S` are sorted in descending order. Iterating the decomposition produces the components `U`, `S`, and `V`. # Examples ```jldoctest julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] 4ร—5 Matrix{Float64}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> F = svd(A) SVD{Float64, Float64, Matrix{Float64}, Vector{Float64}} U factor: 4ร—4 Matrix{Float64}: 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 -1.0 0.0 singular values: 4-element Vector{Float64}: 3.0 2.23606797749979 2.0 0.0 Vt factor: 4ร—5 Matrix{Float64}: -0.0 0.0 1.0 -0.0 0.0 0.447214 0.0 0.0 0.0 0.894427 0.0 -1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 julia> F.U * Diagonal(F.S) * F.Vt 4ร—5 Matrix{Float64}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> u, s, v = F; # destructuring via iteration julia> u == F.U && s == F.S && v == F.V true ``` """ struct SVD{T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr}} <: Factorization{T} U::M S::C Vt::M function SVD{T,Tr,M,C}(U, S, Vt) where {T,Tr,M<:AbstractArray{T},C<:AbstractVector{Tr}} require_one_based_indexing(U, S, Vt) new{T,Tr,M,C}(U, S, Vt) end end SVD(U::AbstractArray{T}, S::AbstractVector{Tr}, Vt::AbstractArray{T}) where {T,Tr} = SVD{T,Tr,typeof(U),typeof(S)}(U, S, Vt) SVD{T}(U::AbstractArray, S::AbstractVector{Tr}, Vt::AbstractArray) where {T,Tr} = SVD(convert(AbstractArray{T}, U), convert(AbstractVector{Tr}, S), convert(AbstractArray{T}, Vt)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(SVD{T,Tr,M}(U::AbstractArray{T}, S::AbstractVector{Tr}, Vt::AbstractArray{T}) where {T,Tr,M}, SVD{T,Tr,M,typeof(S)}(U, S, Vt)) SVD{T}(F::SVD) where {T} = SVD( convert(AbstractMatrix{T}, F.U), convert(AbstractVector{real(T)}, F.S), convert(AbstractMatrix{T}, F.Vt)) Factorization{T}(F::SVD) where {T} = SVD{T}(F) # iteration for destructuring into components Base.iterate(S::SVD) = (S.U, Val(:S)) Base.iterate(S::SVD, ::Val{:S}) = (S.S, Val(:V)) Base.iterate(S::SVD, ::Val{:V}) = (S.V, Val(:done)) Base.iterate(S::SVD, ::Val{:done}) = nothing default_svd_alg(A) = DivideAndConquer() """ svd!(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> SVD `svd!` is the same as [`svd`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. See documentation of [`svd`](@ref) for details. """ function svd!(A::StridedMatrix{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T<:BlasFloat} m, n = size(A) if m == 0 || n == 0 u, s, vt = (Matrix{T}(I, m, full ? m : n), real(zeros(T,0)), Matrix{T}(I, n, n)) else u, s, vt = _svd!(A, full, alg) end SVD(u, s, vt) end function svd!(A::StridedVector{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T<:BlasFloat} m = length(A) normA = norm(A) if iszero(normA) return SVD(Matrix{T}(I, m, full ? m : 1), [normA], ones(T, 1, 1)) elseif !full normalize!(A) return SVD(reshape(A, (m, 1)), [normA], ones(T, 1, 1)) else u, s, vt = _svd!(reshape(A, (m, 1)), full, alg) return SVD(u, s, vt) end end _svd!(A::StridedMatrix{T}, full::Bool, alg::Algorithm) where {T<:BlasFloat} = throw(ArgumentError("Unsupported value for `alg` keyword.")) _svd!(A::StridedMatrix{T}, full::Bool, alg::DivideAndConquer) where {T<:BlasFloat} = LAPACK.gesdd!(full ? 'A' : 'S', A) function _svd!(A::StridedMatrix{T}, full::Bool, alg::QRIteration) where {T<:BlasFloat} c = full ? 'A' : 'S' u, s, vt = LAPACK.gesvd!(c, c, A) end """ svd(A; full::Bool = false, alg::Algorithm = default_svd_alg(A)) -> SVD Compute the singular value decomposition (SVD) of `A` and return an `SVD` object. `U`, `S`, `V` and `Vt` can be obtained from the factorization `F` with `F.U`, `F.S`, `F.V` and `F.Vt`, such that `A = U * Diagonal(S) * Vt`. The algorithm produces `Vt` and hence `Vt` is more efficient to extract than `V`. The singular values in `S` are sorted in descending order. Iterating the decomposition produces the components `U`, `S`, and `V`. If `full = false` (default), a "thin" SVD is returned. For an ``M \\times N`` matrix `A`, in the full factorization `U` is ``M \\times M`` and `V` is ``N \\times N``, while in the thin factorization `U` is ``M \\times K`` and `V` is ``N \\times K``, where ``K = \\min(M,N)`` is the number of singular values. If `alg = DivideAndConquer()` a divide-and-conquer algorithm is used to calculate the SVD. Another (typically slower but more accurate) option is `alg = QRIteration()`. !!! compat "Julia 1.3" The `alg` keyword argument requires Julia 1.3 or later. # Examples ```jldoctest julia> A = rand(4,3); julia> F = svd(A); # Store the Factorization Object julia> A โ‰ˆ F.U * Diagonal(F.S) * F.Vt true julia> U, S, V = F; # destructuring via iteration julia> A โ‰ˆ U * Diagonal(S) * V' true julia> Uonly, = svd(A); # Store U only julia> Uonly == U true ``` """ function svd(A::AbstractVecOrMat{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T} svd!(eigencopy_oftype(A, eigtype(T)), full = full, alg = alg) end function svd(A::AbstractVecOrMat{T}; full::Bool = false, alg::Algorithm = default_svd_alg(A)) where {T <: Union{Float16,Complex{Float16}}} A = svd!(eigencopy_oftype(A, eigtype(T)), full = full, alg = alg) return SVD{T}(A) end function svd(x::Number; full::Bool = false, alg::Algorithm = default_svd_alg(x)) SVD(x == 0 ? fill(one(x), 1, 1) : fill(x/abs(x), 1, 1), [abs(x)], fill(one(x), 1, 1)) end function svd(x::Integer; full::Bool = false, alg::Algorithm = default_svd_alg(x)) svd(float(x), full = full, alg = alg) end function svd(A::Adjoint; full::Bool = false, alg::Algorithm = default_svd_alg(A)) s = svd(A.parent, full = full, alg = alg) return SVD(s.Vt', s.S, s.U') end function svd(A::Transpose; full::Bool = false, alg::Algorithm = default_svd_alg(A)) s = svd(A.parent, full = full, alg = alg) return SVD(transpose(s.Vt), s.S, transpose(s.U)) end function getproperty(F::SVD, d::Symbol) if d === :V return getfield(F, :Vt)' else return getfield(F, d) end end Base.propertynames(F::SVD, private::Bool=false) = private ? (:V, fieldnames(typeof(F))...) : (:U, :S, :V, :Vt) """ svdvals!(A) Return the singular values of `A`, saving space by overwriting the input. See also [`svdvals`](@ref) and [`svd`](@ref). """ svdvals!(A::StridedMatrix{T}) where {T<:BlasFloat} = isempty(A) ? zeros(real(T), 0) : LAPACK.gesdd!('N', A)[2] svdvals!(A::StridedVector{T}) where {T<:BlasFloat} = svdvals!(reshape(A, (length(A), 1))) """ svdvals(A) Return the singular values of `A` in descending order. # Examples ```jldoctest julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.] 4ร—5 Matrix{Float64}: 1.0 0.0 0.0 0.0 2.0 0.0 0.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 julia> svdvals(A) 4-element Vector{Float64}: 3.0 2.23606797749979 2.0 0.0 ``` """ svdvals(A::AbstractMatrix{T}) where {T} = svdvals!(eigencopy_oftype(A, eigtype(T))) svdvals(A::AbstractVector{T}) where {T} = [convert(eigtype(T), norm(A))] svdvals(x::Number) = abs(x) svdvals(S::SVD{<:Any,T}) where {T} = (S.S)::Vector{T} ### SVD least squares ### function ldiv!(A::SVD{T}, B::AbstractVecOrMat) where T m, n = size(A) k = searchsortedlast(A.S, eps(real(T))*A.S[1], rev=true) mul!(view(B, 1:n, :), view(A.Vt, 1:k, :)', view(A.S, 1:k) .\ (view(A.U, :, 1:k)' * _cut_B(B, 1:m))) return B end function inv(F::SVD{T}) where T @inbounds for i in eachindex(F.S) iszero(F.S[i]) && throw(SingularException(i)) end k = searchsortedlast(F.S, eps(real(T))*F.S[1], rev=true) @views (F.S[1:k] .\ F.Vt[1:k, :])' * F.U[:,1:k]' end size(A::SVD, dim::Integer) = dim == 1 ? size(A.U, dim) : size(A.Vt, dim) size(A::SVD) = (size(A, 1), size(A, 2)) function adjoint(F::SVD) return SVD(F.Vt', F.S, F.U') end function show(io::IO, mime::MIME{Symbol("text/plain")}, F::SVD{<:Any,<:Any,<:AbstractArray,<:AbstractVector}) summary(io, F); println(io) println(io, "U factor:") show(io, mime, F.U) println(io, "\nsingular values:") show(io, mime, F.S) println(io, "\nVt factor:") show(io, mime, F.Vt) end # Generalized svd """ GeneralizedSVD <: Factorization Matrix factorization type of the generalized singular value decomposition (SVD) of two matrices `A` and `B`, such that `A = F.U*F.D1*F.R0*F.Q'` and `B = F.V*F.D2*F.R0*F.Q'`. This is the return type of [`svd(_, _)`](@ref), the corresponding matrix factorization function. For an M-by-N matrix `A` and P-by-N matrix `B`, - `U` is a M-by-M orthogonal matrix, - `V` is a P-by-P orthogonal matrix, - `Q` is a N-by-N orthogonal matrix, - `D1` is a M-by-(K+L) diagonal matrix with 1s in the first K entries, - `D2` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal, - `R0` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is nonsingular upper block triangular, `K+L` is the effective numerical rank of the matrix `[A; B]`. Iterating the decomposition produces the components `U`, `V`, `Q`, `D1`, `D2`, and `R0`. The entries of `F.D1` and `F.D2` are related, as explained in the LAPACK documentation for the [generalized SVD](http://www.netlib.org/lapack/lug/node36.html) and the [xGGSVD3](http://www.netlib.org/lapack/explore-html/d6/db3/dggsvd3_8f.html) routine which is called underneath (in LAPACK 3.6.0 and newer). # Examples ```jldoctest julia> A = [1. 0.; 0. -1.] 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 -1.0 julia> B = [0. 1.; 1. 0.] 2ร—2 Matrix{Float64}: 0.0 1.0 1.0 0.0 julia> F = svd(A, B) GeneralizedSVD{Float64, Matrix{Float64}, Float64, Vector{Float64}} U factor: 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 1.0 V factor: 2ร—2 Matrix{Float64}: -0.0 -1.0 1.0 0.0 Q factor: 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 1.0 D1 factor: 2ร—2 Matrix{Float64}: 0.707107 0.0 0.0 0.707107 D2 factor: 2ร—2 Matrix{Float64}: 0.707107 0.0 0.0 0.707107 R0 factor: 2ร—2 Matrix{Float64}: 1.41421 0.0 0.0 -1.41421 julia> F.U*F.D1*F.R0*F.Q' 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 -1.0 julia> F.V*F.D2*F.R0*F.Q' 2ร—2 Matrix{Float64}: -0.0 1.0 1.0 0.0 ``` """ struct GeneralizedSVD{T,S<:AbstractMatrix,Tr,C<:AbstractVector{Tr}} <: Factorization{T} U::S V::S Q::S a::C b::C k::Int l::Int R::S function GeneralizedSVD{T,S,Tr,C}(U, V, Q, a, b, k, l, R) where {T,S<:AbstractMatrix{T},Tr,C<:AbstractVector{Tr}} new{T,S,Tr,C}(U, V, Q, a, b, k, l, R) end end GeneralizedSVD(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T}, a::AbstractVector{Tr}, b::AbstractVector{Tr}, k::Int, l::Int, R::AbstractMatrix{T}) where {T, Tr} = GeneralizedSVD{T,typeof(U),Tr,typeof(a)}(U, V, Q, a, b, k, l, R) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(GeneralizedSVD{T,S}(U, V, Q, a, b, k, l, R) where {T, S}, GeneralizedSVD{T,S,real(T),typeof(a)}(U, V, Q, a, b, k, l, R)) # iteration for destructuring into components Base.iterate(S::GeneralizedSVD) = (S.U, Val(:V)) Base.iterate(S::GeneralizedSVD, ::Val{:V}) = (S.V, Val(:Q)) Base.iterate(S::GeneralizedSVD, ::Val{:Q}) = (S.Q, Val(:D1)) Base.iterate(S::GeneralizedSVD, ::Val{:D1}) = (S.D1, Val(:D2)) Base.iterate(S::GeneralizedSVD, ::Val{:D2}) = (S.D2, Val(:R0)) Base.iterate(S::GeneralizedSVD, ::Val{:R0}) = (S.R0, Val(:done)) Base.iterate(S::GeneralizedSVD, ::Val{:done}) = nothing """ svd!(A, B) -> GeneralizedSVD `svd!` is the same as [`svd`](@ref), but modifies the arguments `A` and `B` in-place, instead of making copies. See documentation of [`svd`](@ref) for details. """ function svd!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 if LAPACK.version() < v"3.6.0" U, V, Q, a, b, k, l, R = LAPACK.ggsvd!('U', 'V', 'Q', A, B) else U, V, Q, a, b, k, l, R = LAPACK.ggsvd3!('U', 'V', 'Q', A, B) end GeneralizedSVD(U, V, Q, a, b, Int(k), Int(l), R) end svd(A::AbstractMatrix{T}, B::AbstractMatrix{T}) where {T<:BlasFloat} = svd!(copy_similar(A, T), copy_similar(B, T)) """ svd(A, B) -> GeneralizedSVD Compute the generalized SVD of `A` and `B`, returning a `GeneralizedSVD` factorization object `F` such that `[A;B] = [F.U * F.D1; F.V * F.D2] * F.R0 * F.Q'` - `U` is a M-by-M orthogonal matrix, - `V` is a P-by-P orthogonal matrix, - `Q` is a N-by-N orthogonal matrix, - `D1` is a M-by-(K+L) diagonal matrix with 1s in the first K entries, - `D2` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal, - `R0` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is nonsingular upper block triangular, `K+L` is the effective numerical rank of the matrix `[A; B]`. Iterating the decomposition produces the components `U`, `V`, `Q`, `D1`, `D2`, and `R0`. The generalized SVD is used in applications such as when one wants to compare how much belongs to `A` vs. how much belongs to `B`, as in human vs yeast genome, or signal vs noise, or between clusters vs within clusters. (See Edelman and Wang for discussion: https://arxiv.org/abs/1901.00485) It decomposes `[A; B]` into `[UC; VS]H`, where `[UC; VS]` is a natural orthogonal basis for the column space of `[A; B]`, and `H = RQ'` is a natural non-orthogonal basis for the rowspace of `[A;B]`, where the top rows are most closely attributed to the `A` matrix, and the bottom to the `B` matrix. The multi-cosine/sine matrices `C` and `S` provide a multi-measure of how much `A` vs how much `B`, and `U` and `V` provide directions in which these are measured. # Examples ```jldoctest julia> A = randn(3,2); B=randn(4,2); julia> F = svd(A, B); julia> U,V,Q,C,S,R = F; julia> H = R*Q'; julia> [A; B] โ‰ˆ [U*C; V*S]*H true julia> [A; B] โ‰ˆ [F.U*F.D1; F.V*F.D2]*F.R0*F.Q' true julia> Uonly, = svd(A,B); julia> U == Uonly true ``` """ function svd(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} S = promote_type(eigtype(TA),TB) return svd!(copy_similar(A, S), copy_similar(B, S)) end # This method can be heavily optimized but it is probably not critical # and might introduce bugs or inconsistencies relative to the 1x1 matrix # version svd(x::Number, y::Number) = svd(fill(x, 1, 1), fill(y, 1, 1)) @inline function getproperty(F::GeneralizedSVD{T}, d::Symbol) where T Fa = getfield(F, :a) Fb = getfield(F, :b) Fk = getfield(F, :k) Fl = getfield(F, :l) FU = getfield(F, :U) FV = getfield(F, :V) FQ = getfield(F, :Q) FR = getfield(F, :R) if d === :alpha return Fa elseif d === :beta return Fb elseif d === :vals || d === :S return Fa[1:Fk + Fl] ./ Fb[1:Fk + Fl] elseif d === :D1 m = size(FU, 1) if m - Fk - Fl >= 0 return [Matrix{T}(I, Fk, Fk) zeros(T, Fk, Fl) ; zeros(T, Fl, Fk) Diagonal(Fa[Fk + 1:Fk + Fl]); zeros(T, m - Fk - Fl, Fk + Fl) ] else return [Matrix{T}(I, m, Fk) [zeros(T, Fk, m - Fk); Diagonal(Fa[Fk + 1:m])] zeros(T, m, Fk + Fl - m)] end elseif d === :D2 m = size(FU, 1) p = size(FV, 1) if m - Fk - Fl >= 0 return [zeros(T, Fl, Fk) Diagonal(Fb[Fk + 1:Fk + Fl]); zeros(T, p - Fl, Fk + Fl)] else return [zeros(T, p, Fk) [Diagonal(Fb[Fk + 1:m]); zeros(T, Fk + p - m, m - Fk)] [zeros(T, m - Fk, Fk + Fl - m); Matrix{T}(I, Fk + p - m, Fk + Fl - m)]] end elseif d === :R0 n = size(FQ, 1) return [zeros(T, Fk + Fl, n - Fk - Fl) FR] else getfield(F, d) end end Base.propertynames(F::GeneralizedSVD) = (:alpha, :beta, :vals, :S, :D1, :D2, :R0, fieldnames(typeof(F))...) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::GeneralizedSVD{<:Any,<:AbstractArray}) summary(io, F); println(io) println(io, "U factor:") show(io, mime, F.U) println(io, "\nV factor:") show(io, mime, F.V) println(io, "\nQ factor:") show(io, mime, F.Q) println(io, "\nD1 factor:") show(io, mime, F.D1) println(io, "\nD2 factor:") show(io, mime, F.D2) println(io, "\nR0 factor:") show(io, mime, F.R0) end """ svdvals!(A, B) Return the generalized singular values from the generalized singular value decomposition of `A` and `B`, saving space by overwriting `A` and `B`. See also [`svd`](@ref) and [`svdvals`](@ref). """ function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 if LAPACK.version() < v"3.6.0" _, _, _, a, b, k, l, _ = LAPACK.ggsvd!('N', 'N', 'N', A, B) else _, _, _, a, b, k, l, _ = LAPACK.ggsvd3!('N', 'N', 'N', A, B) end a[1:k + l] ./ b[1:k + l] end """ svdvals(A, B) Return the generalized singular values from the generalized singular value decomposition of `A` and `B`. See also [`svd`](@ref). # Examples ```jldoctest julia> A = [1. 0.; 0. -1.] 2ร—2 Matrix{Float64}: 1.0 0.0 0.0 -1.0 julia> B = [0. 1.; 1. 0.] 2ร—2 Matrix{Float64}: 0.0 1.0 1.0 0.0 julia> svdvals(A, B) 2-element Vector{Float64}: 1.0 1.0 ``` """ function svdvals(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} S = promote_type(eigtype(TA), TB) return svdvals!(copy_similar(A, S), copy_similar(B, S)) end svdvals(x::Number, y::Number) = abs(x/y) # Conversion AbstractMatrix(F::SVD) = (F.U * Diagonal(F.S)) * F.Vt AbstractArray(F::SVD) = AbstractMatrix(F) Matrix(F::SVD) = Array(AbstractArray(F)) Array(F::SVD) = Matrix(F) z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/symmetric.jlึv# This file is a part of Julia. License is MIT: https://julialang.org/license # Symmetric and Hermitian matrices struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} data::S uplo::Char function Symmetric{T,S}(data, uplo::Char) where {T,S<:AbstractMatrix{<:T}} require_one_based_indexing(data) (uplo != 'U' && uplo != 'L') && throw_uplo() new{T,S}(data, uplo) end end """ Symmetric(A, uplo=:U) Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. `Symmetric` views are mainly useful for real-symmetric matrices, for which specialized algorithms (e.g. for eigenproblems) are enabled for `Symmetric` types. More generally, see also [`Hermitian(A)`](@ref) for Hermitian matrices `A == A'`, which is effectively equivalent to `Symmetric` for real matrices but is also useful for complex matrices. (Whereas complex `Symmetric` matrices are supported but have few if any specialized algorithms.) To compute the symmetric part of a real matrix, or more generally the Hermitian part `(A + A') / 2` of a real or complex matrix `A`, use [`hermitianpart`](@ref). # Examples ```jldoctest julia> A = [1 2 3; 4 5 6; 7 8 9] 3ร—3 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 julia> Supper = Symmetric(A) 3ร—3 Symmetric{Int64, Matrix{Int64}}: 1 2 3 2 5 6 3 6 9 julia> Slower = Symmetric(A, :L) 3ร—3 Symmetric{Int64, Matrix{Int64}}: 1 4 7 4 5 8 7 8 9 julia> hermitianpart(A) 3ร—3 Hermitian{Float64, Matrix{Float64}}: 1.0 3.0 5.0 3.0 5.0 7.0 5.0 7.0 9.0 ``` Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric (e.g. if `A == transpose(A)`). """ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) checksquare(A) return symmetric_type(typeof(A))(A, char_uplo(uplo)) end """ symmetric(A, uplo=:U) Construct a symmetric view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the other one. If `A` is a `Number`, it is returned as is. If a symmetric view of a matrix is to be constructed of which the elements are neither matrices nor numbers, an appropriate method of `symmetric` has to be implemented. In that case, `symmetric_type` has to be implemented, too. """ symmetric(A::AbstractMatrix, uplo::Symbol=:U) = Symmetric(A, uplo) symmetric(A::Number, ::Symbol=:U) = A """ symmetric_type(T::Type) The type of the object returned by `symmetric(::T, ::Symbol)`. For matrices, this is an appropriately typed `Symmetric`, for `Number`s, it is the original type. If `symmetric` is implemented for a custom type, so should be `symmetric_type`, and vice versa. """ function symmetric_type(::Type{T}) where {S, T<:AbstractMatrix{S}} return Symmetric{Union{S, promote_op(transpose, S), symmetric_type(S)}, T} end function symmetric_type(::Type{T}) where {S<:Number, T<:AbstractMatrix{S}} return Symmetric{S, T} end function symmetric_type(::Type{T}) where {S<:AbstractMatrix, T<:AbstractMatrix{S}} return Symmetric{AbstractMatrix, T} end symmetric_type(::Type{T}) where {T<:Number} = T struct Hermitian{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T} data::S uplo::Char function Hermitian{T,S}(data, uplo::Char) where {T,S<:AbstractMatrix{<:T}} require_one_based_indexing(data) (uplo != 'U' && uplo != 'L') && throw_uplo() new{T,S}(data, uplo) end end """ Hermitian(A, uplo=:U) Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of the matrix `A`. To compute the Hermitian part of `A`, use [`hermitianpart`](@ref). # Examples ```jldoctest julia> A = [1 2+2im 3-3im; 4 5 6-6im; 7 8+8im 9] 3ร—3 Matrix{Complex{Int64}}: 1+0im 2+2im 3-3im 4+0im 5+0im 6-6im 7+0im 8+8im 9+0im julia> Hupper = Hermitian(A) 3ร—3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: 1+0im 2+2im 3-3im 2-2im 5+0im 6-6im 3+3im 6+6im 9+0im julia> Hlower = Hermitian(A, :L) 3ร—3 Hermitian{Complex{Int64}, Matrix{Complex{Int64}}}: 1+0im 4+0im 7+0im 4+0im 5+0im 8-8im 7+0im 8+8im 9+0im julia> hermitianpart(A) 3ร—3 Hermitian{ComplexF64, Matrix{ComplexF64}}: 1.0+0.0im 3.0+1.0im 5.0-1.5im 3.0-1.0im 5.0+0.0im 7.0-7.0im 5.0+1.5im 7.0+7.0im 9.0+0.0im ``` Note that `Hupper` will not be equal to `Hlower` unless `A` is itself Hermitian (e.g. if `A == adjoint(A)`). All non-real parts of the diagonal will be ignored. ```julia Hermitian(fill(complex(1,1), 1, 1)) == fill(1, 1, 1) ``` """ function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) n = checksquare(A) return hermitian_type(typeof(A))(A, char_uplo(uplo)) end """ hermitian(A, uplo=:U) Construct a hermitian view of `A`. If `A` is a matrix, `uplo` controls whether the upper (if `uplo = :U`) or lower (if `uplo = :L`) triangle of `A` is used to implicitly fill the other one. If `A` is a `Number`, its real part is returned converted back to the input type. If a hermitian view of a matrix is to be constructed of which the elements are neither matrices nor numbers, an appropriate method of `hermitian` has to be implemented. In that case, `hermitian_type` has to be implemented, too. """ hermitian(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(A, uplo) hermitian(A::Number, ::Symbol=:U) = convert(typeof(A), real(A)) """ hermitian_type(T::Type) The type of the object returned by `hermitian(::T, ::Symbol)`. For matrices, this is an appropriately typed `Hermitian`, for `Number`s, it is the original type. If `hermitian` is implemented for a custom type, so should be `hermitian_type`, and vice versa. """ function hermitian_type(::Type{T}) where {S, T<:AbstractMatrix{S}} return Hermitian{Union{S, promote_op(adjoint, S), hermitian_type(S)}, T} end function hermitian_type(::Type{T}) where {S<:Number, T<:AbstractMatrix{S}} return Hermitian{S, T} end function hermitian_type(::Type{T}) where {S<:AbstractMatrix, T<:AbstractMatrix{S}} return Hermitian{AbstractMatrix, T} end hermitian_type(::Type{T}) where {T<:Number} = T _unwrap(A::Hermitian) = parent(A) _unwrap(A::Symmetric) = parent(A) for (S, H) in ((:Symmetric, :Hermitian), (:Hermitian, :Symmetric)) @eval begin $S(A::$S) = A function $S(A::$S, uplo::Symbol) if A.uplo == char_uplo(uplo) return A else throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end end $S(A::$H) = $S(A, sym_uplo(A.uplo)) function $S(A::$H, uplo::Symbol) if A.uplo == char_uplo(uplo) if $H === Hermitian && !(eltype(A) <: Real) && any(!isreal, A.data[i] for i in diagind(A.data)) throw(ArgumentError("Cannot construct $($S)($($H))); diagonal contains complex values")) end return $S(A.data, sym_uplo(A.uplo)) else throw(ArgumentError("Cannot construct $($S); uplo doesn't match")) end end end end convert(::Type{T}, m::Union{Symmetric,Hermitian}) where {T<:Symmetric} = m isa T ? m : T(m)::T convert(::Type{T}, m::Union{Symmetric,Hermitian}) where {T<:Hermitian} = m isa T ? m : T(m)::T const HermOrSym{T, S} = Union{Hermitian{T,S}, Symmetric{T,S}} const RealHermSym{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}} const RealHermSymComplexHerm{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Hermitian{Complex{T},S}} const RealHermSymComplexSym{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Symmetric{Complex{T},S}} size(A::HermOrSym, d) = size(A.data, d) size(A::HermOrSym) = size(A.data) @inline function Base.isassigned(A::HermOrSym, i::Int, j::Int) @boundscheck checkbounds(Bool, A, i, j) || return false @inbounds if i == j || ((A.uplo == 'U') == (i < j)) return isassigned(A.data, i, j) else return isassigned(A.data, j, i) end end @inline function getindex(A::Symmetric, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) @inbounds if i == j return symmetric(A.data[i, j], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else return transpose(A.data[j, i]) end end @inline function getindex(A::Hermitian, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) @inbounds if i == j return hermitian(A.data[i, j], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) elseif (A.uplo == 'U') == (i < j) return A.data[i, j] else return adjoint(A.data[j, i]) end end function setindex!(A::Symmetric, v, i::Integer, j::Integer) i == j || throw(ArgumentError("Cannot set a non-diagonal index in a symmetric matrix")) setindex!(A.data, v, i, j) end function setindex!(A::Hermitian, v, i::Integer, j::Integer) if i != j throw(ArgumentError("Cannot set a non-diagonal index in a Hermitian matrix")) elseif !isreal(v) throw(ArgumentError("Cannot set a diagonal entry in a Hermitian matrix to a nonreal value")) else setindex!(A.data, v, i, j) end end diag(A::Symmetric) = symmetric.(diag(parent(A)), sym_uplo(A.uplo)) diag(A::Hermitian) = hermitian.(diag(parent(A)), sym_uplo(A.uplo)) isdiag(A::HermOrSym) = isdiag(A.uplo == 'U' ? UpperTriangular(A.data) : LowerTriangular(A.data)) # For A<:Union{Symmetric,Hermitian}, similar(A[, neweltype]) should yield a matrix with the same # symmetry type, uplo flag, and underlying storage type as A. The following methods cover these cases. similar(A::Symmetric, ::Type{T}) where {T} = Symmetric(similar(parent(A), T), ifelse(A.uplo == 'U', :U, :L)) # If the Hermitian constructor's check ascertaining that the wrapped matrix's # diagonal is strictly real is removed, the following method can be simplified. function similar(A::Hermitian, ::Type{T}) where T B = similar(parent(A), T) for i in 1:size(B, 1) B[i, i] = 0 end return Hermitian(B, ifelse(A.uplo == 'U', :U, :L)) end # On the other hand, similar(A, [neweltype,] shape...) should yield a matrix of the underlying # storage type of A (not wrapped in a symmetry type). The following method covers these cases. similar(A::Union{Symmetric,Hermitian}, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(A), T, dims) # Conversion function Matrix(A::Symmetric) B = copytri!(convert(Matrix, copy(A.data)), A.uplo) for i = 1:size(A, 1) B[i,i] = symmetric(A[i,i], sym_uplo(A.uplo))::symmetric_type(eltype(A.data)) end return B end function Matrix(A::Hermitian) B = copytri!(convert(Matrix, copy(A.data)), A.uplo, true) for i = 1:size(A, 1) B[i,i] = hermitian(A[i,i], sym_uplo(A.uplo))::hermitian_type(eltype(A.data)) end return B end Array(A::Union{Symmetric,Hermitian}) = convert(Matrix, A) parent(A::HermOrSym) = A.data Symmetric{T,S}(A::Symmetric{T,S}) where {T,S<:AbstractMatrix{T}} = A Symmetric{T,S}(A::Symmetric) where {T,S<:AbstractMatrix{T}} = Symmetric{T,S}(convert(S,A.data),A.uplo) AbstractMatrix{T}(A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) Hermitian{T,S}(A::Hermitian{T,S}) where {T,S<:AbstractMatrix{T}} = A Hermitian{T,S}(A::Hermitian) where {T,S<:AbstractMatrix{T}} = Hermitian{T,S}(convert(S,A.data),A.uplo) AbstractMatrix{T}(A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), sym_uplo(A.uplo)) copy(A::Symmetric{T,S}) where {T,S} = (B = copy(A.data); Symmetric{T,typeof(B)}(B,A.uplo)) copy(A::Hermitian{T,S}) where {T,S} = (B = copy(A.data); Hermitian{T,typeof(B)}(B,A.uplo)) function copyto!(dest::Symmetric, src::Symmetric) if src.uplo == dest.uplo copyto!(dest.data, src.data) else transpose!(dest.data, src.data) end return dest end function copyto!(dest::Hermitian, src::Hermitian) if src.uplo == dest.uplo copyto!(dest.data, src.data) else adjoint!(dest.data, src.data) end return dest end # fill[stored]! fill!(A::HermOrSym, x) = fillstored!(A, x) function fillstored!(A::HermOrSym{T}, x) where T xT = convert(T, x) if isa(A, Hermitian) isreal(xT) || throw(ArgumentError("cannot fill Hermitian matrix with a nonreal value")) end if A.uplo == 'U' fillband!(A.data, xT, 0, size(A,2)-1) else # A.uplo == 'L' fillband!(A.data, xT, 1-size(A,1), 0) end return A end Base.isreal(A::HermOrSym{<:Real}) = true function Base.isreal(A::HermOrSym) n = size(A, 1) @inbounds if A.uplo == 'U' for j in 1:n for i in 1:(j - (A isa Hermitian)) if !isreal(A.data[i,j]) return false end end end else for j in 1:n for i in (j + (A isa Hermitian)):n if !isreal(A.data[i,j]) return false end end end end return true end ishermitian(A::Hermitian) = true ishermitian(A::Symmetric{<:Real}) = true ishermitian(A::Symmetric{<:Complex}) = isreal(A) issymmetric(A::Hermitian{<:Real}) = true issymmetric(A::Hermitian{<:Complex}) = isreal(A) issymmetric(A::Symmetric) = true adjoint(A::Hermitian) = A transpose(A::Symmetric) = A adjoint(A::Symmetric{<:Real}) = A transpose(A::Hermitian{<:Real}) = A adjoint(A::Symmetric) = Adjoint(A) transpose(A::Hermitian) = Transpose(A) real(A::Symmetric{<:Real}) = A real(A::Hermitian{<:Real}) = A real(A::Symmetric) = Symmetric(real(A.data), sym_uplo(A.uplo)) real(A::Hermitian) = Hermitian(real(A.data), sym_uplo(A.uplo)) imag(A::Symmetric) = Symmetric(imag(A.data), sym_uplo(A.uplo)) Base.copy(A::Adjoint{<:Any,<:Symmetric}) = Symmetric(copy(adjoint(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) Base.copy(A::Transpose{<:Any,<:Hermitian}) = Hermitian(copy(transpose(A.parent.data)), ifelse(A.parent.uplo == 'U', :L, :U)) tr(A::Symmetric) = tr(A.data) # to avoid AbstractMatrix fallback (incl. allocations) tr(A::Hermitian) = real(tr(A.data)) Base.conj(A::HermOrSym) = typeof(A)(conj(A.data), A.uplo) Base.conj!(A::HermOrSym) = typeof(A)(conj!(A.data), A.uplo) # tril/triu function tril(A::Hermitian, k::Integer=0) if A.uplo == 'U' && k <= 0 return tril!(copy(A.data'),k) elseif A.uplo == 'U' && k > 0 return tril!(copy(A.data'),-1) + tril!(triu(A.data),k) elseif A.uplo == 'L' && k <= 0 return tril(A.data,k) else return tril(A.data,-1) + tril!(triu!(copy(A.data')),k) end end function tril(A::Symmetric, k::Integer=0) if A.uplo == 'U' && k <= 0 return tril!(copy(transpose(A.data)),k) elseif A.uplo == 'U' && k > 0 return tril!(copy(transpose(A.data)),-1) + tril!(triu(A.data),k) elseif A.uplo == 'L' && k <= 0 return tril(A.data,k) else return tril(A.data,-1) + tril!(triu!(copy(transpose(A.data))),k) end end function triu(A::Hermitian, k::Integer=0) if A.uplo == 'U' && k >= 0 return triu(A.data,k) elseif A.uplo == 'U' && k < 0 return triu(A.data,1) + triu!(tril!(copy(A.data')),k) elseif A.uplo == 'L' && k >= 0 return triu!(copy(A.data'),k) else return triu!(copy(A.data'),1) + triu!(tril(A.data),k) end end function triu(A::Symmetric, k::Integer=0) if A.uplo == 'U' && k >= 0 return triu(A.data,k) elseif A.uplo == 'U' && k < 0 return triu(A.data,1) + triu!(tril!(copy(transpose(A.data))),k) elseif A.uplo == 'L' && k >= 0 return triu!(copy(transpose(A.data)),k) else return triu!(copy(transpose(A.data)),1) + triu!(tril(A.data),k) end end for (T, trans, real) in [(:Symmetric, :transpose, :identity), (:(Hermitian{<:Union{Real,Complex}}), :adjoint, :real)] @eval begin function dot(A::$T, B::$T) n = size(A, 2) if n != size(B, 2) throw(DimensionMismatch("A has dimensions $(size(A)) but B has dimensions $(size(B))")) end dotprod = zero(dot(first(A), first(B))) @inbounds if A.uplo == 'U' && B.uplo == 'U' for j in 1:n for i in 1:(j - 1) dotprod += 2 * $real(dot(A.data[i, j], B.data[i, j])) end dotprod += dot(A[j, j], B[j, j]) end elseif A.uplo == 'L' && B.uplo == 'L' for j in 1:n dotprod += dot(A[j, j], B[j, j]) for i in (j + 1):n dotprod += 2 * $real(dot(A.data[i, j], B.data[i, j])) end end elseif A.uplo == 'U' && B.uplo == 'L' for j in 1:n for i in 1:(j - 1) dotprod += 2 * $real(dot(A.data[i, j], $trans(B.data[j, i]))) end dotprod += dot(A[j, j], B[j, j]) end else for j in 1:n dotprod += dot(A[j, j], B[j, j]) for i in (j + 1):n dotprod += 2 * $real(dot(A.data[i, j], $trans(B.data[j, i]))) end end end return dotprod end end end (-)(A::Symmetric) = Symmetric(-A.data, sym_uplo(A.uplo)) (-)(A::Hermitian) = Hermitian(-A.data, sym_uplo(A.uplo)) ## Addition/subtraction for f โˆˆ (:+, :-), (Wrapper, conjugation) โˆˆ ((:Hermitian, :adjoint), (:Symmetric, :transpose)) @eval begin function $f(A::$Wrapper, B::$Wrapper) if A.uplo == B.uplo return $Wrapper($f(parent(A), parent(B)), sym_uplo(A.uplo)) elseif A.uplo == 'U' return $Wrapper($f(parent(A), $conjugation(parent(B))), :U) else return $Wrapper($f($conjugation(parent(A)), parent(B)), :U) end end end end for f in (:+, :-) @eval begin $f(A::Hermitian, B::Symmetric{<:Real}) = $f(A, Hermitian(parent(B), sym_uplo(B.uplo))) $f(A::Symmetric{<:Real}, B::Hermitian) = $f(Hermitian(parent(A), sym_uplo(A.uplo)), B) $f(A::SymTridiagonal, B::Symmetric) = Symmetric($f(A, B.data), sym_uplo(B.uplo)) $f(A::Symmetric, B::SymTridiagonal) = Symmetric($f(A.data, B), sym_uplo(A.uplo)) $f(A::SymTridiagonal{<:Real}, B::Hermitian) = Hermitian($f(A, B.data), sym_uplo(B.uplo)) $f(A::Hermitian, B::SymTridiagonal{<:Real}) = Hermitian($f(A.data, B), sym_uplo(A.uplo)) end end *(A::HermOrSym, B::HermOrSym) = A * copyto!(similar(parent(B)), B) function dot(x::AbstractVector, A::RealHermSymComplexHerm, y::AbstractVector) require_one_based_indexing(x, y) n = length(x) (n == length(y) == size(A, 1)) || throw(DimensionMismatch()) data = A.data r = dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) iszero(n) && return r if A.uplo == 'U' @inbounds for j = 1:length(y) r += dot(x[j], real(data[j,j]), y[j]) @simd for i = 1:j-1 Aij = data[i,j] r += dot(x[i], Aij, y[j]) + dot(x[j], adjoint(Aij), y[i]) end end else # A.uplo == 'L' @inbounds for j = 1:length(y) r += dot(x[j], real(data[j,j]), y[j]) @simd for i = j+1:length(y) Aij = data[i,j] r += dot(x[i], Aij, y[j]) + dot(x[j], adjoint(Aij), y[i]) end end end return r end # Scaling with Number *(A::Symmetric, x::Number) = Symmetric(A.data*x, sym_uplo(A.uplo)) *(x::Number, A::Symmetric) = Symmetric(x*A.data, sym_uplo(A.uplo)) *(A::Hermitian, x::Real) = Hermitian(A.data*x, sym_uplo(A.uplo)) *(x::Real, A::Hermitian) = Hermitian(x*A.data, sym_uplo(A.uplo)) /(A::Symmetric, x::Number) = Symmetric(A.data/x, sym_uplo(A.uplo)) /(A::Hermitian, x::Real) = Hermitian(A.data/x, sym_uplo(A.uplo)) factorize(A::HermOrSym) = _factorize(A) function _factorize(A::HermOrSym{T}; check::Bool=true) where T TT = typeof(sqrt(oneunit(T))) if isdiag(A) return Diagonal(A) elseif TT <: BlasFloat return bunchkaufman(A; check=check) else # fallback return lu(A; check=check) end end det(A::RealHermSymComplexHerm) = real(det(_factorize(A; check=false))) det(A::Symmetric{<:Real}) = det(_factorize(A; check=false)) det(A::Symmetric) = det(_factorize(A; check=false)) \(A::HermOrSym, B::AbstractVector) = \(factorize(A), B) # Bunch-Kaufman solves can not utilize BLAS-3 for multiple right hand sides # so using LU is faster for AbstractMatrix right hand side \(A::HermOrSym, B::AbstractMatrix) = \(isdiag(A) ? Diagonal(A) : lu(A), B) function _inv(A::HermOrSym) n = checksquare(A) B = inv!(lu(A)) conjugate = isa(A, Hermitian) # symmetrize if A.uplo == 'U' # add to upper triangle @inbounds for i = 1:n, j = i:n B[i,j] = conjugate ? (B[i,j] + conj(B[j,i])) / 2 : (B[i,j] + B[j,i]) / 2 end else # A.uplo == 'L', add to lower triangle @inbounds for i = 1:n, j = i:n B[j,i] = conjugate ? (B[j,i] + conj(B[i,j])) / 2 : (B[j,i] + B[i,j]) / 2 end end B end # StridedMatrix restriction seems necessary due to inv! call in _inv above inv(A::Hermitian{<:Any,<:StridedMatrix}) = Hermitian(_inv(A), sym_uplo(A.uplo)) inv(A::Symmetric{<:Any,<:StridedMatrix}) = Symmetric(_inv(A), sym_uplo(A.uplo)) function svd(A::RealHermSymComplexHerm; full::Bool=false) vals, vecs = eigen(A) I = sortperm(vals; by=abs, rev=true) permute!(vals, I) Base.permutecols!!(vecs, I) # left-singular vectors V = copy(vecs) # right-singular vectors # shifting -1 from singular values to right-singular vectors @inbounds for i = 1:length(vals) if vals[i] < 0 vals[i] = -vals[i] for j = 1:size(V,1); V[j,i] = -V[j,i]; end end end return SVD(vecs, vals, V') end function svdvals!(A::RealHermSymComplexHerm) vals = eigvals!(A) for i = 1:length(vals) vals[i] = abs(vals[i]) end return sort!(vals, rev = true) end # Matrix functions ^(A::Symmetric{<:Real}, p::Integer) = sympow(A, p) ^(A::Symmetric{<:Complex}, p::Integer) = sympow(A, p) function sympow(A::Symmetric, p::Integer) if p < 0 return Symmetric(Base.power_by_squaring(inv(A), -p)) else return Symmetric(Base.power_by_squaring(A, p)) end end function ^(A::Symmetric{<:Real}, p::Real) isinteger(p) && return integerpow(A, p) F = eigen(A) if all(ฮป -> ฮป โ‰ฅ 0, F.values) return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors') else return Symmetric((F.vectors * Diagonal((complex(F.values)).^p)) * F.vectors') end end function ^(A::Symmetric{<:Complex}, p::Real) isinteger(p) && return integerpow(A, p) return Symmetric(schurpow(A, p)) end function ^(A::Hermitian, p::Integer) if p < 0 retmat = Base.power_by_squaring(inv(A), -p) else retmat = Base.power_by_squaring(A, p) end for i = 1:size(A,1) retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) end function ^(A::Hermitian{T}, p::Real) where T isinteger(p) && return integerpow(A, p) F = eigen(A) if all(ฮป -> ฮป โ‰ฅ 0, F.values) retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors' if T <: Real return Hermitian(retmat) else for i = 1:size(A,1) retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) end else return (F.vectors * Diagonal((complex(F.values).^p))) * F.vectors' end end for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh) @eval begin function ($func)(A::HermOrSym{<:Real}) F = eigen(A) return Symmetric((F.vectors * Diagonal(($func).(F.values))) * F.vectors') end function ($func)(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' for i = 1:n retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) end end end function cis(A::Union{RealHermSymComplexHerm,SymTridiagonal{<:Real}}) F = eigen(A) # The returned matrix is unitary, and is complex-symmetric for real A return F.vectors .* cis.(F.values') * F.vectors' end for func in (:acos, :asin) @eval begin function ($func)(A::HermOrSym{<:Real}) F = eigen(A) if all(ฮป -> -1 โ‰ค ฮป โ‰ค 1, F.values) retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' else retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' end return Symmetric(retmat) end function ($func)(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) if all(ฮป -> -1 โ‰ค ฮป โ‰ค 1, F.values) retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' for i = 1:n retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) else return (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' end end end end function acosh(A::HermOrSym{<:Real}) F = eigen(A) if all(ฮป -> ฮป โ‰ฅ 1, F.values) retmat = (F.vectors * Diagonal(acosh.(F.values))) * F.vectors' else retmat = (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors' end return Symmetric(retmat) end function acosh(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) if all(ฮป -> ฮป โ‰ฅ 1, F.values) retmat = (F.vectors * Diagonal(acosh.(F.values))) * F.vectors' for i = 1:n retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) else return (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors' end end function sincos(A::HermOrSym{<:Real}) n = checksquare(A) F = eigen(A) S, C = Diagonal(similar(A, (n,))), Diagonal(similar(A, (n,))) for i in 1:n S.diag[i], C.diag[i] = sincos(F.values[i]) end return Symmetric((F.vectors * S) * F.vectors'), Symmetric((F.vectors * C) * F.vectors') end function sincos(A::Hermitian{<:Complex}) n = checksquare(A) F = eigen(A) S, C = Diagonal(similar(A, (n,))), Diagonal(similar(A, (n,))) for i in 1:n S.diag[i], C.diag[i] = sincos(F.values[i]) end retmatS, retmatC = (F.vectors * S) * F.vectors', (F.vectors * C) * F.vectors' for i = 1:n retmatS[i,i] = real(retmatS[i,i]) retmatC[i,i] = real(retmatC[i,i]) end return Hermitian(retmatS), Hermitian(retmatC) end for func in (:log, :sqrt) # sqrt has rtol arg to handle matrices that are semidefinite up to roundoff errors rtolarg = func === :sqrt ? Any[Expr(:kw, :(rtol::Real), :(eps(real(float(one(T))))*size(A,1)))] : Any[] rtolval = func === :sqrt ? :(-maximum(abs, F.values) * rtol) : 0 @eval begin function ($func)(A::HermOrSym{T}; $(rtolarg...)) where {T<:Real} F = eigen(A) ฮปโ‚€ = $rtolval # treat ฮป โ‰ฅ ฮปโ‚€ as "zero" eigenvalues up to roundoff if all(ฮป -> ฮป โ‰ฅ ฮปโ‚€, F.values) retmat = (F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors' else retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors' end return Symmetric(retmat) end function ($func)(A::Hermitian{T}; $(rtolarg...)) where {T<:Complex} n = checksquare(A) F = eigen(A) ฮปโ‚€ = $rtolval # treat ฮป โ‰ฅ ฮปโ‚€ as "zero" eigenvalues up to roundoff if all(ฮป -> ฮป โ‰ฅ ฮปโ‚€, F.values) retmat = (F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors' for i = 1:n retmat[i,i] = real(retmat[i,i]) end return Hermitian(retmat) else retmat = (F.vectors * Diagonal(($func).(complex(F.values)))) * F.vectors' return retmat end end end end """ hermitianpart(A, uplo=:U) -> Hermitian Return the Hermitian part of the square matrix `A`, defined as `(A + A') / 2`, as a [`Hermitian`](@ref) matrix. For real matrices `A`, this is also known as the symmetric part of `A`; it is also sometimes called the "operator real part". The optional argument `uplo` controls the corresponding argument of the [`Hermitian`](@ref) view. For real matrices, the latter is equivalent to a [`Symmetric`](@ref) view. See also [`hermitianpart!`](@ref) for the corresponding in-place operation. !!! compat "Julia 1.10" This function requires Julia 1.10 or later. """ hermitianpart(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart(A), uplo) """ hermitianpart!(A, uplo=:U) -> Hermitian Overwrite the square matrix `A` in-place with its Hermitian part `(A + A') / 2`, and return [`Hermitian(A, uplo)`](@ref). For real matrices `A`, this is also known as the symmetric part of `A`. See also [`hermitianpart`](@ref) for the corresponding out-of-place operation. !!! compat "Julia 1.10" This function requires Julia 1.10 or later. """ hermitianpart!(A::AbstractMatrix, uplo::Symbol=:U) = Hermitian(_hermitianpart!(A), uplo) _hermitianpart(A::AbstractMatrix) = _hermitianpart!(copy_similar(A, Base.promote_op(/, eltype(A), Int))) _hermitianpart(a::Number) = real(a) function _hermitianpart!(A::AbstractMatrix) require_one_based_indexing(A) n = checksquare(A) @inbounds for j in 1:n A[j, j] = _hermitianpart(A[j, j]) for i in 1:j-1 A[i, j] = val = (A[i, j] + adjoint(A[j, i])) / 2 A[j, i] = adjoint(val) end end return A end ## structured matrix printing ## function Base.replace_in_print_matrix(A::HermOrSym,i::Integer,j::Integer,s::AbstractString) ijminmax = minmax(i, j) inds = A.uplo == 'U' ? ijminmax : reverse(ijminmax) Base.replace_in_print_matrix(parent(A), inds..., s) end y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/cholesky.jlโn# This file is a part of Julia. License is MIT: https://julialang.org/license ########################## # Cholesky Factorization # ########################## # The dispatch structure in the cholesky, and cholesky! methods is a bit # complicated and some explanation is therefore provided in the following # # In the methods below, LAPACK is called when possible, i.e. StridedMatrices with Float32, # Float64, ComplexF32, and ComplexF64 element types. For other element or # matrix types, the unblocked Julia implementation in _chol! is used. For cholesky # and cholesky! pivoting is supported through a RowMaximum() argument. A type argument is # necessary for type stability since the output of cholesky and cholesky! is either # Cholesky or CholeskyPivoted. The latter is only # supported for the four LAPACK element types. For other types, e.g. BigFloats RowMaximum() will # give an error. It is required that the input is Hermitian (including real symmetric) either # through the Hermitian and Symmetric views or exact symmetric or Hermitian elements which # is checked for and an error is thrown if the check fails. # The internal structure is as follows # - _chol! returns the factor and info without checking positive definiteness # - cholesky/cholesky! returns Cholesky without checking positive definiteness # FixMe? The dispatch below seems overly complicated. One simplification could be to # merge the two Cholesky types into one. It would remove the need for Val completely but # the cost would be extra unnecessary/unused fields for the unpivoted Cholesky and runtime # checks of those fields before calls to LAPACK to check which version of the Cholesky # factorization the type represents. """ Cholesky <: Factorization Matrix factorization type of the Cholesky factorization of a dense symmetric/Hermitian positive definite matrix `A`. This is the return type of [`cholesky`](@ref), the corresponding matrix factorization function. The triangular Cholesky factor can be obtained from the factorization `F::Cholesky` via `F.L` and `F.U`, where `A โ‰ˆ F.U' * F.U โ‰ˆ F.L * F.L'`. The following functions are available for `Cholesky` objects: [`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) and [`isposdef`](@ref). Iterating the decomposition produces the components `L` and `U`. # Examples ```jldoctest julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.] 3ร—3 Matrix{Float64}: 4.0 12.0 -16.0 12.0 37.0 -43.0 -16.0 -43.0 98.0 julia> C = cholesky(A) Cholesky{Float64, Matrix{Float64}} U factor: 3ร—3 UpperTriangular{Float64, Matrix{Float64}}: 2.0 6.0 -8.0 โ‹… 1.0 5.0 โ‹… โ‹… 3.0 julia> C.U 3ร—3 UpperTriangular{Float64, Matrix{Float64}}: 2.0 6.0 -8.0 โ‹… 1.0 5.0 โ‹… โ‹… 3.0 julia> C.L 3ร—3 LowerTriangular{Float64, Matrix{Float64}}: 2.0 โ‹… โ‹… 6.0 1.0 โ‹… -8.0 5.0 3.0 julia> C.L * C.U == A true julia> l, u = C; # destructuring via iteration julia> l == C.L && u == C.U true ``` """ struct Cholesky{T,S<:AbstractMatrix} <: Factorization{T} factors::S uplo::Char info::BlasInt function Cholesky{T,S}(factors, uplo, info) where {T,S<:AbstractMatrix} require_one_based_indexing(factors) new(factors, uplo, info) end end Cholesky(A::AbstractMatrix{T}, uplo::Symbol, info::Integer) where {T} = Cholesky{T,typeof(A)}(A, char_uplo(uplo), info) Cholesky(A::AbstractMatrix{T}, uplo::AbstractChar, info::Integer) where {T} = Cholesky{T,typeof(A)}(A, uplo, info) Cholesky(U::UpperTriangular{T}) where {T} = Cholesky{T,typeof(U.data)}(U.data, 'U', 0) Cholesky(L::LowerTriangular{T}) where {T} = Cholesky{T,typeof(L.data)}(L.data, 'L', 0) # iteration for destructuring into components Base.iterate(C::Cholesky) = (C.L, Val(:U)) Base.iterate(C::Cholesky, ::Val{:U}) = (C.U, Val(:done)) Base.iterate(C::Cholesky, ::Val{:done}) = nothing """ CholeskyPivoted Matrix factorization type of the pivoted Cholesky factorization of a dense symmetric/Hermitian positive semi-definite matrix `A`. This is the return type of [`cholesky(_, ::RowMaximum)`](@ref), the corresponding matrix factorization function. The triangular Cholesky factor can be obtained from the factorization `F::CholeskyPivoted` via `F.L` and `F.U`, and the permutation via `F.p`, where `A[F.p, F.p] โ‰ˆ Ur' * Ur โ‰ˆ Lr * Lr'` with `Ur = F.U[1:F.rank, :]` and `Lr = F.L[:, 1:F.rank]`, or alternatively `A โ‰ˆ Up' * Up โ‰ˆ Lp * Lp'` with `Up = F.U[1:F.rank, invperm(F.p)]` and `Lp = F.L[invperm(F.p), 1:F.rank]`. The following functions are available for `CholeskyPivoted` objects: [`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), and [`rank`](@ref). Iterating the decomposition produces the components `L` and `U`. # Examples ```jldoctest julia> X = [1.0, 2.0, 3.0, 4.0]; julia> A = X * X'; julia> C = cholesky(A, RowMaximum(), check = false) CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}} U factor with rank 1: 4ร—4 UpperTriangular{Float64, Matrix{Float64}}: 4.0 2.0 3.0 1.0 โ‹… 0.0 6.0 2.0 โ‹… โ‹… 9.0 3.0 โ‹… โ‹… โ‹… 1.0 permutation: 4-element Vector{Int64}: 4 2 3 1 julia> C.U[1:C.rank, :]' * C.U[1:C.rank, :] โ‰ˆ A[C.p, C.p] true julia> l, u = C; # destructuring via iteration julia> l == C.L && u == C.U true ``` """ struct CholeskyPivoted{T,S<:AbstractMatrix,P<:AbstractVector{<:Integer}} <: Factorization{T} factors::S uplo::Char piv::P rank::BlasInt tol::Real info::BlasInt function CholeskyPivoted{T,S,P}(factors, uplo, piv, rank, tol, info) where {T,S<:AbstractMatrix,P<:AbstractVector} require_one_based_indexing(factors) new{T,S,P}(factors, uplo, piv, rank, tol, info) end end CholeskyPivoted(A::AbstractMatrix{T}, uplo::AbstractChar, piv::AbstractVector{<:Integer}, rank::Integer, tol::Real, info::Integer) where T = CholeskyPivoted{T,typeof(A),typeof(piv)}(A, uplo, piv, rank, tol, info) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(CholeskyPivoted{T,S}(factors, uplo, piv, rank, tol, info) where {T,S<:AbstractMatrix}, CholeskyPivoted{T,S,typeof(piv)}(factors, uplo, piv, rank, tol, info), false) # iteration for destructuring into components Base.iterate(C::CholeskyPivoted) = (C.L, Val(:U)) Base.iterate(C::CholeskyPivoted, ::Val{:U}) = (C.U, Val(:done)) Base.iterate(C::CholeskyPivoted, ::Val{:done}) = nothing # make a copy that allow inplace Cholesky factorization choltype(A) = promote_type(typeof(sqrt(oneunit(eltype(A)))), Float32) cholcopy(A::AbstractMatrix) = eigencopy_oftype(A, choltype(A)) # _chol!. Internal methods for calling unpivoted Cholesky ## BLAS/LAPACK element types function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{UpperTriangular}) C, info = LAPACK.potrf!('U', A) return UpperTriangular(C), info end function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular}) C, info = LAPACK.potrf!('L', A) return LowerTriangular(C), info end ## Non BLAS/LAPACK element types (generic) function _chol!(A::AbstractMatrix, ::Type{UpperTriangular}) require_one_based_indexing(A) n = checksquare(A) realdiag = eltype(A) <: Complex @inbounds begin for k = 1:n Akk = realdiag ? real(A[k,k]) : A[k,k] for i = 1:k - 1 Akk -= realdiag ? abs2(A[i,k]) : A[i,k]'A[i,k] end A[k,k] = Akk Akk, info = _chol!(Akk, UpperTriangular) if info != 0 return UpperTriangular(A), convert(BlasInt, k) end A[k,k] = Akk AkkInv = inv(copy(Akk')) for j = k + 1:n for i = 1:k - 1 A[k,j] -= A[i,k]'A[i,j] end A[k,j] = AkkInv*A[k,j] end end end return UpperTriangular(A), convert(BlasInt, 0) end function _chol!(A::AbstractMatrix, ::Type{LowerTriangular}) require_one_based_indexing(A) n = checksquare(A) realdiag = eltype(A) <: Complex @inbounds begin for k = 1:n Akk = realdiag ? real(A[k,k]) : A[k,k] for i = 1:k - 1 Akk -= realdiag ? abs2(A[k,i]) : A[k,i]*A[k,i]' end A[k,k] = Akk Akk, info = _chol!(Akk, LowerTriangular) if info != 0 return LowerTriangular(A), convert(BlasInt, k) end A[k,k] = Akk AkkInv = inv(Akk) for j = 1:k - 1 @simd for i = k + 1:n A[i,k] -= A[i,j]*A[k,j]' end end for i = k + 1:n A[i,k] *= AkkInv' end end end return LowerTriangular(A), convert(BlasInt, 0) end ## Numbers function _chol!(x::Number, _) rx = real(x) iszero(rx) && return (rx, convert(BlasInt, 1)) rxr = sqrt(abs(rx)) rval = convert(promote_type(typeof(x), typeof(rxr)), rxr) return (rval, convert(BlasInt, rx != abs(x))) end ## for StridedMatrices, check that matrix is symmetric/Hermitian # cholesky!. Destructive methods for computing Cholesky factorization of real symmetric # or Hermitian matrix ## No pivoting (default) function cholesky!(A::RealHermSymComplexHerm, ::NoPivot = NoPivot(); check::Bool = true) C, info = _chol!(A.data, A.uplo == 'U' ? UpperTriangular : LowerTriangular) check && checkpositivedefinite(info) return Cholesky(C.data, A.uplo, info) end ### for AbstractMatrix, check that matrix is symmetric/Hermitian """ cholesky!(A::AbstractMatrix, NoPivot(); check = true) -> Cholesky The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the factorization produces a number not representable by the element type of `A`, e.g. for integer types. # Examples ```jldoctest julia> A = [1 2; 2 50] 2ร—2 Matrix{Int64}: 1 2 2 50 julia> cholesky!(A) ERROR: InexactError: Int64(6.782329983125268) Stacktrace: [...] ``` """ function cholesky!(A::AbstractMatrix, ::NoPivot = NoPivot(); check::Bool = true) checksquare(A) if !ishermitian(A) # return with info = -1 if not Hermitian check && checkpositivedefinite(-1) return Cholesky(A, 'U', convert(BlasInt, -1)) else return cholesky!(Hermitian(A), NoPivot(); check = check) end end @deprecate cholesky!(A::StridedMatrix, ::Val{false}; check::Bool = true) cholesky!(A, NoPivot(); check) false @deprecate cholesky!(A::RealHermSymComplexHerm, ::Val{false}; check::Bool = true) cholesky!(A, NoPivot(); check) false ## With pivoting ### BLAS/LAPACK element types function cholesky!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, ::RowMaximum; tol = 0.0, check::Bool = true) AA, piv, rank, info = LAPACK.pstrf!(A.uplo, A.data, tol) C = CholeskyPivoted{eltype(AA),typeof(AA),typeof(piv)}(AA, A.uplo, piv, rank, tol, info) check && chkfullrank(C) return C end @deprecate cholesky!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, ::Val{true}; kwargs...) cholesky!(A, RowMaximum(); kwargs...) false ### Non BLAS/LAPACK element types (generic). Since generic fallback for pivoted Cholesky ### is not implemented yet we throw an error cholesky!(A::RealHermSymComplexHerm{<:Real}, ::RowMaximum; tol = 0.0, check::Bool = true) = throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet")) @deprecate cholesky!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; kwargs...) cholesky!(A, RowMaximum(); kwargs...) false ### for AbstractMatrix, check that matrix is symmetric/Hermitian """ cholesky!(A::AbstractMatrix, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted The same as [`cholesky`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the factorization produces a number not representable by the element type of `A`, e.g. for integer types. """ function cholesky!(A::AbstractMatrix, ::RowMaximum; tol = 0.0, check::Bool = true) checksquare(A) if !ishermitian(A) C = CholeskyPivoted(A, 'U', Vector{BlasInt}(),convert(BlasInt, 1), tol, convert(BlasInt, -1)) check && chkfullrank(C) return C else return cholesky!(Hermitian(A), RowMaximum(); tol = tol, check = check) end end @deprecate cholesky!(A::StridedMatrix, ::Val{true}; kwargs...) cholesky!(A, RowMaximum(); kwargs...) false # cholesky. Non-destructive methods for computing Cholesky factorization of real symmetric # or Hermitian matrix ## No pivoting (default) """ cholesky(A, NoPivot(); check = true) -> Cholesky Compute the Cholesky factorization of a dense symmetric positive definite matrix `A` and return a [`Cholesky`](@ref) factorization. The matrix `A` can either be a [`Symmetric`](@ref) or [`Hermitian`](@ref) [`AbstractMatrix`](@ref) or a *perfectly* symmetric or Hermitian `AbstractMatrix`. The triangular Cholesky factor can be obtained from the factorization `F` via `F.L` and `F.U`, where `A โ‰ˆ F.U' * F.U โ‰ˆ F.L * F.L'`. The following functions are available for `Cholesky` objects: [`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), [`logdet`](@ref) and [`isposdef`](@ref). If you have a matrix `A` that is slightly non-Hermitian due to roundoff errors in its construction, wrap it in `Hermitian(A)` before passing it to `cholesky` in order to treat it as perfectly Hermitian. When `check = true`, an error is thrown if the decomposition fails. When `check = false`, responsibility for checking the decomposition's validity (via [`issuccess`](@ref)) lies with the user. # Examples ```jldoctest julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.] 3ร—3 Matrix{Float64}: 4.0 12.0 -16.0 12.0 37.0 -43.0 -16.0 -43.0 98.0 julia> C = cholesky(A) Cholesky{Float64, Matrix{Float64}} U factor: 3ร—3 UpperTriangular{Float64, Matrix{Float64}}: 2.0 6.0 -8.0 โ‹… 1.0 5.0 โ‹… โ‹… 3.0 julia> C.U 3ร—3 UpperTriangular{Float64, Matrix{Float64}}: 2.0 6.0 -8.0 โ‹… 1.0 5.0 โ‹… โ‹… 3.0 julia> C.L 3ร—3 LowerTriangular{Float64, Matrix{Float64}}: 2.0 โ‹… โ‹… 6.0 1.0 โ‹… -8.0 5.0 3.0 julia> C.L * C.U == A true ``` """ cholesky(A::AbstractMatrix, ::NoPivot=NoPivot(); check::Bool = true) = cholesky!(cholcopy(A); check) @deprecate cholesky(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, ::Val{false}; check::Bool = true) cholesky(A, NoPivot(); check) false function cholesky(A::AbstractMatrix{Float16}, ::NoPivot=NoPivot(); check::Bool = true) X = cholesky!(cholcopy(A); check = check) return Cholesky{Float16}(X) end @deprecate cholesky(A::Union{StridedMatrix{Float16},RealHermSymComplexHerm{Float16,<:StridedMatrix}}, ::Val{false}; check::Bool = true) cholesky(A, NoPivot(); check) false ## With pivoting """ cholesky(A, RowMaximum(); tol = 0.0, check = true) -> CholeskyPivoted Compute the pivoted Cholesky factorization of a dense symmetric positive semi-definite matrix `A` and return a [`CholeskyPivoted`](@ref) factorization. The matrix `A` can either be a [`Symmetric`](@ref) or [`Hermitian`](@ref) [`AbstractMatrix`](@ref) or a *perfectly* symmetric or Hermitian `AbstractMatrix`. The triangular Cholesky factor can be obtained from the factorization `F` via `F.L` and `F.U`, and the permutation via `F.p`, where `A[F.p, F.p] โ‰ˆ Ur' * Ur โ‰ˆ Lr * Lr'` with `Ur = F.U[1:F.rank, :]` and `Lr = F.L[:, 1:F.rank]`, or alternatively `A โ‰ˆ Up' * Up โ‰ˆ Lp * Lp'` with `Up = F.U[1:F.rank, invperm(F.p)]` and `Lp = F.L[invperm(F.p), 1:F.rank]`. The following functions are available for `CholeskyPivoted` objects: [`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), and [`rank`](@ref). The argument `tol` determines the tolerance for determining the rank. For negative values, the tolerance is the machine precision. If you have a matrix `A` that is slightly non-Hermitian due to roundoff errors in its construction, wrap it in `Hermitian(A)` before passing it to `cholesky` in order to treat it as perfectly Hermitian. When `check = true`, an error is thrown if the decomposition fails. When `check = false`, responsibility for checking the decomposition's validity (via [`issuccess`](@ref)) lies with the user. # Examples ```jldoctest julia> X = [1.0, 2.0, 3.0, 4.0]; julia> A = X * X'; julia> C = cholesky(A, RowMaximum(), check = false) CholeskyPivoted{Float64, Matrix{Float64}, Vector{Int64}} U factor with rank 1: 4ร—4 UpperTriangular{Float64, Matrix{Float64}}: 4.0 2.0 3.0 1.0 โ‹… 0.0 6.0 2.0 โ‹… โ‹… 9.0 3.0 โ‹… โ‹… โ‹… 1.0 permutation: 4-element Vector{Int64}: 4 2 3 1 julia> C.U[1:C.rank, :]' * C.U[1:C.rank, :] โ‰ˆ A[C.p, C.p] true julia> l, u = C; # destructuring via iteration julia> l == C.L && u == C.U true ``` """ cholesky(A::AbstractMatrix, ::RowMaximum; tol = 0.0, check::Bool = true) = cholesky!(cholcopy(A), RowMaximum(); tol, check) @deprecate cholesky(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, ::Val{true}; tol = 0.0, check::Bool = true) cholesky(A, RowMaximum(); tol, check) false function cholesky(A::AbstractMatrix{Float16}, ::RowMaximum; tol = 0.0, check::Bool = true) X = cholesky!(cholcopy(A), RowMaximum(); tol, check) return CholeskyPivoted{Float16}(X) end ## Number function cholesky(x::Number, uplo::Symbol=:U) C, info = _chol!(x, uplo) xf = fill(C, 1, 1) Cholesky(xf, uplo, info) end function Cholesky{T}(C::Cholesky) where T Cnew = convert(AbstractMatrix{T}, C.factors) Cholesky{T, typeof(Cnew)}(Cnew, C.uplo, C.info) end Factorization{T}(C::Cholesky{T}) where {T} = C Factorization{T}(C::Cholesky) where {T} = Cholesky{T}(C) CholeskyPivoted{T}(C::CholeskyPivoted{T}) where {T} = C CholeskyPivoted{T}(C::CholeskyPivoted) where {T} = CholeskyPivoted(AbstractMatrix{T}(C.factors),C.uplo,C.piv,C.rank,C.tol,C.info) Factorization{T}(C::CholeskyPivoted{T}) where {T} = C Factorization{T}(C::CholeskyPivoted) where {T} = CholeskyPivoted{T}(C) AbstractMatrix(C::Cholesky) = C.uplo == 'U' ? C.U'C.U : C.L*C.L' AbstractArray(C::Cholesky) = AbstractMatrix(C) Matrix(C::Cholesky) = Array(AbstractArray(C)) Array(C::Cholesky) = Matrix(C) function AbstractMatrix(F::CholeskyPivoted) ip = invperm(F.p) U = F.U[1:F.rank,ip] U'U end AbstractArray(F::CholeskyPivoted) = AbstractMatrix(F) Matrix(F::CholeskyPivoted) = Array(AbstractArray(F)) Array(F::CholeskyPivoted) = Matrix(F) copy(C::Cholesky) = Cholesky(copy(C.factors), C.uplo, C.info) copy(C::CholeskyPivoted) = CholeskyPivoted(copy(C.factors), C.uplo, C.piv, C.rank, C.tol, C.info) size(C::Union{Cholesky, CholeskyPivoted}) = size(C.factors) size(C::Union{Cholesky, CholeskyPivoted}, d::Integer) = size(C.factors, d) function getproperty(C::Cholesky, d::Symbol) Cfactors = getfield(C, :factors) Cuplo = getfield(C, :uplo) if d === :U return UpperTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) elseif d === :L return LowerTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) elseif d === :UL return (Cuplo === 'U' ? UpperTriangular(Cfactors) : LowerTriangular(Cfactors)) else return getfield(C, d) end end Base.propertynames(F::Cholesky, private::Bool=false) = (:U, :L, :UL, (private ? fieldnames(typeof(F)) : ())...) function getproperty(C::CholeskyPivoted{T}, d::Symbol) where {T} Cfactors = getfield(C, :factors) Cuplo = getfield(C, :uplo) if d === :U return UpperTriangular(sym_uplo(Cuplo) == d ? Cfactors : copy(Cfactors')) elseif d === :L return LowerTriangular(sym_uplo(Cuplo) == d ? Cfactors : copy(Cfactors')) elseif d === :p return getfield(C, :piv) elseif d === :P n = size(C, 1) P = zeros(T, n, n) for i = 1:n P[getfield(C, :piv)[i], i] = one(T) end return P else return getfield(C, d) end end Base.propertynames(F::CholeskyPivoted, private::Bool=false) = (:U, :L, :p, :P, (private ? fieldnames(typeof(F)) : ())...) issuccess(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 adjoint(C::Union{Cholesky,CholeskyPivoted}) = C function show(io::IO, mime::MIME{Symbol("text/plain")}, C::Cholesky) if issuccess(C) summary(io, C); println(io) println(io, "$(C.uplo) factor:") show(io, mime, C.UL) else print(io, "Failed factorization of type $(typeof(C))") end end function show(io::IO, mime::MIME{Symbol("text/plain")}, C::CholeskyPivoted) summary(io, C); println(io) println(io, "$(C.uplo) factor with rank $(rank(C)):") show(io, mime, C.uplo == 'U' ? C.U : C.L) println(io, "\npermutation:") show(io, mime, C.p) end ldiv!(C::Cholesky{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.potrs!(C.uplo, C.factors, B) function ldiv!(C::Cholesky, B::AbstractVecOrMat) if C.uplo == 'L' return ldiv!(adjoint(LowerTriangular(C.factors)), ldiv!(LowerTriangular(C.factors), B)) else return ldiv!(UpperTriangular(C.factors), ldiv!(adjoint(UpperTriangular(C.factors)), B)) end end function ldiv!(C::CholeskyPivoted{T,<:StridedMatrix}, B::StridedVector{T}) where T<:BlasFloat invpermute!(LAPACK.potrs!(C.uplo, C.factors, permute!(B, C.piv)), C.piv) end function ldiv!(C::CholeskyPivoted{T,<:StridedMatrix}, B::StridedMatrix{T}) where T<:BlasFloat n = size(C, 1) for i=1:size(B, 2) permute!(view(B, 1:n, i), C.piv) end LAPACK.potrs!(C.uplo, C.factors, B) for i=1:size(B, 2) invpermute!(view(B, 1:n, i), C.piv) end B end function ldiv!(C::CholeskyPivoted, B::AbstractVector) if C.uplo == 'L' ldiv!(adjoint(LowerTriangular(C.factors)), ldiv!(LowerTriangular(C.factors), permute!(B, C.piv))) else ldiv!(UpperTriangular(C.factors), ldiv!(adjoint(UpperTriangular(C.factors)), permute!(B, C.piv))) end invpermute!(B, C.piv) end function ldiv!(C::CholeskyPivoted, B::AbstractMatrix) n = size(C, 1) for i in 1:size(B, 2) permute!(view(B, 1:n, i), C.piv) end if C.uplo == 'L' ldiv!(adjoint(LowerTriangular(C.factors)), ldiv!(LowerTriangular(C.factors), B)) else ldiv!(UpperTriangular(C.factors), ldiv!(adjoint(UpperTriangular(C.factors)), B)) end for i in 1:size(B, 2) invpermute!(view(B, 1:n, i), C.piv) end B end function rdiv!(B::AbstractMatrix, C::Cholesky) if C.uplo == 'L' return rdiv!(rdiv!(B, adjoint(LowerTriangular(C.factors))), LowerTriangular(C.factors)) else return rdiv!(rdiv!(B, UpperTriangular(C.factors)), adjoint(UpperTriangular(C.factors))) end end function LinearAlgebra.rdiv!(B::AbstractMatrix, C::CholeskyPivoted) n = size(C, 2) for i in 1:size(B, 1) permute!(view(B, i, 1:n), C.piv) end if C.uplo == 'L' rdiv!(rdiv!(B, adjoint(LowerTriangular(C.factors))), LowerTriangular(C.factors)) else rdiv!(rdiv!(B, UpperTriangular(C.factors)), adjoint(UpperTriangular(C.factors))) end for i in 1:size(B, 1) invpermute!(view(B, i, 1:n), C.piv) end B end isposdef(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 function det(C::Cholesky) dd = one(real(eltype(C))) @inbounds for i in 1:size(C.factors,1) dd *= real(C.factors[i,i])^2 end return dd end function logdet(C::Cholesky) dd = zero(real(eltype(C))) @inbounds for i in 1:size(C.factors,1) dd += log(real(C.factors[i,i])) end dd + dd # instead of 2.0dd which can change the type end function det(C::CholeskyPivoted) if C.rank < size(C.factors, 1) return zero(real(eltype(C))) else dd = one(real(eltype(C))) for i in 1:size(C.factors,1) dd *= real(C.factors[i,i])^2 end return dd end end function logdet(C::CholeskyPivoted) if C.rank < size(C.factors, 1) return real(eltype(C))(-Inf) else dd = zero(real(eltype(C))) for i in 1:size(C.factors,1) dd += log(real(C.factors[i,i])) end return dd + dd # instead of 2.0dd which can change the type end end logabsdet(C::Union{Cholesky, CholeskyPivoted}) = logdet(C), one(eltype(C)) # since C is p.s.d. inv!(C::Cholesky{<:BlasFloat,<:StridedMatrix}) = copytri!(LAPACK.potri!(C.uplo, C.factors), C.uplo, true) inv(C::Cholesky{<:BlasFloat,<:StridedMatrix}) = inv!(copy(C)) function inv(C::CholeskyPivoted{<:BlasFloat,<:StridedMatrix}) ipiv = invperm(C.piv) copytri!(LAPACK.potri!(C.uplo, copy(C.factors)), C.uplo, true)[ipiv, ipiv] end function chkfullrank(C::CholeskyPivoted) if C.rank < size(C.factors, 1) throw(RankDeficientException(C.info)) end end rank(C::CholeskyPivoted) = C.rank """ lowrankupdate!(C::Cholesky, v::AbstractVector) -> CC::Cholesky Update a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then `CC = cholesky(C.U'C.U + v*v')` but the computation of `CC` only uses `O(n^2)` operations. The input factorization `C` is updated in place such that on exit `C == CC`. The vector `v` is destroyed during the computation. """ function lowrankupdate!(C::Cholesky, v::AbstractVector) A = C.factors n = length(v) if size(C, 1) != n throw(DimensionMismatch("updating vector must fit size of factorization")) end if C.uplo == 'U' conj!(v) end for i = 1:n # Compute Givens rotation c, s, r = givensAlgorithm(A[i,i], v[i]) # Store new diagonal element A[i,i] = r # Update remaining elements in row/column if C.uplo == 'U' for j = i + 1:n Aij = A[i,j] vj = v[j] A[i,j] = c*Aij + s*vj v[j] = -s'*Aij + c*vj end else for j = i + 1:n Aji = A[j,i] vj = v[j] A[j,i] = c*Aji + s*vj v[j] = -s'*Aji + c*vj end end end return C end """ lowrankdowndate!(C::Cholesky, v::AbstractVector) -> CC::Cholesky Downdate a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then `CC = cholesky(C.U'C.U - v*v')` but the computation of `CC` only uses `O(n^2)` operations. The input factorization `C` is updated in place such that on exit `C == CC`. The vector `v` is destroyed during the computation. """ function lowrankdowndate!(C::Cholesky, v::AbstractVector) A = C.factors n = length(v) if size(C, 1) != n throw(DimensionMismatch("updating vector must fit size of factorization")) end if C.uplo == 'U' conj!(v) end for i = 1:n Aii = A[i,i] # Compute Givens rotation s = conj(v[i]/Aii) s2 = abs2(s) if s2 > 1 throw(LinearAlgebra.PosDefException(i)) end c = sqrt(1 - abs2(s)) # Store new diagonal element A[i,i] = c*Aii # Update remaining elements in row/column if C.uplo == 'U' for j = i + 1:n vj = v[j] Aij = (A[i,j] - s*vj)/c A[i,j] = Aij v[j] = -s'*Aij + c*vj end else for j = i + 1:n vj = v[j] Aji = (A[j,i] - s*vj)/c A[j,i] = Aji v[j] = -s'*Aji + c*vj end end end return C end """ lowrankupdate(C::Cholesky, v::AbstractVector) -> CC::Cholesky Update a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then `CC = cholesky(C.U'C.U + v*v')` but the computation of `CC` only uses `O(n^2)` operations. """ lowrankupdate(C::Cholesky, v::AbstractVector) = lowrankupdate!(copy(C), copy(v)) """ lowrankdowndate(C::Cholesky, v::AbstractVector) -> CC::Cholesky Downdate a Cholesky factorization `C` with the vector `v`. If `A = C.U'C.U` then `CC = cholesky(C.U'C.U - v*v')` but the computation of `CC` only uses `O(n^2)` operations. """ lowrankdowndate(C::Cholesky, v::AbstractVector) = lowrankdowndate!(copy(C), copy(v)) s/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/lu.jl7_# This file is a part of Julia. License is MIT: https://julialang.org/license #################### # LU Factorization # #################### """ LU <: Factorization Matrix factorization type of the `LU` factorization of a square matrix `A`. This is the return type of [`lu`](@ref), the corresponding matrix factorization function. The individual components of the factorization `F::LU` can be accessed via [`getproperty`](@ref): | Component | Description | |:----------|:-----------------------------------------| | `F.L` | `L` (unit lower triangular) part of `LU` | | `F.U` | `U` (upper triangular) part of `LU` | | `F.p` | (right) permutation `Vector` | | `F.P` | (right) permutation `Matrix` | Iterating the factorization produces the components `F.L`, `F.U`, and `F.p`. # Examples ```jldoctest julia> A = [4 3; 6 3] 2ร—2 Matrix{Int64}: 4 3 6 3 julia> F = lu(A) LU{Float64, Matrix{Float64}, Vector{Int64}} L factor: 2ร—2 Matrix{Float64}: 1.0 0.0 0.666667 1.0 U factor: 2ร—2 Matrix{Float64}: 6.0 3.0 0.0 1.0 julia> F.L * F.U == A[F.p, :] true julia> l, u, p = lu(A); # destructuring via iteration julia> l == F.L && u == F.U && p == F.p true ``` """ struct LU{T,S<:AbstractMatrix{T},P<:AbstractVector{<:Integer}} <: Factorization{T} factors::S ipiv::P info::BlasInt function LU{T,S,P}(factors, ipiv, info) where {T, S<:AbstractMatrix{T}, P<:AbstractVector{<:Integer}} require_one_based_indexing(factors) new{T,S,P}(factors, ipiv, info) end end LU(factors::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, info::BlasInt) where {T} = LU{T,typeof(factors),typeof(ipiv)}(factors, ipiv, info) LU{T}(factors::AbstractMatrix, ipiv::AbstractVector{<:Integer}, info::Integer) where {T} = LU(convert(AbstractMatrix{T}, factors), ipiv, BlasInt(info)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(LU{T,S}(factors::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, info::BlasInt) where {T,S}, LU{T,S,typeof(ipiv)}(factors, ipiv, info), false) # iteration for destructuring into components Base.iterate(S::LU) = (S.L, Val(:U)) Base.iterate(S::LU, ::Val{:U}) = (S.U, Val(:p)) Base.iterate(S::LU, ::Val{:p}) = (S.p, Val(:done)) Base.iterate(S::LU, ::Val{:done}) = nothing # LU prefers transpose over adjoint in the real case, override the generic fallback adjoint(F::LU{<:Real}) = TransposeFactorization(F) transpose(F::LU{<:Real}) = TransposeFactorization(F) # the following method is meant to catch calls to lu!(A::LAPACKArray) without a pivoting stategy lu!(A::StridedMatrix{<:BlasFloat}; check::Bool = true) = lu!(A, RowMaximum(); check=check) function lu!(A::StridedMatrix{T}, ::RowMaximum; check::Bool = true) where {T<:BlasFloat} lpt = LAPACK.getrf!(A; check) check && checknonsingular(lpt[3]) return LU{T,typeof(lpt[1]),typeof(lpt[2])}(lpt[1], lpt[2], lpt[3]) end function lu!(A::HermOrSym{T}, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(T); check::Bool = true) where {T} copytri!(A.data, A.uplo, isa(A, Hermitian)) lu!(A.data, pivot; check = check) end # for backward compatibility # TODO: remove towards Julia v2 @deprecate lu!(A::Union{StridedMatrix,HermOrSym,Tridiagonal}, ::Val{true}; check::Bool = true) lu!(A, RowMaximum(); check=check) @deprecate lu!(A::Union{StridedMatrix,HermOrSym,Tridiagonal}, ::Val{false}; check::Bool = true) lu!(A, NoPivot(); check=check) """ lu!(A, pivot = RowMaximum(); check = true) -> LU `lu!` is the same as [`lu`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the factorization produces a number not representable by the element type of `A`, e.g. for integer types. # Examples ```jldoctest julia> A = [4. 3.; 6. 3.] 2ร—2 Matrix{Float64}: 4.0 3.0 6.0 3.0 julia> F = lu!(A) LU{Float64, Matrix{Float64}, Vector{Int64}} L factor: 2ร—2 Matrix{Float64}: 1.0 0.0 0.666667 1.0 U factor: 2ร—2 Matrix{Float64}: 6.0 3.0 0.0 1.0 julia> iA = [4 3; 6 3] 2ร—2 Matrix{Int64}: 4 3 6 3 julia> lu!(iA) ERROR: InexactError: Int64(0.6666666666666666) Stacktrace: [...] ``` """ lu!(A::AbstractMatrix, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(eltype(A)); check::Bool = true) = generic_lufact!(A, pivot; check = check) function generic_lufact!(A::AbstractMatrix{T}, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(T); check::Bool = true) where {T} check && LAPACK.chkfinite(A) # Extract values m, n = size(A) minmn = min(m,n) # Initialize variables info = 0 ipiv = Vector{BlasInt}(undef, minmn) @inbounds begin for k = 1:minmn # find index max kp = k if pivot === RowMaximum() && k < m amax = abs(A[k, k]) for i = k+1:m absi = abs(A[i,k]) if absi > amax kp = i amax = absi end end elseif pivot === RowNonZero() for i = k:m if !iszero(A[i,k]) kp = i break end end end ipiv[k] = kp if !iszero(A[kp,k]) if k != kp # Interchange for i = 1:n tmp = A[k,i] A[k,i] = A[kp,i] A[kp,i] = tmp end end # Scale first column Akkinv = inv(A[k,k]) for i = k+1:m A[i,k] *= Akkinv end elseif info == 0 info = k end # Update the rest for j = k+1:n for i = k+1:m A[i,j] -= A[i,k]*A[k,j] end end end end check && checknonsingular(info, pivot) return LU{T,typeof(A),typeof(ipiv)}(A, ipiv, convert(BlasInt, info)) end function lutype(T::Type) # In generic_lufact!, the elements of the lower part of the matrix are # obtained using the division of two matrix elements. Hence their type can # be different (e.g. the division of two types with the same unit is a type # without unit). # The elements of the upper part are obtained by U - U * L # where U is an upper part element and L is a lower part element. # Therefore, the types LT, UT should be invariant under the map: # (LT, UT) -> begin # L = oneunit(UT) / oneunit(UT) # U = oneunit(UT) - oneunit(UT) * L # typeof(L), typeof(U) # end # The following should handle most cases UT = typeof(oneunit(T) - oneunit(T) * (oneunit(T) / (oneunit(T) + zero(T)))) LT = typeof(oneunit(UT) / oneunit(UT)) S = promote_type(T, LT, UT) end lupivottype(::Type{T}) where {T} = RowMaximum() # for all other types we must promote to a type which is stable under division """ lu(A, pivot = RowMaximum(); check = true) -> F::LU Compute the LU factorization of `A`. When `check = true`, an error is thrown if the decomposition fails. When `check = false`, responsibility for checking the decomposition's validity (via [`issuccess`](@ref)) lies with the user. In most cases, if `A` is a subtype `S` of `AbstractMatrix{T}` with an element type `T` supporting `+`, `-`, `*` and `/`, the return type is `LU{T,S{T}}`. In general, LU factorization involves a permutation of the rows of the matrix (corresponding to the `F.p` output described below), known as "pivoting" (because it corresponds to choosing which row contains the "pivot", the diagonal entry of `F.U`). One of the following pivoting strategies can be selected via the optional `pivot` argument: * `RowMaximum()` (default): the standard pivoting strategy; the pivot corresponds to the element of maximum absolute value among the remaining, to be factorized rows. This pivoting strategy requires the element type to also support [`abs`](@ref) and [`<`](@ref). (This is generally the only numerically stable option for floating-point matrices.) * `RowNonZero()`: the pivot corresponds to the first non-zero element among the remaining, to be factorized rows. (This corresponds to the typical choice in hand calculations, and is also useful for more general algebraic number types that support [`iszero`](@ref) but not `abs` or `<`.) * `NoPivot()`: pivoting turned off (may fail if a zero entry is encountered). The individual components of the factorization `F` can be accessed via [`getproperty`](@ref): | Component | Description | |:----------|:------------------------------------| | `F.L` | `L` (lower triangular) part of `LU` | | `F.U` | `U` (upper triangular) part of `LU` | | `F.p` | (right) permutation `Vector` | | `F.P` | (right) permutation `Matrix` | Iterating the factorization produces the components `F.L`, `F.U`, and `F.p`. The relationship between `F` and `A` is `F.L*F.U == A[F.p, :]` `F` further supports the following functions: | Supported function | `LU` | `LU{T,Tridiagonal{T}}` | |:---------------------------------|:-----|:-----------------------| | [`/`](@ref) | โœ“ | | | [`\\`](@ref) | โœ“ | โœ“ | | [`inv`](@ref) | โœ“ | โœ“ | | [`det`](@ref) | โœ“ | โœ“ | | [`logdet`](@ref) | โœ“ | โœ“ | | [`logabsdet`](@ref) | โœ“ | โœ“ | | [`size`](@ref) | โœ“ | โœ“ | # Examples ```jldoctest julia> A = [4 3; 6 3] 2ร—2 Matrix{Int64}: 4 3 6 3 julia> F = lu(A) LU{Float64, Matrix{Float64}, Vector{Int64}} L factor: 2ร—2 Matrix{Float64}: 1.0 0.0 0.666667 1.0 U factor: 2ร—2 Matrix{Float64}: 6.0 3.0 0.0 1.0 julia> F.L * F.U == A[F.p, :] true julia> l, u, p = lu(A); # destructuring via iteration julia> l == F.L && u == F.U && p == F.p true ``` """ function lu(A::AbstractMatrix{T}, pivot::Union{RowMaximum,NoPivot,RowNonZero} = lupivottype(T); check::Bool = true) where {T} lu!(_lucopy(A, lutype(T)), pivot; check = check) end # TODO: remove for Julia v2.0 @deprecate lu(A::AbstractMatrix, ::Val{true}; check::Bool = true) lu(A, RowMaximum(); check=check) @deprecate lu(A::AbstractMatrix, ::Val{false}; check::Bool = true) lu(A, NoPivot(); check=check) _lucopy(A::AbstractMatrix, T) = copy_similar(A, T) _lucopy(A::HermOrSym, T) = copymutable_oftype(A, T) _lucopy(A::Tridiagonal, T) = copymutable_oftype(A, T) lu(S::LU) = S function lu(x::Number; check::Bool=true) info = x == 0 ? one(BlasInt) : zero(BlasInt) check && checknonsingular(info) return LU(fill(x, 1, 1), BlasInt[1], info) end function LU{T}(F::LU) where T M = convert(AbstractMatrix{T}, F.factors) LU{T,typeof(M),typeof(F.ipiv)}(M, F.ipiv, F.info) end LU{T,S,P}(F::LU) where {T,S,P} = LU{T,S,P}(convert(S, F.factors), convert(P, F.ipiv), F.info) Factorization{T}(F::LU{T}) where {T} = F Factorization{T}(F::LU) where {T} = LU{T}(F) copy(A::LU{T,S,P}) where {T,S,P} = LU{T,S,P}(copy(A.factors), copy(A.ipiv), A.info) size(A::LU) = size(getfield(A, :factors)) size(A::LU, i::Integer) = size(getfield(A, :factors), i) function ipiv2perm(v::AbstractVector{T}, maxi::Integer) where T require_one_based_indexing(v) p = T[1:maxi;] @inbounds for i in 1:length(v) p[i], p[v[i]] = p[v[i]], p[i] end return p end function getproperty(F::LU{T}, d::Symbol) where T m, n = size(F) if d === :L L = tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) for i = 1:min(m,n); L[i,i] = one(T); end return L elseif d === :U return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) elseif d === :p return ipiv2perm(getfield(F, :ipiv), m) elseif d === :P return Matrix{T}(I, m, m)[:,invperm(F.p)] else getfield(F, d) end end Base.propertynames(F::LU, private::Bool=false) = (:L, :U, :p, :P, (private ? fieldnames(typeof(F)) : ())...) issuccess(F::LU) = F.info == 0 function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LU) if issuccess(F) summary(io, F); println(io) println(io, "L factor:") show(io, mime, F.L) println(io, "\nU factor:") show(io, mime, F.U) else print(io, "Failed factorization of type $(typeof(F))") end end _apply_ipiv_rows!(A::LU, B::AbstractVecOrMat) = _ipiv_rows!(A, 1 : length(A.ipiv), B) _apply_inverse_ipiv_rows!(A::LU, B::AbstractVecOrMat) = _ipiv_rows!(A, length(A.ipiv) : -1 : 1, B) function _ipiv_rows!(A::LU, order::OrdinalRange, B::AbstractVecOrMat) for i = order if i != A.ipiv[i] _swap_rows!(B, i, A.ipiv[i]) end end B end function _swap_rows!(B::AbstractVector, i::Integer, j::Integer) B[i], B[j] = B[j], B[i] B end function _swap_rows!(B::AbstractMatrix, i::Integer, j::Integer) for col = 1 : size(B, 2) B[i,col], B[j,col] = B[j,col], B[i,col] end B end _apply_ipiv_cols!(A::LU, B::AbstractVecOrMat) = _ipiv_cols!(A, 1 : length(A.ipiv), B) _apply_inverse_ipiv_cols!(A::LU, B::AbstractVecOrMat) = _ipiv_cols!(A, length(A.ipiv) : -1 : 1, B) function _ipiv_cols!(A::LU, order::OrdinalRange, B::AbstractVecOrMat) for i = order if i != A.ipiv[i] _swap_cols!(B, i, A.ipiv[i]) end end B end function _swap_cols!(B::AbstractVector, i::Integer, j::Integer) _swap_rows!(B, i, j) end function _swap_cols!(B::AbstractMatrix, i::Integer, j::Integer) for row = 1 : size(B, 1) B[row,i], B[row,j] = B[row,j], B[row,i] end B end function rdiv!(A::AbstractVecOrMat, B::LU) rdiv!(rdiv!(A, UpperTriangular(B.factors)), UnitLowerTriangular(B.factors)) _apply_inverse_ipiv_cols!(B, A) end ldiv!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.getrs!('N', A.factors, A.ipiv, B) function ldiv!(A::LU, B::AbstractVecOrMat) _apply_ipiv_rows!(A, B) ldiv!(UpperTriangular(A.factors), ldiv!(UnitLowerTriangular(A.factors), B)) end ldiv!(transA::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = (A = transA.parent; LAPACK.getrs!('T', A.factors, A.ipiv, B)) function ldiv!(transA::TransposeFactorization{<:Any,<:LU}, B::AbstractVecOrMat) A = transA.parent ldiv!(transpose(UnitLowerTriangular(A.factors)), ldiv!(transpose(UpperTriangular(A.factors)), B)) _apply_inverse_ipiv_rows!(A, B) end ldiv!(adjA::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = (A = adjA.parent; LAPACK.getrs!('C', A.factors, A.ipiv, B)) function ldiv!(adjA::AdjointFactorization{<:Any,<:LU}, B::AbstractVecOrMat) A = adjA.parent ldiv!(adjoint(UnitLowerTriangular(A.factors)), ldiv!(adjoint(UpperTriangular(A.factors)), B)) _apply_inverse_ipiv_rows!(A, B) end (\)(A::AdjointFactorization{T,<:LU{T,<:StridedMatrix}}, B::Adjoint{T,<:StridedVecOrMat{T}}) where {T<:BlasComplex} = LAPACK.getrs!('C', A.parent.factors, A.parent.ipiv, copy(B)) (\)(A::TransposeFactorization{T,<:LU{T,<:StridedMatrix}}, B::Transpose{T,<:StridedVecOrMat{T}}) where {T<:BlasFloat} = LAPACK.getrs!('T', A.parent.factors, A.parent.ipiv, copy(B)) function det(F::LU{T}) where T n = checksquare(F) issuccess(F) || return zero(T) P = one(T) c = 0 @inbounds for i = 1:n P *= F.factors[i,i] if F.ipiv[i] != i c += 1 end end s = (isodd(c) ? -one(T) : one(T)) return P * s end function logabsdet(F::LU{T}) where T # return log(abs(det)) and sign(det) n = checksquare(F) issuccess(F) || return log(zero(real(T))), log(one(T)) c = 0 P = one(T) abs_det = zero(real(T)) @inbounds for i = 1:n dg_ii = F.factors[i,i] P *= sign(dg_ii) if F.ipiv[i] != i c += 1 end abs_det += log(abs(dg_ii)) end s = ifelse(isodd(c), -one(real(T)), one(real(T))) * P abs_det, s end inv!(A::LU{<:BlasFloat,<:StridedMatrix}) = LAPACK.getri!(A.factors, A.ipiv) inv!(A::LU{T,<:StridedMatrix}) where {T} = ldiv!(A.factors, copy(A), Matrix{T}(I, size(A, 1), size(A, 1))) inv(A::LU{<:BlasFloat,<:StridedMatrix}) = inv!(copy(A)) # Tridiagonal # See dgttrf.f function lu!(A::Tridiagonal{T,V}, pivot::Union{RowMaximum,NoPivot} = RowMaximum(); check::Bool = true) where {T,V} # Extract values n = size(A, 1) # Initialize variables info = 0 ipiv = Vector{BlasInt}(undef, n) dl = A.dl d = A.d du = A.du if dl === du throw(ArgumentError("off-diagonals of `A` must not alias")) end # Check if Tridiagonal matrix already has du2 for pivoting has_du2_defined = isdefined(A, :du2) && length(A.du2) == max(0, n-2) if has_du2_defined du2 = A.du2::V else du2 = similar(d, max(0, n-2))::V end fill!(du2, 0) @inbounds begin for i = 1:n ipiv[i] = i end for i = 1:n-2 # pivot or not? if pivot === NoPivot() || abs(d[i]) >= abs(dl[i]) # No interchange if d[i] != 0 fact = dl[i]/d[i] dl[i] = fact d[i+1] -= fact*du[i] du2[i] = 0 end else # Interchange fact = d[i]/dl[i] d[i] = dl[i] dl[i] = fact tmp = du[i] du[i] = d[i+1] d[i+1] = tmp - fact*d[i+1] du2[i] = du[i+1] du[i+1] = -fact*du[i+1] ipiv[i] = i+1 end end if n > 1 i = n-1 if pivot === NoPivot() || abs(d[i]) >= abs(dl[i]) if d[i] != 0 fact = dl[i]/d[i] dl[i] = fact d[i+1] -= fact*du[i] end else fact = d[i]/dl[i] d[i] = dl[i] dl[i] = fact tmp = du[i] du[i] = d[i+1] d[i+1] = tmp - fact*d[i+1] ipiv[i] = i+1 end end # check for a zero on the diagonal of U for i = 1:n if d[i] == 0 info = i break end end end B = has_du2_defined ? A : Tridiagonal{T,V}(dl, d, du, du2) check && checknonsingular(info, pivot) return LU{T,Tridiagonal{T,V},typeof(ipiv)}(B, ipiv, convert(BlasInt, info)) end factorize(A::Tridiagonal) = lu(A) function getproperty(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} m, n = size(F) if d === :L dl = getfield(getfield(F, :factors), :dl) L = Array(Bidiagonal(fill!(similar(dl, n), one(T)), dl, d)) for i = 2:n tmp = L[getfield(F, :ipiv)[i], 1:i - 1] L[getfield(F, :ipiv)[i], 1:i - 1] = L[i, 1:i - 1] L[i, 1:i - 1] = tmp end return L elseif d === :U U = Array(Bidiagonal(getfield(getfield(F, :factors), :d), getfield(getfield(F, :factors), :du), d)) for i = 1:n - 2 U[i,i + 2] = getfield(getfield(F, :factors), :du2)[i] end return U elseif d === :p return ipiv2perm(getfield(F, :ipiv), m) elseif d === :P return Matrix{T}(I, m, m)[:,invperm(F.p)] end return getfield(F, d) end # See dgtts2.f function ldiv!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} require_one_based_indexing(B) n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) end nrhs = size(B,2) dl = A.factors.dl d = A.factors.d du = A.factors.du du2 = A.factors.du2 ipiv = A.ipiv @inbounds begin for j = 1:nrhs for i = 1:n-1 ip = ipiv[i] tmp = B[i+1-ip+i,j] - dl[i]*B[ip,j] B[i,j] = B[ip,j] B[i+1,j] = tmp end B[n,j] /= d[n] if n > 1 B[n-1,j] = (B[n-1,j] - du[n-1]*B[n,j])/d[n-1] end for i = n-2:-1:1 B[i,j] = (B[i,j] - du[i]*B[i+1,j] - du2[i]*B[i+2,j])/d[i] end end end return B end function ldiv!(transA::TransposeFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} require_one_based_indexing(B) A = transA.parent n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) end nrhs = size(B,2) dl = A.factors.dl d = A.factors.d du = A.factors.du du2 = A.factors.du2 ipiv = A.ipiv @inbounds begin for j = 1:nrhs B[1,j] /= d[1] if n > 1 B[2,j] = (B[2,j] - du[1]*B[1,j])/d[2] end for i = 3:n B[i,j] = (B[i,j] - du[i-1]*B[i-1,j] - du2[i-2]*B[i-2,j])/d[i] end for i = n-1:-1:1 if ipiv[i] == i B[i,j] = B[i,j] - dl[i]*B[i+1,j] else tmp = B[i+1,j] B[i+1,j] = B[i,j] - dl[i]*tmp B[i,j] = tmp end end end end return B end # Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) function ldiv!(adjA::AdjointFactorization{<:Any,<:LU{T,Tridiagonal{T,V}}}, B::AbstractVecOrMat) where {T,V} require_one_based_indexing(B) A = adjA.parent n = size(A,1) if n != size(B,1) throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows")) end nrhs = size(B,2) dl = A.factors.dl d = A.factors.d du = A.factors.du du2 = A.factors.du2 ipiv = A.ipiv @inbounds begin for j = 1:nrhs B[1,j] /= conj(d[1]) if n > 1 B[2,j] = (B[2,j] - conj(du[1])*B[1,j])/conj(d[2]) end for i = 3:n B[i,j] = (B[i,j] - conj(du[i-1])*B[i-1,j] - conj(du2[i-2])*B[i-2,j])/conj(d[i]) end for i = n-1:-1:1 if ipiv[i] == i B[i,j] = B[i,j] - conj(dl[i])*B[i+1,j] else tmp = B[i+1,j] B[i+1,j] = B[i,j] - conj(dl[i])*tmp B[i,j] = tmp end end end end return B end rdiv!(B::AbstractMatrix, A::LU) = transpose(ldiv!(transpose(A), transpose(B))) # Conversions AbstractMatrix(F::LU) = (F.L * F.U)[invperm(F.p),:] AbstractArray(F::LU) = AbstractMatrix(F) Matrix(F::LU) = Array(AbstractArray(F)) Array(F::LU) = Matrix(F) function Tridiagonal(F::LU{T,Tridiagonal{T,V}}) where {T,V} n = size(F, 1) dl = copy(F.factors.dl) d = copy(F.factors.d) du = copy(F.factors.du) du2 = copy(F.factors.du2) for i = n - 1:-1:1 li = dl[i] dl[i] = li*d[i] d[i + 1] += li*du[i] if i < n - 1 du[i + 1] += li*du2[i] end if F.ipiv[i] != i tmp = dl[i] dl[i] = d[i] d[i] = tmp tmp = d[i + 1] d[i + 1] = du[i] du[i] = tmp if i < n - 1 tmp = du[i + 1] du[i + 1] = du2[i] du2[i] = tmp end end end return Tridiagonal(dl, d, du) end AbstractMatrix(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Tridiagonal(F) AbstractArray(F::LU{T,Tridiagonal{T,V}}) where {T,V} = AbstractMatrix(F) Matrix(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Array(AbstractArray(F)) Array(F::LU{T,Tridiagonal{T,V}}) where {T,V} = Matrix(F) }/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/bunchkaufman.jlศ2# This file is a part of Julia. License is MIT: https://julialang.org/license ## Create an extractor that extracts the modified original matrix, e.g. ## LD for BunchKaufman, UL for CholeskyDense, LU for LUDense and ## define size methods for Factorization types using it. """ BunchKaufman <: Factorization Matrix factorization type of the Bunch-Kaufman factorization of a symmetric or Hermitian matrix `A` as `P'UDU'P` or `P'LDL'P`, depending on whether the upper (the default) or the lower triangle is stored in `A`. If `A` is complex symmetric then `U'` and `L'` denote the unconjugated transposes, i.e. `transpose(U)` and `transpose(L)`, respectively. This is the return type of [`bunchkaufman`](@ref), the corresponding matrix factorization function. If `S::BunchKaufman` is the factorization object, the components can be obtained via `S.D`, `S.U` or `S.L` as appropriate given `S.uplo`, and `S.p`. Iterating the decomposition produces the components `S.D`, `S.U` or `S.L` as appropriate given `S.uplo`, and `S.p`. # Examples ```jldoctest julia> A = [1 2; 2 3] 2ร—2 Matrix{Int64}: 1 2 2 3 julia> S = bunchkaufman(A) # A gets wrapped internally by Symmetric(A) BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} D factor: 2ร—2 Tridiagonal{Float64, Vector{Float64}}: -0.333333 0.0 0.0 3.0 U factor: 2ร—2 UnitUpperTriangular{Float64, Matrix{Float64}}: 1.0 0.666667 โ‹… 1.0 permutation: 2-element Vector{Int64}: 1 2 julia> d, u, p = S; # destructuring via iteration julia> d == S.D && u == S.U && p == S.p true julia> S = bunchkaufman(Symmetric(A, :L)) BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} D factor: 2ร—2 Tridiagonal{Float64, Vector{Float64}}: 3.0 0.0 0.0 -0.333333 L factor: 2ร—2 UnitLowerTriangular{Float64, Matrix{Float64}}: 1.0 โ‹… 0.666667 1.0 permutation: 2-element Vector{Int64}: 2 1 ``` """ struct BunchKaufman{T,S<:AbstractMatrix,P<:AbstractVector{<:Integer}} <: Factorization{T} LD::S ipiv::P uplo::Char symmetric::Bool rook::Bool info::BlasInt function BunchKaufman{T,S,P}(LD, ipiv, uplo, symmetric, rook, info) where {T,S<:AbstractMatrix,P<:AbstractVector} require_one_based_indexing(LD) new{T,S,P}(LD, ipiv, uplo, symmetric, rook, info) end end BunchKaufman(A::AbstractMatrix{T}, ipiv::AbstractVector{<:Integer}, uplo::AbstractChar, symmetric::Bool, rook::Bool, info::BlasInt) where {T} = BunchKaufman{T,typeof(A),typeof(ipiv)}(A, ipiv, uplo, symmetric, rook, info) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(BunchKaufman{T,S}(LD, ipiv, uplo, symmetric, rook, info) where {T,S}, BunchKaufman{T,S,typeof(ipiv)}(LD, ipiv, uplo, symmetric, rook, info), false) # iteration for destructuring into components Base.iterate(S::BunchKaufman) = (S.D, Val(:UL)) Base.iterate(S::BunchKaufman, ::Val{:UL}) = (S.uplo == 'L' ? S.L : S.U, Val(:p)) Base.iterate(S::BunchKaufman, ::Val{:p}) = (S.p, Val(:done)) Base.iterate(S::BunchKaufman, ::Val{:done}) = nothing """ bunchkaufman!(A, rook::Bool=false; check = true) -> BunchKaufman `bunchkaufman!` is the same as [`bunchkaufman`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. """ function bunchkaufman!(A::RealHermSymComplexSym{<:BlasReal,<:StridedMatrix}, rook::Bool = false; check::Bool = true) LD, ipiv, info = rook ? LAPACK.sytrf_rook!(A.uplo, A.data) : LAPACK.sytrf!(A.uplo, A.data) check && checknonsingular(info) BunchKaufman(LD, ipiv, A.uplo, true, rook, info) end function bunchkaufman!(A::Hermitian{<:BlasComplex,<:StridedMatrix}, rook::Bool = false; check::Bool = true) LD, ipiv, info = rook ? LAPACK.hetrf_rook!(A.uplo, A.data) : LAPACK.hetrf!(A.uplo, A.data) check && checknonsingular(info) BunchKaufman(LD, ipiv, A.uplo, false, rook, info) end function bunchkaufman!(A::StridedMatrix{<:BlasFloat}, rook::Bool = false; check::Bool = true) if ishermitian(A) return bunchkaufman!(Hermitian(A), rook; check = check) elseif issymmetric(A) return bunchkaufman!(Symmetric(A), rook; check = check) else throw(ArgumentError("Bunch-Kaufman decomposition is only valid for symmetric or Hermitian matrices")) end end """ bunchkaufman(A, rook::Bool=false; check = true) -> S::BunchKaufman Compute the Bunch-Kaufman [^Bunch1977] factorization of a symmetric or Hermitian matrix `A` as `P'*U*D*U'*P` or `P'*L*D*L'*P`, depending on which triangle is stored in `A`, and return a [`BunchKaufman`](@ref) object. Note that if `A` is complex symmetric then `U'` and `L'` denote the unconjugated transposes, i.e. `transpose(U)` and `transpose(L)`. Iterating the decomposition produces the components `S.D`, `S.U` or `S.L` as appropriate given `S.uplo`, and `S.p`. If `rook` is `true`, rook pivoting is used. If `rook` is false, rook pivoting is not used. When `check = true`, an error is thrown if the decomposition fails. When `check = false`, responsibility for checking the decomposition's validity (via [`issuccess`](@ref)) lies with the user. The following functions are available for `BunchKaufman` objects: [`size`](@ref), `\\`, [`inv`](@ref), [`issymmetric`](@ref), [`ishermitian`](@ref), [`getindex`](@ref). [^Bunch1977]: J R Bunch and L Kaufman, Some stable methods for calculating inertia and solving symmetric linear systems, Mathematics of Computation 31:137 (1977), 163-179. [url](http://www.ams.org/journals/mcom/1977-31-137/S0025-5718-1977-0428694-0/). # Examples ```jldoctest julia> A = [1 2; 2 3] 2ร—2 Matrix{Int64}: 1 2 2 3 julia> S = bunchkaufman(A) # A gets wrapped internally by Symmetric(A) BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} D factor: 2ร—2 Tridiagonal{Float64, Vector{Float64}}: -0.333333 0.0 0.0 3.0 U factor: 2ร—2 UnitUpperTriangular{Float64, Matrix{Float64}}: 1.0 0.666667 โ‹… 1.0 permutation: 2-element Vector{Int64}: 1 2 julia> d, u, p = S; # destructuring via iteration julia> d == S.D && u == S.U && p == S.p true julia> S.U*S.D*S.U' - S.P*A*S.P' 2ร—2 Matrix{Float64}: 0.0 0.0 0.0 0.0 julia> S = bunchkaufman(Symmetric(A, :L)) BunchKaufman{Float64, Matrix{Float64}, Vector{Int64}} D factor: 2ร—2 Tridiagonal{Float64, Vector{Float64}}: 3.0 0.0 0.0 -0.333333 L factor: 2ร—2 UnitLowerTriangular{Float64, Matrix{Float64}}: 1.0 โ‹… 0.666667 1.0 permutation: 2-element Vector{Int64}: 2 1 julia> S.L*S.D*S.L' - A[S.p, S.p] 2ร—2 Matrix{Float64}: 0.0 0.0 0.0 0.0 ``` """ bunchkaufman(A::AbstractMatrix{T}, rook::Bool=false; check::Bool = true) where {T} = bunchkaufman!(eigencopy_oftype(A, typeof(sqrt(oneunit(T)))), rook; check = check) BunchKaufman{T}(B::BunchKaufman) where {T} = BunchKaufman(convert(Matrix{T}, B.LD), B.ipiv, B.uplo, B.symmetric, B.rook, B.info) Factorization{T}(B::BunchKaufman) where {T} = BunchKaufman{T}(B) size(B::BunchKaufman) = size(getfield(B, :LD)) size(B::BunchKaufman, d::Integer) = size(getfield(B, :LD), d) issymmetric(B::BunchKaufman) = B.symmetric ishermitian(B::BunchKaufman{T}) where T = T<:Real || !B.symmetric function _ipiv2perm_bk(v::AbstractVector{T}, maxi::Integer, uplo::AbstractChar, rook::Bool) where T require_one_based_indexing(v) p = T[1:maxi;] uploL = uplo == 'L' i = uploL ? 1 : maxi # if uplo == 'U' we construct the permutation backwards @inbounds while 1 <= i <= length(v) vi = v[i] if vi > 0 # the 1x1 blocks p[i], p[vi] = p[vi], p[i] i += uploL ? 1 : -1 else # the 2x2 blocks if rook p[i], p[-vi] = p[-vi], p[i] end if uploL vp = rook ? -v[i+1] : -vi p[i + 1], p[vp] = p[vp], p[i + 1] i += 2 else # 'U' vp = rook ? -v[i-1] : -vi p[i - 1], p[vp] = p[vp], p[i - 1] i -= 2 end end end return p end function getproperty(B::BunchKaufman{T,<:StridedMatrix}, d::Symbol) where {T<:BlasFloat} n = size(B, 1) if d === :p return _ipiv2perm_bk(getfield(B, :ipiv), n, getfield(B, :uplo), B.rook) elseif d === :P return Matrix{T}(I, n, n)[:,invperm(B.p)] elseif d === :L || d === :U || d === :D if getfield(B, :rook) LUD, od = LAPACK.syconvf_rook!(getfield(B, :uplo), 'C', copy(getfield(B, :LD)), getfield(B, :ipiv)) else LUD, od = LAPACK.syconv!(getfield(B, :uplo), copy(getfield(B, :LD)), getfield(B, :ipiv)) end if d === :D if getfield(B, :uplo) == 'L' odl = od[1:n - 1] return Tridiagonal(odl, diag(LUD), getfield(B, :symmetric) ? odl : conj.(odl)) else # 'U' odu = od[2:n] return Tridiagonal(getfield(B, :symmetric) ? odu : conj.(odu), diag(LUD), odu) end elseif d === :L if getfield(B, :uplo) == 'L' return UnitLowerTriangular(LUD) else throw(ArgumentError("factorization is U*D*transpose(U) but you requested L")) end else # :U if B.uplo == 'U' return UnitUpperTriangular(LUD) else throw(ArgumentError("factorization is L*D*transpose(L) but you requested U")) end end else getfield(B, d) end end Base.propertynames(B::BunchKaufman, private::Bool=false) = (:p, :P, :L, :U, :D, (private ? fieldnames(typeof(B)) : ())...) issuccess(B::BunchKaufman) = B.info == 0 function adjoint(B::BunchKaufman) if ishermitian(B) return B else throw(ArgumentError("adjoint not implemented for complex symmetric matrices")) end end function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, B::BunchKaufman) if issuccess(B) summary(io, B); println(io) println(io, "D factor:") show(io, mime, B.D) println(io, "\n$(B.uplo) factor:") show(io, mime, B.uplo == 'L' ? B.L : B.U) println(io, "\npermutation:") show(io, mime, B.p) else print(io, "Failed factorization of type $(typeof(B))") end end function inv(B::BunchKaufman{<:BlasReal,<:StridedMatrix}) if B.rook copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) else copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) end end function inv(B::BunchKaufman{<:BlasComplex,<:StridedMatrix}) if issymmetric(B) if B.rook copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo) else copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo) end else if B.rook copytri!(LAPACK.hetri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) else copytri!(LAPACK.hetri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true) end end end function ldiv!(B::BunchKaufman{T,<:StridedMatrix}, R::StridedVecOrMat{T}) where {T<:BlasReal} if B.rook LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R) else LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R) end end function ldiv!(B::BunchKaufman{T,<:StridedMatrix}, R::StridedVecOrMat{T}) where {T<:BlasComplex} if B.rook if issymmetric(B) LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R) else LAPACK.hetrs_rook!(B.uplo, B.LD, B.ipiv, R) end else if issymmetric(B) LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R) else LAPACK.hetrs!(B.uplo, B.LD, B.ipiv, R) end end end function logabsdet(F::BunchKaufman) M = F.LD p = F.ipiv n = size(F.LD, 1) if !issuccess(F) return eltype(F)(-Inf), zero(eltype(F)) end s = one(real(eltype(F))) i = 1 abs_det = zero(real(eltype(F))) while i <= n if p[i] > 0 elm = M[i,i] s *= sign(elm) abs_det += log(abs(elm)) i += 1 else # 2x2 pivot case. Make sure not to square before the subtraction by scaling # with the off-diagonal element. This is safe because the off diagonal is # always large for 2x2 pivots. if F.uplo == 'U' elm = M[i, i + 1]*(M[i,i]/M[i, i + 1]*M[i + 1, i + 1] - (issymmetric(F) ? M[i, i + 1] : conj(M[i, i + 1]))) s *= sign(elm) abs_det += log(abs(elm)) else elm = M[i + 1,i]*(M[i, i]/M[i + 1, i]*M[i + 1, i + 1] - (issymmetric(F) ? M[i + 1, i] : conj(M[i + 1, i]))) s *= sign(elm) abs_det += log(abs(elm)) end i += 2 end end return abs_det, s end ## reconstruct the original matrix ## TODO: understand the procedure described at ## http://www.nag.com/numeric/FL/nagdoc_fl22/pdf/F07/f07mdf.pdf y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/diagonal.jlอx# This file is a part of Julia. License is MIT: https://julialang.org/license ## Diagonal matrices struct Diagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} diag::V function Diagonal{T,V}(diag) where {T,V<:AbstractVector{T}} require_one_based_indexing(diag) new{T,V}(diag) end end Diagonal{T,V}(d::Diagonal) where {T,V<:AbstractVector{T}} = Diagonal{T,V}(d.diag) Diagonal(v::AbstractVector{T}) where {T} = Diagonal{T,typeof(v)}(v) Diagonal{T}(v::AbstractVector) where {T} = Diagonal(convert(AbstractVector{T}, v)::AbstractVector{T}) function Base.promote_rule(A::Type{<:Diagonal{<:Any,V}}, B::Type{<:Diagonal{<:Any,W}}) where {V,W} X = promote_type(V, W) T = eltype(X) isconcretetype(T) && return Diagonal{T,X} return typejoin(A, B) end """ Diagonal(V::AbstractVector) Construct a lazy matrix with `V` as its diagonal. See also [`UniformScaling`](@ref) for the lazy identity matrix `I`, [`diagm`](@ref) to make a dense matrix, and [`diag`](@ref) to extract diagonal elements. # Examples ```jldoctest julia> d = Diagonal([1, 10, 100]) 3ร—3 Diagonal{$Int, Vector{$Int}}: 1 โ‹… โ‹… โ‹… 10 โ‹… โ‹… โ‹… 100 julia> diagm([7, 13]) 2ร—2 Matrix{$Int}: 7 0 0 13 julia> ans + I 2ร—2 Matrix{Int64}: 8 0 0 14 julia> I(2) 2ร—2 Diagonal{Bool, Vector{Bool}}: 1 โ‹… โ‹… 1 ``` Note that a one-column matrix is not treated like a vector, but instead calls the method `Diagonal(A::AbstractMatrix)` which extracts 1-element `diag(A)`: ```jldoctest julia> A = transpose([7.0 13.0]) 2ร—1 transpose(::Matrix{Float64}) with eltype Float64: 7.0 13.0 julia> Diagonal(A) 1ร—1 Diagonal{Float64, Vector{Float64}}: 7.0 ``` """ Diagonal(V::AbstractVector) """ Diagonal(A::AbstractMatrix) Construct a matrix from the diagonal of `A`. # Examples ```jldoctest julia> A = permutedims(reshape(1:15, 5, 3)) 3ร—5 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 julia> Diagonal(A) 3ร—3 Diagonal{$Int, Vector{$Int}}: 1 โ‹… โ‹… โ‹… 7 โ‹… โ‹… โ‹… 13 julia> diag(A, 2) 3-element Vector{$Int}: 3 9 15 ``` """ Diagonal(A::AbstractMatrix) = Diagonal(diag(A)) Diagonal{T}(A::AbstractMatrix) where T = Diagonal{T}(diag(A)) function convert(::Type{T}, A::AbstractMatrix) where T<:Diagonal checksquare(A) isdiag(A) ? T(A) : throw(InexactError(:convert, T, A)) end Diagonal(D::Diagonal) = D Diagonal{T}(D::Diagonal{T}) where {T} = D Diagonal{T}(D::Diagonal) where {T} = Diagonal{T}(D.diag) AbstractMatrix{T}(D::Diagonal) where {T} = Diagonal{T}(D) Matrix(D::Diagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(D) Array(D::Diagonal{T}) where {T} = Matrix(D) function Matrix{T}(D::Diagonal) where {T} n = size(D, 1) B = Matrix{T}(undef, n, n) n > 1 && fill!(B, zero(T)) @inbounds for i in 1:n B[i,i] = D.diag[i] end return B end """ Diagonal{T}(undef, n) Construct an uninitialized `Diagonal{T}` of length `n`. See `undef`. """ Diagonal{T}(::UndefInitializer, n::Integer) where T = Diagonal(Vector{T}(undef, n)) similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T)) similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(D.diag, T, dims) copyto!(D1::Diagonal, D2::Diagonal) = (copyto!(D1.diag, D2.diag); D1) size(D::Diagonal) = (n = length(D.diag); (n,n)) function size(D::Diagonal,d::Integer) if d<1 throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) end return d<=2 ? length(D.diag) : 1 end @inline function Base.isassigned(D::Diagonal, i::Int, j::Int) @boundscheck checkbounds(Bool, D, i, j) || return false if i == j @inbounds r = isassigned(D.diag, i) else r = true end r end @inline function getindex(D::Diagonal, i::Int, j::Int) @boundscheck checkbounds(D, i, j) if i == j @inbounds r = D.diag[i] else r = diagzero(D, i, j) end r end diagzero(::Diagonal{T}, i, j) where {T} = zero(T) diagzero(D::Diagonal{<:AbstractMatrix{T}}, i, j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2)) function setindex!(D::Diagonal, v, i::Int, j::Int) @boundscheck checkbounds(D, i, j) if i == j @inbounds D.diag[i] = v elseif !iszero(v) throw(ArgumentError("cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)")) end return v end ## structured matrix methods ## function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString) i==j ? s : Base.replace_with_centered_mark(s) end parent(D::Diagonal) = D.diag ishermitian(D::Diagonal{<:Real}) = true ishermitian(D::Diagonal{<:Number}) = isreal(D.diag) ishermitian(D::Diagonal) = all(ishermitian, D.diag) issymmetric(D::Diagonal{<:Number}) = true issymmetric(D::Diagonal) = all(issymmetric, D.diag) isposdef(D::Diagonal) = all(isposdef, D.diag) factorize(D::Diagonal) = D real(D::Diagonal) = Diagonal(real(D.diag)) imag(D::Diagonal) = Diagonal(imag(D.diag)) iszero(D::Diagonal) = all(iszero, D.diag) isone(D::Diagonal) = all(isone, D.diag) isdiag(D::Diagonal) = all(isdiag, D.diag) isdiag(D::Diagonal{<:Number}) = true istriu(D::Diagonal, k::Integer=0) = k <= 0 || iszero(D.diag) ? true : false istril(D::Diagonal, k::Integer=0) = k >= 0 || iszero(D.diag) ? true : false function triu!(D::Diagonal{T}, k::Integer=0) where T n = size(D,1) if !(-n + 1 <= k <= n + 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 0 fill!(D.diag, zero(T)) end return D end function tril!(D::Diagonal{T}, k::Integer=0) where T n = size(D,1) if !(-n - 1 <= k <= n - 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < 0 fill!(D.diag, zero(T)) end return D end (==)(Da::Diagonal, Db::Diagonal) = Da.diag == Db.diag (-)(A::Diagonal) = Diagonal(-A.diag) (+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag) (-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag) for f in (:+, :-) @eval function $f(D::Diagonal, S::Symmetric) return Symmetric($f(D, S.data), sym_uplo(S.uplo)) end @eval function $f(S::Symmetric, D::Diagonal) return Symmetric($f(S.data, D), sym_uplo(S.uplo)) end @eval function $f(D::Diagonal{<:Real}, H::Hermitian) return Hermitian($f(D, H.data), sym_uplo(H.uplo)) end @eval function $f(H::Hermitian, D::Diagonal{<:Real}) return Hermitian($f(H.data, D), sym_uplo(H.uplo)) end end (*)(x::Number, D::Diagonal) = Diagonal(x * D.diag) (*)(D::Diagonal, x::Number) = Diagonal(D.diag * x) (/)(D::Diagonal, x::Number) = Diagonal(D.diag / x) (\)(x::Number, D::Diagonal) = Diagonal(x \ D.diag) (^)(D::Diagonal, a::Number) = Diagonal(D.diag .^ a) (^)(D::Diagonal, a::Real) = Diagonal(D.diag .^ a) # for disambiguation (^)(D::Diagonal, a::Integer) = Diagonal(D.diag .^ a) # for disambiguation Base.literal_pow(::typeof(^), D::Diagonal, valp::Val) = Diagonal(Base.literal_pow.(^, D.diag, valp)) # for speed Base.literal_pow(::typeof(^), D::Diagonal, ::Val{-1}) = inv(D) # for disambiguation function _muldiag_size_check(A, B) nA = size(A, 2) mB = size(B, 1) @noinline throw_dimerr(::AbstractMatrix, nA, mB) = throw(DimensionMismatch("second dimension of A, $nA, does not match first dimension of B, $mB")) @noinline throw_dimerr(::AbstractVector, nA, mB) = throw(DimensionMismatch("second dimension of D, $nA, does not match length of V, $mB")) nA == mB || throw_dimerr(B, nA, mB) return nothing end # the output matrix should have the same size as the non-diagonal input matrix or vector @noinline throw_dimerr(szC, szA) = throw(DimensionMismatch("output matrix has size: $szC, but should have size $szA")) _size_check_out(C, ::Diagonal, A) = _size_check_out(C, A) _size_check_out(C, A, ::Diagonal) = _size_check_out(C, A) _size_check_out(C, A::Diagonal, ::Diagonal) = _size_check_out(C, A) function _size_check_out(C, A) szA = size(A) szC = size(C) szA == szC || throw_dimerr(szC, szA) return nothing end function _muldiag_size_check(C, A, B) _muldiag_size_check(A, B) _size_check_out(C, A, B) end function (*)(Da::Diagonal, Db::Diagonal) _muldiag_size_check(Da, Db) return Diagonal(Da.diag .* Db.diag) end function (*)(D::Diagonal, V::AbstractVector) _muldiag_size_check(D, V) return D.diag .* V end (*)(A::AbstractMatrix, D::Diagonal) = mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag))), A, D) (*)(A::HermOrSym, D::Diagonal) = mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), A, D) (*)(D::Diagonal, A::AbstractMatrix) = mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag))), D, A) (*)(D::Diagonal, A::HermOrSym) = mul!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), D, A) rmul!(A::AbstractMatrix, D::Diagonal) = @inline mul!(A, A, D) lmul!(D::Diagonal, B::AbstractVecOrMat) = @inline mul!(B, D, B) function (*)(A::AdjOrTransAbsMat, D::Diagonal) Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag))) rmul!(Ac, D) end function (*)(D::Diagonal, A::AdjOrTransAbsMat) Ac = copy_similar(A, promote_op(*, eltype(A), eltype(D.diag))) lmul!(D, Ac) end function __muldiag!(out, D::Diagonal, B, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out, B) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else if bis0 @inbounds for j in axes(B, 2) @simd for i in axes(B, 1) out[i,j] = D.diag[i] * B[i,j] * alpha end end else @inbounds for j in axes(B, 2) @simd for i in axes(B, 1) out[i,j] = D.diag[i] * B[i,j] * alpha + out[i,j] * beta end end end end return out end function __muldiag!(out, A, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out, A) alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out, beta) else if bis0 @inbounds for j in axes(A, 2) dja = D.diag[j] * alpha @simd for i in axes(A, 1) out[i,j] = A[i,j] * dja end end else @inbounds for j in axes(A, 2) dja = D.diag[j] * alpha @simd for i in axes(A, 1) out[i,j] = A[i,j] * dja + out[i,j] * beta end end end end return out end function __muldiag!(out::Diagonal, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} d1 = D1.diag d2 = D2.diag alpha, beta = _add.alpha, _add.beta if iszero(alpha) _rmul_or_fill!(out.diag, beta) else if bis0 @inbounds @simd for i in eachindex(out.diag) out.diag[i] = d1[i] * d2[i] * alpha end else @inbounds @simd for i in eachindex(out.diag) out.diag[i] = d1[i] * d2[i] * alpha + out.diag[i] * beta end end end return out end function __muldiag!(out, D1::Diagonal, D2::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} require_one_based_indexing(out) alpha, beta = _add.alpha, _add.beta mA = size(D1, 1) d1 = D1.diag d2 = D2.diag _rmul_or_fill!(out, beta) if !iszero(alpha) @inbounds @simd for i in 1:mA out[i,i] += d1[i] * d2[i] * alpha end end return out end function _mul_diag!(out, A, B, _add) _muldiag_size_check(out, A, B) __muldiag!(out, A, B, _add) return out end _mul!(out::AbstractVecOrMat, D::Diagonal, V::AbstractVector, _add) = _mul_diag!(out, D, V, _add) _mul!(out::AbstractMatrix, D::Diagonal, B::AbstractMatrix, _add) = _mul_diag!(out, D, B, _add) _mul!(out::AbstractMatrix, A::AbstractMatrix, D::Diagonal, _add) = _mul_diag!(out, A, D, _add) _mul!(C::Diagonal, Da::Diagonal, Db::Diagonal, _add) = _mul_diag!(C, Da, Db, _add) _mul!(C::AbstractMatrix, Da::Diagonal, Db::Diagonal, _add) = _mul_diag!(C, Da, Db, _add) function (*)(Da::Diagonal, A::AbstractMatrix, Db::Diagonal) _muldiag_size_check(Da, A) _muldiag_size_check(A, Db) return broadcast(*, Da.diag, A, permutedims(Db.diag)) end function (*)(Da::Diagonal, Db::Diagonal, Dc::Diagonal) _muldiag_size_check(Da, Db) _muldiag_size_check(Db, Dc) return Diagonal(Da.diag .* Db.diag .* Dc.diag) end /(A::AbstractVecOrMat, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D))), A, D) /(A::HermOrSym, D::Diagonal) = _rdiv!(similar(A, _init_eltype(/, eltype(A), eltype(D)), size(A)), A, D) rdiv!(A::AbstractVecOrMat, D::Diagonal) = @inline _rdiv!(A, A, D) # avoid copy when possible via internal 3-arg backend function _rdiv!(B::AbstractVecOrMat, A::AbstractVecOrMat, D::Diagonal) require_one_based_indexing(A) dd = D.diag m, n = size(A, 1), size(A, 2) if (k = length(dd)) != n throw(DimensionMismatch("left hand side has $n columns but D is $k by $k")) end @inbounds for j in 1:n ddj = dd[j] iszero(ddj) && throw(SingularException(j)) for i in 1:m B[i, j] = A[i, j] / ddj end end B end function \(D::Diagonal, B::AbstractVector) j = findfirst(iszero, D.diag) isnothing(j) || throw(SingularException(j)) return D.diag .\ B end \(D::Diagonal, B::AbstractMatrix) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B))), D, B) \(D::Diagonal, B::HermOrSym) = ldiv!(similar(B, _init_eltype(\, eltype(D), eltype(B)), size(B)), D, B) ldiv!(D::Diagonal, B::AbstractVecOrMat) = @inline ldiv!(B, D, B) function ldiv!(B::AbstractVecOrMat, D::Diagonal, A::AbstractVecOrMat) require_one_based_indexing(A, B) dd = D.diag d = length(dd) m, n = size(A, 1), size(A, 2) mโ€ฒ, nโ€ฒ = size(B, 1), size(B, 2) m == d || throw(DimensionMismatch("right hand side has $m rows but D is $d by $d")) (m, n) == (mโ€ฒ, nโ€ฒ) || throw(DimensionMismatch("expect output to be $m by $n, but got $mโ€ฒ by $nโ€ฒ")) j = findfirst(iszero, D.diag) isnothing(j) || throw(SingularException(j)) @inbounds for j = 1:n, i = 1:m B[i, j] = dd[i] \ A[i, j] end B end # Optimizations for \, / between Diagonals \(D::Diagonal, B::Diagonal) = ldiv!(similar(B, promote_op(\, eltype(D), eltype(B))), D, B) /(A::Diagonal, D::Diagonal) = _rdiv!(similar(A, promote_op(/, eltype(A), eltype(D))), A, D) function _rdiv!(Dc::Diagonal, Db::Diagonal, Da::Diagonal) n, k = length(Db.diag), length(Da.diag) n == k || throw(DimensionMismatch("left hand side has $n columns but D is $k by $k")) j = findfirst(iszero, Da.diag) isnothing(j) || throw(SingularException(j)) Dc.diag .= Db.diag ./ Da.diag Dc end ldiv!(Dc::Diagonal, Da::Diagonal, Db::Diagonal) = Diagonal(ldiv!(Dc.diag, Da, Db.diag)) # optimizations for (Sym)Tridiagonal and Diagonal @propagate_inbounds _getudiag(T::Tridiagonal, i) = T.du[i] @propagate_inbounds _getudiag(S::SymTridiagonal, i) = S.ev[i] @propagate_inbounds _getdiag(T::Tridiagonal, i) = T.d[i] @propagate_inbounds _getdiag(S::SymTridiagonal, i) = symmetric(S.dv[i], :U)::symmetric_type(eltype(S.dv)) @propagate_inbounds _getldiag(T::Tridiagonal, i) = T.dl[i] @propagate_inbounds _getldiag(S::SymTridiagonal, i) = transpose(S.ev[i]) function (\)(D::Diagonal, S::SymTridiagonal) T = promote_op(\, eltype(D), eltype(S)) du = similar(S.ev, T, max(length(S.dv)-1, 0)) d = similar(S.dv, T, length(S.dv)) dl = similar(S.ev, T, max(length(S.dv)-1, 0)) ldiv!(Tridiagonal(dl, d, du), D, S) end (\)(D::Diagonal, T::Tridiagonal) = ldiv!(similar(T, promote_op(\, eltype(D), eltype(T))), D, T) function ldiv!(T::Tridiagonal, D::Diagonal, S::Union{SymTridiagonal,Tridiagonal}) m = size(S, 1) dd = D.diag if (k = length(dd)) != m throw(DimensionMismatch("diagonal matrix is $k by $k but right hand side has $m rows")) end if length(T.d) != m throw(DimensionMismatch("target matrix size $(size(T)) does not match input matrix size $(size(S))")) end m == 0 && return T j = findfirst(iszero, dd) isnothing(j) || throw(SingularException(j)) ddj = dd[1] T.d[1] = ddj \ _getdiag(S, 1) @inbounds if m > 1 T.du[1] = ddj \ _getudiag(S, 1) for j in 2:m-1 ddj = dd[j] T.dl[j-1] = ddj \ _getldiag(S, j-1) T.d[j] = ddj \ _getdiag(S, j) T.du[j] = ddj \ _getudiag(S, j) end ddj = dd[m] T.dl[m-1] = ddj \ _getldiag(S, m-1) T.d[m] = ddj \ _getdiag(S, m) end return T end function (/)(S::SymTridiagonal, D::Diagonal) T = promote_op(\, eltype(D), eltype(S)) du = similar(S.ev, T, max(length(S.dv)-1, 0)) d = similar(S.dv, T, length(S.dv)) dl = similar(S.ev, T, max(length(S.dv)-1, 0)) _rdiv!(Tridiagonal(dl, d, du), S, D) end (/)(T::Tridiagonal, D::Diagonal) = _rdiv!(similar(T, promote_op(/, eltype(T), eltype(D))), T, D) function _rdiv!(T::Tridiagonal, S::Union{SymTridiagonal,Tridiagonal}, D::Diagonal) n = size(S, 2) dd = D.diag if (k = length(dd)) != n throw(DimensionMismatch("left hand side has $n columns but D is $k by $k")) end if length(T.d) != n throw(DimensionMismatch("target matrix size $(size(T)) does not match input matrix size $(size(S))")) end n == 0 && return T j = findfirst(iszero, dd) isnothing(j) || throw(SingularException(j)) ddj = dd[1] T.d[1] = _getdiag(S, 1) / ddj @inbounds if n > 1 T.dl[1] = _getldiag(S, 1) / ddj for j in 2:n-1 ddj = dd[j] T.dl[j] = _getldiag(S, j) / ddj T.d[j] = _getdiag(S, j) / ddj T.du[j-1] = _getudiag(S, j-1) / ddj end ddj = dd[n] T.d[n] = _getdiag(S, n) / ddj T.du[n-1] = _getudiag(S, n-1) / ddj end return T end # Optimizations for [l/r]mul!, l/rdiv!, *, / and \ between Triangular and Diagonal. # These functions are generally more efficient if we calculate the whole data field. # The following code implements them in a unified pattern to avoid missing. @inline function _setdiag!(data, f, diag, diagโ€ฒ = nothing) @inbounds for i in 1:length(diag) data[i,i] = isnothing(diagโ€ฒ) ? f(diag[i]) : f(diag[i],diagโ€ฒ[i]) end data end for Tri in (:UpperTriangular, :LowerTriangular) UTri = Symbol(:Unit, Tri) # 2 args for (fun, f) in zip((:*, :rmul!, :rdiv!, :/), (:identity, :identity, :inv, :inv)) @eval $fun(A::$Tri, D::Diagonal) = $Tri($fun(A.data, D)) @eval $fun(A::$UTri, D::Diagonal) = $Tri(_setdiag!($fun(A.data, D), $f, D.diag)) end for (fun, f) in zip((:*, :lmul!, :ldiv!, :\), (:identity, :identity, :inv, :inv)) @eval $fun(D::Diagonal, A::$Tri) = $Tri($fun(D, A.data)) @eval $fun(D::Diagonal, A::$UTri) = $Tri(_setdiag!($fun(D, A.data), $f, D.diag)) end # 3-arg ldiv! @eval ldiv!(C::$Tri, D::Diagonal, A::$Tri) = $Tri(ldiv!(C.data, D, A.data)) @eval ldiv!(C::$Tri, D::Diagonal, A::$UTri) = $Tri(_setdiag!(ldiv!(C.data, D, A.data), inv, D.diag)) # 3-arg mul! is disambiguated in special.jl # 5-arg mul! @eval _mul!(C::$Tri, D::Diagonal, A::$Tri, _add) = $Tri(mul!(C.data, D, A.data, _add.alpha, _add.beta)) @eval function _mul!(C::$Tri, D::Diagonal, A::$UTri, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} ฮฑ, ฮฒ = _add.alpha, _add.beta iszero(ฮฑ) && return _rmul_or_fill!(C, ฮฒ) diagโ€ฒ = bis0 ? nothing : diag(C) data = mul!(C.data, D, A.data, ฮฑ, ฮฒ) $Tri(_setdiag!(data, _add, D.diag, diagโ€ฒ)) end @eval _mul!(C::$Tri, A::$Tri, D::Diagonal, _add) = $Tri(mul!(C.data, A.data, D, _add.alpha, _add.beta)) @eval function _mul!(C::$Tri, A::$UTri, D::Diagonal, _add::MulAddMul{ais1,bis0}) where {ais1,bis0} ฮฑ, ฮฒ = _add.alpha, _add.beta iszero(ฮฑ) && return _rmul_or_fill!(C, ฮฒ) diagโ€ฒ = bis0 ? nothing : diag(C) data = mul!(C.data, A.data, D, ฮฑ, ฮฒ) $Tri(_setdiag!(data, _add, D.diag, diagโ€ฒ)) end end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::Diagonal) valA = A.diag; nA = length(valA) valB = B.diag; nB = length(valB) nC = checksquare(C) @boundscheck nC == nA*nB || throw(DimensionMismatch("expect C to be a $(nA*nB)x$(nA*nB) matrix, got size $(nC)x$(nC)")) isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) @inbounds for i = 1:nA, j = 1:nB idx = (i-1)*nB+j C[idx, idx] = valA[i] * valB[j] end return C end kron(A::Diagonal, B::Diagonal) = Diagonal(kron(A.diag, B.diag)) function kron(A::Diagonal, B::SymTridiagonal) kdv = kron(diag(A), B.dv) # We don't need to drop the last element kev = kron(diag(A), _pushzero(_evview(B))) SymTridiagonal(kdv, kev) end function kron(A::Diagonal, B::Tridiagonal) # `_droplast!` is only guaranteed to work with `Vector` kd = _makevector(kron(diag(A), B.d)) kdl = _droplast!(_makevector(kron(diag(A), _pushzero(B.dl)))) kdu = _droplast!(_makevector(kron(diag(A), _pushzero(B.du)))) Tridiagonal(kdl, kd, kdu) end @inline function kron!(C::AbstractMatrix, A::Diagonal, B::AbstractMatrix) require_one_based_indexing(B) (mA, nA) = size(A) (mB, nB) = size(B) (mC, nC) = size(C) @boundscheck (mC, nC) == (mA * mB, nA * nB) || throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) m = 1 @inbounds for j = 1:nA A_jj = A[j,j] for k = 1:nB for l = 1:mB C[m] = A_jj * B[l,k] m += 1 end m += (nA - 1) * mB end m += mB end return C end @inline function kron!(C::AbstractMatrix, A::AbstractMatrix, B::Diagonal) require_one_based_indexing(A) (mA, nA) = size(A) (mB, nB) = size(B) (mC, nC) = size(C) @boundscheck (mC, nC) == (mA * mB, nA * nB) || throw(DimensionMismatch("expect C to be a $(mA * mB)x$(nA * nB) matrix, got size $(mC)x$(nC)")) isempty(A) || isempty(B) || fill!(C, zero(A[1,1] * B[1,1])) m = 1 @inbounds for j = 1:nA for l = 1:mB Bll = B[l,l] for k = 1:mA C[m] = A[k,j] * Bll m += nB end m += 1 end m -= nB end return C end conj(D::Diagonal) = Diagonal(conj(D.diag)) transpose(D::Diagonal{<:Number}) = D transpose(D::Diagonal) = Diagonal(transpose.(D.diag)) adjoint(D::Diagonal{<:Number}) = Diagonal(vec(adjoint(D.diag))) adjoint(D::Diagonal{<:Number,<:Base.ReshapedArray{<:Number,1,<:Adjoint}}) = Diagonal(adjoint(parent(D.diag))) adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) permutedims(D::Diagonal) = D permutedims(D::Diagonal, perm) = (Base.checkdims_perm(D, D, perm); D) function diag(D::Diagonal{T}, k::Integer=0) where T # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of k if k == 0 return copyto!(similar(D.diag, length(D.diag)), D.diag) elseif -size(D,1) <= k <= size(D,1) return fill!(similar(D.diag, size(D,1)-abs(k)), zero(T)) else throw(ArgumentError(string("requested diagonal, $k, must be at least $(-size(D, 1)) ", "and at most $(size(D, 2)) for an $(size(D, 1))-by-$(size(D, 2)) matrix"))) end end tr(D::Diagonal) = sum(tr, D.diag) det(D::Diagonal) = prod(det, D.diag) function logdet(D::Diagonal{<:Complex}) # make sure branch cut is correct z = sum(log, D.diag) complex(real(z), rem2pi(imag(z), RoundNearest)) end # Matrix functions for f in (:exp, :cis, :log, :sqrt, :cos, :sin, :tan, :csc, :sec, :cot, :cosh, :sinh, :tanh, :csch, :sech, :coth, :acos, :asin, :atan, :acsc, :asec, :acot, :acosh, :asinh, :atanh, :acsch, :asech, :acoth) @eval $f(D::Diagonal) = Diagonal($f.(D.diag)) end function inv(D::Diagonal{T}) where T Di = similar(D.diag, typeof(inv(oneunit(T)))) for i = 1:length(D.diag) if iszero(D.diag[i]) throw(SingularException(i)) end Di[i] = inv(D.diag[i]) end Diagonal(Di) end function pinv(D::Diagonal{T}) where T Di = similar(D.diag, typeof(inv(oneunit(T)))) for i = 1:length(D.diag) if !iszero(D.diag[i]) invD = inv(D.diag[i]) if isfinite(invD) Di[i] = invD continue end end # fallback Di[i] = zero(T) end Diagonal(Di) end function pinv(D::Diagonal{T}, tol::Real) where T Di = similar(D.diag, typeof(inv(oneunit(T)))) if !isempty(D.diag) maxabsD = maximum(abs, D.diag) for i = 1:length(D.diag) if abs(D.diag[i]) > tol*maxabsD invD = inv(D.diag[i]) if isfinite(invD) Di[i] = invD continue end end # fallback Di[i] = zero(T) end end Diagonal(Di) end #Eigensystem eigvals(D::Diagonal{<:Number}; permute::Bool=true, scale::Bool=true) = copy(D.diag) eigvals(D::Diagonal; permute::Bool=true, scale::Bool=true) = [eigvals(x) for x in D.diag] #For block matrices, etc. eigvecs(D::Diagonal) = Matrix{eltype(D)}(I, size(D)) function eigen(D::Diagonal; permute::Bool=true, scale::Bool=true, sortby::Union{Function,Nothing}=nothing) if any(!isfinite, D.diag) throw(ArgumentError("matrix contains Infs or NaNs")) end Td = Base.promote_op(/, eltype(D), eltype(D)) ฮป = eigvals(D) if !isnothing(sortby) p = sortperm(ฮป; alg=QuickSort, by=sortby) ฮป = ฮป[p] evecs = zeros(Td, size(D)) @inbounds for i in eachindex(p) evecs[p[i],i] = one(Td) end else evecs = Matrix{Td}(I, size(D)) end Eigen(ฮป, evecs) end function eigen(Da::Diagonal, Db::Diagonal; sortby::Union{Function,Nothing}=nothing) if any(!isfinite, Da.diag) || any(!isfinite, Db.diag) throw(ArgumentError("matrices contain Infs or NaNs")) end if any(iszero, Db.diag) throw(ArgumentError("right-hand side diagonal matrix is singular")) end return GeneralizedEigen(eigen(Db \ Da; sortby)...) end function eigen(A::AbstractMatrix, D::Diagonal; sortby::Union{Function,Nothing}=nothing) if any(iszero, D.diag) throw(ArgumentError("right-hand side diagonal matrix is singular")) end if size(A, 1) == size(A, 2) && isdiag(A) return eigen(Diagonal(A), D; sortby) elseif all(isposdef, D.diag) S = promote_type(eigtype(eltype(A)), eltype(D)) return eigen(A, cholesky(Diagonal{S}(D)); sortby) else return eigen!(D \ A; sortby) end end #Singular system svdvals(D::Diagonal{<:Number}) = sort!(abs.(D.diag), rev = true) svdvals(D::Diagonal) = [svdvals(v) for v in D.diag] function svd(D::Diagonal{T}) where {T<:Number} d = D.diag s = abs.(d) piv = sortperm(s, rev = true) S = s[piv] Td = typeof(oneunit(T)/oneunit(T)) U = zeros(Td, size(D)) Vt = copy(U) for i in 1:length(d) j = piv[i] U[j,i] = d[j] / S[i] Vt[i,j] = one(Td) end return SVD(U, S, Vt) end # disambiguation methods: * and / of Diagonal and Adj/Trans AbsVec *(u::AdjointAbsVec, D::Diagonal) = (D'u')' *(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) * transpose(u)) *(x::AdjointAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) *(x::TransposeAbsVec, D::Diagonal, y::AbstractVector) = _mapreduce_prod(*, x, D, y) /(u::AdjointAbsVec, D::Diagonal) = (D' \ u')' /(u::TransposeAbsVec, D::Diagonal) = transpose(transpose(D) \ transpose(u)) # disambiguation methods: Call unoptimized version for user defined AbstractTriangular. *(A::AbstractTriangular, D::Diagonal) = @invoke *(A::AbstractMatrix, D::Diagonal) *(D::Diagonal, A::AbstractTriangular) = @invoke *(D::Diagonal, A::AbstractMatrix) dot(x::AbstractVector, D::Diagonal, y::AbstractVector) = _mapreduce_prod(dot, x, D, y) dot(A::Diagonal, B::Diagonal) = dot(A.diag, B.diag) function dot(D::Diagonal, B::AbstractMatrix) size(D) == size(B) || throw(DimensionMismatch("Matrix sizes $(size(D)) and $(size(B)) differ")) return dot(D.diag, view(B, diagind(B))) end dot(A::AbstractMatrix, B::Diagonal) = conj(dot(B, A)) function _mapreduce_prod(f, x, D::Diagonal, y) if !(length(x) == length(D.diag) == length(y)) throw(DimensionMismatch("x has length $(length(x)), D has size $(size(D)), and y has $(length(y))")) end if isempty(x) && isempty(D) && isempty(y) return zero(promote_op(f, eltype(x), eltype(D), eltype(y))) else return mapreduce(t -> f(t[1], t[2], t[3]), +, zip(x, D.diag, y)) end end function cholesky!(A::Diagonal, ::NoPivot = NoPivot(); check::Bool = true) info = 0 for (i, di) in enumerate(A.diag) if isreal(di) && real(di) > 0 A.diag[i] = โˆšdi elseif check throw(PosDefException(i)) else info = i break end end Cholesky(A, 'U', convert(BlasInt, info)) end @deprecate cholesky!(A::Diagonal, ::Val{false}; check::Bool = true) cholesky!(A::Diagonal, NoPivot(); check) false @deprecate cholesky(A::Diagonal, ::Val{false}; check::Bool = true) cholesky(A::Diagonal, NoPivot(); check) false inv(C::Cholesky{<:Any,<:Diagonal}) = Diagonal(map(invโˆ˜abs2, C.factors.diag)) cholcopy(A::Diagonal) = copymutable_oftype(A, choltype(A)) cholcopy(A::RealHermSymComplexHerm{<:Any,<:Diagonal}) = Diagonal(copy_similar(diag(A), choltype(A))) function getproperty(C::Cholesky{<:Any,<:Diagonal}, d::Symbol) Cfactors = getfield(C, :factors) if d in (:U, :L, :UL) return Cfactors else return getfield(C, d) end end Base._sum(A::Diagonal, ::Colon) = sum(A.diag) function Base._sum(A::Diagonal, dims::Integer) res = Base.reducedim_initarray(A, dims, zero(eltype(A))) if dims <= 2 for i = 1:length(A.diag) @inbounds res[i] = A.diag[i] end else for i = 1:length(A.diag) @inbounds res[i,i] = A.diag[i] end end res end function logabsdet(A::Diagonal) mapreduce(x -> (log(abs(x)), sign(x)), ((d1, s1), (d2, s2)) -> (d1 + d2, s1 * s2), A.diag) end function Base.muladd(A::Diagonal, B::Diagonal, z::Diagonal) Diagonal(A.diag .* B.diag .+ z.diag) end /cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/symmetriceigen.jl…## This file is a part of Julia. License is MIT: https://julialang.org/license # preserve HermOrSym wrapper eigencopy_oftype(A::Hermitian, S) = Hermitian(copy_similar(A, S), sym_uplo(A.uplo)) eigencopy_oftype(A::Symmetric, S) = Symmetric(copy_similar(A, S), sym_uplo(A.uplo)) # Eigensolvers for symmetric and Hermitian matrices eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) = Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) function eigen(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) eigen!(eigencopy_oftype(A, S), sortby=sortby) end eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = Eigen(LAPACK.syevr!('V', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)...) """ eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> Eigen Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). The [`UnitRange`](@ref) `irange` specifies indices of the sorted eigenvalues to search for. !!! note If `irange` is not `1:n`, where `n` is the dimension of `A`, then the returned factorization will be a *truncated* factorization. """ function eigen(A::RealHermSymComplexHerm, irange::UnitRange) S = eigtype(eltype(A)) eigen!(eigencopy_oftype(A, S), irange) end eigen!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} = Eigen(LAPACK.syevr!('V', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)...) """ eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> Eigen Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) Iterating the decomposition produces the components `F.values` and `F.vectors`. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). `vl` is the lower bound of the window of eigenvalues to search for, and `vu` is the upper bound. !!! note If [`vl`, `vu`] does not contain all eigenvalues of `A`, then the returned factorization will be a *truncated* factorization. """ function eigen(A::RealHermSymComplexHerm, vl::Real, vh::Real) S = eigtype(eltype(A)) eigen!(eigencopy_oftype(A, S), vl, vh) end function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) vals = LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] !isnothing(sortby) && sort!(vals, by=sortby) return vals end function eigvals(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) eigvals!(eigencopy_oftype(A, S), sortby=sortby) end """ eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. `irange` is a range of eigenvalue *indices* to search for - for instance, the 2nd to 8th eigenvalues. """ eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = LAPACK.syevr!('N', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)[1] """ eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values Return the eigenvalues of `A`. It is possible to calculate only a subset of the eigenvalues by specifying a [`UnitRange`](@ref) `irange` covering indices of the sorted eigenvalues, e.g. the 2nd to 8th eigenvalues. # Examples ```jldoctest julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 1.0 2.0 โ‹… 2.0 2.0 3.0 โ‹… 3.0 1.0 julia> eigvals(A, 2:2) 1-element Vector{Float64}: 0.9999999999999996 julia> eigvals(A) 3-element Vector{Float64}: -2.1400549446402604 1.0000000000000002 5.140054944640259 ``` """ function eigvals(A::RealHermSymComplexHerm, irange::UnitRange) S = eigtype(eltype(A)) eigvals!(eigencopy_oftype(A, S), irange) end """ eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. `vl` is the lower bound of the interval to search for eigenvalues, and `vu` is the upper bound. """ eigvals!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} = LAPACK.syevr!('N', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)[1] """ eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values Return the eigenvalues of `A`. It is possible to calculate only a subset of the eigenvalues by specifying a pair `vl` and `vu` for the lower and upper boundaries of the eigenvalues. # Examples ```jldoctest julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 1.0 2.0 โ‹… 2.0 2.0 3.0 โ‹… 3.0 1.0 julia> eigvals(A, -1, 2) 1-element Vector{Float64}: 1.0000000000000009 julia> eigvals(A) 3-element Vector{Float64}: -2.1400549446402604 1.0000000000000002 5.140054944640259 ``` """ function eigvals(A::RealHermSymComplexHerm, vl::Real, vh::Real) S = eigtype(eltype(A)) eigvals!(eigencopy_oftype(A, S), vl, vh) end eigmax(A::RealHermSymComplexHerm{<:Real}) = eigvals(A, size(A, 1):size(A, 1))[1] eigmin(A::RealHermSymComplexHerm{<:Real}) = eigvals(A, 1:1)[1] function eigen(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} S = promote_type(eigtype(TA), TB) return eigen!(eigencopy_oftype(A, S), eigencopy_oftype(B, S); kws...) end function eigen!(A::HermOrSym{T,S}, B::HermOrSym{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasReal,S<:StridedMatrix} vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data')) GeneralizedEigen(sorteig!(vals, vecs, sortby)...) end function eigen!(A::Hermitian{T,S}, B::Hermitian{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasComplex,S<:StridedMatrix} vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data')) GeneralizedEigen(sorteig!(vals, vecs, sortby)...) end function eigen(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) if ishermitian(A) eigen!(eigencopy_oftype(Hermitian(A), eigtype(eltype(A))), C; sortby) else eigen!(copy_similar(A, eigtype(eltype(A))), C; sortby) end end function eigen!(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) # Cholesky decomposition based eigenvalues and eigenvectors vals, w = eigen!(UtiAUi!(A, C.U)) vecs = C.U \ w GeneralizedEigen(sorteig!(vals, vecs, sortby)...) end # Perform U' \ A / U in-place, where U::Union{UpperTriangular,Diagonal} UtiAUi!(A, U) = _UtiAUi!(A, U) UtiAUi!(A::Symmetric, U) = Symmetric(_UtiAUi!(copytri!(parent(A), A.uplo), U), sym_uplo(A.uplo)) UtiAUi!(A::Hermitian, U) = Hermitian(_UtiAUi!(copytri!(parent(A), A.uplo, true), U), sym_uplo(A.uplo)) _UtiAUi!(A, U) = rdiv!(ldiv!(U', A), U) function eigvals(A::HermOrSym{TA}, B::HermOrSym{TB}; kws...) where {TA,TB} S = promote_type(eigtype(TA), TB) return eigvals!(eigencopy_oftype(A, S), eigencopy_oftype(B, S); kws...) end function eigvals!(A::HermOrSym{T,S}, B::HermOrSym{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasReal,S<:StridedMatrix} vals = LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data'))[1] isnothing(sortby) || sort!(vals, by=sortby) return vals end function eigvals!(A::Hermitian{T,S}, B::Hermitian{T,S}; sortby::Union{Function,Nothing}=nothing) where {T<:BlasComplex,S<:StridedMatrix} vals = LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : copy(B.data'))[1] isnothing(sortby) || sort!(vals, by=sortby) return vals end eigvecs(A::HermOrSym) = eigvecs(eigen(A)) function eigvals(A::AbstractMatrix, C::Cholesky; sortby::Union{Function,Nothing}=nothing) if ishermitian(A) eigvals!(eigencopy_oftype(Hermitian(A), eigtype(eltype(A))), C; sortby) else eigvals!(copy_similar(A, eigtype(eltype(A))), C; sortby) end end function eigvals!(A::AbstractMatrix{T}, C::Cholesky{T, <:AbstractMatrix}; sortby::Union{Function,Nothing}=nothing) where {T<:Number} # Cholesky decomposition based eigenvalues return eigvals!(UtiAUi!(A, C.U); sortby) end w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/bidiag.jlŸ‚# This file is a part of Julia. License is MIT: https://julialang.org/license # Bidiagonal matrices struct Bidiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} dv::V # diagonal ev::V # sub/super diagonal uplo::Char # upper bidiagonal ('U') or lower ('L') function Bidiagonal{T,V}(dv, ev, uplo::AbstractChar) where {T,V<:AbstractVector{T}} require_one_based_indexing(dv, ev) if length(ev) != max(length(dv)-1, 0) throw(DimensionMismatch("length of diagonal vector is $(length(dv)), length of off-diagonal vector is $(length(ev))")) end (uplo != 'U' && uplo != 'L') && throw_uplo() new{T,V}(dv, ev, uplo) end end function Bidiagonal{T,V}(dv, ev, uplo::Symbol) where {T,V<:AbstractVector{T}} Bidiagonal{T,V}(dv, ev, char_uplo(uplo)) end function Bidiagonal{T}(dv::AbstractVector, ev::AbstractVector, uplo::Union{Symbol,AbstractChar}) where {T} Bidiagonal(convert(AbstractVector{T}, dv)::AbstractVector{T}, convert(AbstractVector{T}, ev)::AbstractVector{T}, uplo) end function Bidiagonal{T,V}(A::Bidiagonal) where {T,V<:AbstractVector{T}} Bidiagonal{T,V}(A.dv, A.ev, A.uplo) end """ Bidiagonal(dv::V, ev::V, uplo::Symbol) where V <: AbstractVector Constructs an upper (`uplo=:U`) or lower (`uplo=:L`) bidiagonal matrix using the given diagonal (`dv`) and off-diagonal (`ev`) vectors. The result is of type `Bidiagonal` and provides efficient specialized linear solvers, but may be converted into a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). The length of `ev` must be one less than the length of `dv`. # Examples ```jldoctest julia> dv = [1, 2, 3, 4] 4-element Vector{Int64}: 1 2 3 4 julia> ev = [7, 8, 9] 3-element Vector{Int64}: 7 8 9 julia> Bu = Bidiagonal(dv, ev, :U) # ev is on the first superdiagonal 4ร—4 Bidiagonal{Int64, Vector{Int64}}: 1 7 โ‹… โ‹… โ‹… 2 8 โ‹… โ‹… โ‹… 3 9 โ‹… โ‹… โ‹… 4 julia> Bl = Bidiagonal(dv, ev, :L) # ev is on the first subdiagonal 4ร—4 Bidiagonal{Int64, Vector{Int64}}: 1 โ‹… โ‹… โ‹… 7 2 โ‹… โ‹… โ‹… 8 3 โ‹… โ‹… โ‹… 9 4 ``` """ function Bidiagonal(dv::V, ev::V, uplo::Symbol) where {T,V<:AbstractVector{T}} Bidiagonal{T,V}(dv, ev, uplo) end function Bidiagonal(dv::V, ev::V, uplo::AbstractChar) where {T,V<:AbstractVector{T}} Bidiagonal{T,V}(dv, ev, uplo) end #To allow Bidiagonal's where the "dv" is Vector{T} and "ev" Vector{S}, #where T and S can be promoted function Bidiagonal(dv::Vector{T}, ev::Vector{S}, uplo::Symbol) where {T,S} TS = promote_type(T,S) return Bidiagonal{TS,Vector{TS}}(dv, ev, uplo) end """ Bidiagonal(A, uplo::Symbol) Construct a `Bidiagonal` matrix from the main diagonal of `A` and its first super- (if `uplo=:U`) or sub-diagonal (if `uplo=:L`). # Examples ```jldoctest julia> A = [1 1 1 1; 2 2 2 2; 3 3 3 3; 4 4 4 4] 4ร—4 Matrix{Int64}: 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 julia> Bidiagonal(A, :U) # contains the main diagonal and first superdiagonal of A 4ร—4 Bidiagonal{Int64, Vector{Int64}}: 1 1 โ‹… โ‹… โ‹… 2 2 โ‹… โ‹… โ‹… 3 3 โ‹… โ‹… โ‹… 4 julia> Bidiagonal(A, :L) # contains the main diagonal and first subdiagonal of A 4ร—4 Bidiagonal{Int64, Vector{Int64}}: 1 โ‹… โ‹… โ‹… 2 2 โ‹… โ‹… โ‹… 3 3 โ‹… โ‹… โ‹… 4 4 ``` """ function Bidiagonal(A::AbstractMatrix, uplo::Symbol) Bidiagonal(diag(A, 0), diag(A, uplo === :U ? 1 : -1), uplo) end Bidiagonal(A::Bidiagonal) = A Bidiagonal{T}(A::Bidiagonal{T}) where {T} = A Bidiagonal{T}(A::Bidiagonal) where {T} = Bidiagonal{T}(A.dv, A.ev, A.uplo) bidiagzero(::Bidiagonal{T}, i, j) where {T} = zero(T) function bidiagzero(A::Bidiagonal{<:AbstractMatrix}, i, j) Tel = eltype(eltype(A.dv)) if i < j && A.uplo == 'U' #= top right zeros =# return zeros(Tel, size(A.ev[i], 1), size(A.ev[j-1], 2)) elseif j < i && A.uplo == 'L' #= bottom left zeros =# return zeros(Tel, size(A.ev[i-1], 1), size(A.ev[j], 2)) else return zeros(Tel, size(A.dv[i], 1), size(A.dv[j], 2)) end end @inline function Base.isassigned(A::Bidiagonal, i::Int, j::Int) @boundscheck checkbounds(Bool, A, i, j) || return false if i == j return @inbounds isassigned(A.dv, i) elseif A.uplo == 'U' && (i == j - 1) return @inbounds isassigned(A.ev, i) elseif A.uplo == 'L' && (i == j + 1) return @inbounds isassigned(A.ev, j) else return true end end @inline function getindex(A::Bidiagonal{T}, i::Integer, j::Integer) where T @boundscheck checkbounds(A, i, j) if i == j return @inbounds A.dv[i] elseif A.uplo == 'U' && (i == j - 1) return @inbounds A.ev[i] elseif A.uplo == 'L' && (i == j + 1) return @inbounds A.ev[j] else return bidiagzero(A, i, j) end end @inline function setindex!(A::Bidiagonal, x, i::Integer, j::Integer) @boundscheck checkbounds(A, i, j) if i == j @inbounds A.dv[i] = x elseif A.uplo == 'U' && (i == j - 1) @inbounds A.ev[i] = x elseif A.uplo == 'L' && (i == j + 1) @inbounds A.ev[j] = x elseif !iszero(x) throw(ArgumentError(string("cannot set entry ($i, $j) off the ", "$(istriu(A) ? "upper" : "lower") bidiagonal band to a nonzero value ($x)"))) end return x end ## structured matrix methods ## function Base.replace_in_print_matrix(A::Bidiagonal,i::Integer,j::Integer,s::AbstractString) if A.uplo == 'U' i==j || i==j-1 ? s : Base.replace_with_centered_mark(s) else i==j || i==j+1 ? s : Base.replace_with_centered_mark(s) end end #Converting from Bidiagonal to dense Matrix function Matrix{T}(A::Bidiagonal) where T n = size(A, 1) B = Matrix{T}(undef, n, n) n == 0 && return B n > 1 && fill!(B, zero(T)) @inbounds for i = 1:n - 1 B[i,i] = A.dv[i] if A.uplo == 'U' B[i,i+1] = A.ev[i] else B[i+1,i] = A.ev[i] end end B[n,n] = A.dv[n] return B end Matrix(A::Bidiagonal{T}) where {T} = Matrix{promote_type(T, typeof(zero(T)))}(A) Array(A::Bidiagonal) = Matrix(A) promote_rule(::Type{Matrix{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = @isdefined(T) && @isdefined(S) ? Matrix{promote_type(T,S)} : Matrix promote_rule(::Type{Matrix}, ::Type{<:Bidiagonal}) = Matrix #Converting from Bidiagonal to Tridiagonal function Tridiagonal{T}(A::Bidiagonal) where T dv = convert(AbstractVector{T}, A.dv) ev = convert(AbstractVector{T}, A.ev) z = fill!(similar(ev), zero(T)) A.uplo == 'U' ? Tridiagonal(z, dv, ev) : Tridiagonal(ev, dv, z) end promote_rule(::Type{<:Tridiagonal{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = @isdefined(T) && @isdefined(S) ? Tridiagonal{promote_type(T,S)} : Tridiagonal promote_rule(::Type{<:Tridiagonal}, ::Type{<:Bidiagonal}) = Tridiagonal # When asked to convert Bidiagonal to AbstractMatrix{T}, preserve structure by converting to Bidiagonal{T} <: AbstractMatrix{T} AbstractMatrix{T}(A::Bidiagonal) where {T} = convert(Bidiagonal{T}, A) convert(::Type{T}, m::AbstractMatrix) where {T<:Bidiagonal} = m isa T ? m : T(m)::T similar(B::Bidiagonal, ::Type{T}) where {T} = Bidiagonal(similar(B.dv, T), similar(B.ev, T), B.uplo) similar(B::Bidiagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = similar(B.dv, T, dims) tr(B::Bidiagonal) = sum(B.dv) function kron(A::Diagonal, B::Bidiagonal) # `_droplast!` is only guaranteed to work with `Vector` kdv = _makevector(kron(diag(A), B.dv)) kev = _droplast!(_makevector(kron(diag(A), _pushzero(B.ev)))) Bidiagonal(kdv, kev, B.uplo) end ################### # LAPACK routines # ################### #Singular values svdvals!(M::Bidiagonal{<:BlasReal}) = LAPACK.bdsdc!(M.uplo, 'N', M.dv, M.ev)[1] function svd!(M::Bidiagonal{<:BlasReal}; full::Bool = false) d, e, U, Vt, Q, iQ = LAPACK.bdsdc!(M.uplo, 'I', M.dv, M.ev) SVD(U, d, Vt) end function svd(M::Bidiagonal; kw...) svd!(copy(M), kw...) end #################### # Generic routines # #################### function show(io::IO, M::Bidiagonal) # TODO: make this readable and one-line summary(io, M) print(io, ":\n diag:") print_matrix(io, (M.dv)') print(io, M.uplo == 'U' ? "\n super:" : "\n sub:") print_matrix(io, (M.ev)') end size(M::Bidiagonal) = (length(M.dv), length(M.dv)) function size(M::Bidiagonal, d::Integer) if d < 1 throw(ArgumentError("dimension must be โ‰ฅ 1, got $d")) elseif d <= 2 return length(M.dv) else return 1 end end #Elementary operations for func in (:conj, :copy, :real, :imag) @eval ($func)(M::Bidiagonal) = Bidiagonal(($func)(M.dv), ($func)(M.ev), M.uplo) end adjoint(B::Bidiagonal) = Adjoint(B) transpose(B::Bidiagonal) = Transpose(B) adjoint(B::Bidiagonal{<:Number}) = Bidiagonal(conj(B.dv), conj(B.ev), B.uplo == 'U' ? :L : :U) transpose(B::Bidiagonal{<:Number}) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? :L : :U) permutedims(B::Bidiagonal) = Bidiagonal(B.dv, B.ev, B.uplo == 'U' ? 'L' : 'U') function permutedims(B::Bidiagonal, perm) Base.checkdims_perm(B, B, perm) NTuple{2}(perm) == (2, 1) ? permutedims(B) : B end function Base.copy(aB::Adjoint{<:Any,<:Bidiagonal}) B = aB.parent return Bidiagonal(map(x -> copy.(adjoint.(x)), (B.dv, B.ev))..., B.uplo == 'U' ? :L : :U) end function Base.copy(tB::Transpose{<:Any,<:Bidiagonal}) B = tB.parent return Bidiagonal(map(x -> copy.(transpose.(x)), (B.dv, B.ev))..., B.uplo == 'U' ? :L : :U) end iszero(M::Bidiagonal) = iszero(M.dv) && iszero(M.ev) isone(M::Bidiagonal) = all(isone, M.dv) && iszero(M.ev) function istriu(M::Bidiagonal, k::Integer=0) if M.uplo == 'U' if k <= 0 return true elseif k == 1 return iszero(M.dv) else # k >= 2 return iszero(M.dv) && iszero(M.ev) end else # M.uplo == 'L' if k <= -1 return true elseif k == 0 return iszero(M.ev) else # k >= 1 return iszero(M.ev) && iszero(M.dv) end end end function istril(M::Bidiagonal, k::Integer=0) if M.uplo == 'U' if k >= 1 return true elseif k == 0 return iszero(M.ev) else # k <= -1 return iszero(M.ev) && iszero(M.dv) end else # M.uplo == 'L' if k >= 0 return true elseif k == -1 return iszero(M.dv) else # k <= -2 return iszero(M.dv) && iszero(M.ev) end end end isdiag(M::Bidiagonal) = iszero(M.ev) function tril!(M::Bidiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n - 1 <= k <= n - 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif M.uplo == 'U' && k < 0 fill!(M.dv, zero(T)) fill!(M.ev, zero(T)) elseif k < -1 fill!(M.dv, zero(T)) fill!(M.ev, zero(T)) elseif M.uplo == 'U' && k == 0 fill!(M.ev, zero(T)) elseif M.uplo == 'L' && k == -1 fill!(M.dv, zero(T)) end return M end function triu!(M::Bidiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n + 1 <= k <= n + 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least", "$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif M.uplo == 'L' && k > 0 fill!(M.dv, zero(T)) fill!(M.ev, zero(T)) elseif k > 1 fill!(M.dv, zero(T)) fill!(M.ev, zero(T)) elseif M.uplo == 'L' && k == 0 fill!(M.ev, zero(T)) elseif M.uplo == 'U' && k == 1 fill!(M.dv, zero(T)) end return M end function diag(M::Bidiagonal{T}, n::Integer=0) where T # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n if n == 0 return copyto!(similar(M.dv, length(M.dv)), M.dv) elseif (n == 1 && M.uplo == 'U') || (n == -1 && M.uplo == 'L') return copyto!(similar(M.ev, length(M.ev)), M.ev) elseif -size(M,1) <= n <= size(M,1) return fill!(similar(M.dv, size(M,1)-abs(n)), zero(T)) else throw(ArgumentError(string("requested diagonal, $n, must be at least $(-size(M, 1)) ", "and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end function +(A::Bidiagonal, B::Bidiagonal) if A.uplo == B.uplo || length(A.dv) == 0 Bidiagonal(A.dv+B.dv, A.ev+B.ev, A.uplo) else newdv = A.dv+B.dv Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(B.ev), newdv, typeof(newdv)(A.ev)) : (typeof(newdv)(A.ev), newdv, typeof(newdv)(B.ev)))...) end end function -(A::Bidiagonal, B::Bidiagonal) if A.uplo == B.uplo || length(A.dv) == 0 Bidiagonal(A.dv-B.dv, A.ev-B.ev, A.uplo) else newdv = A.dv-B.dv Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-B.ev), newdv, typeof(newdv)(A.ev)) : (typeof(newdv)(A.ev), newdv, typeof(newdv)(-B.ev)))...) end end -(A::Bidiagonal)=Bidiagonal(-A.dv,-A.ev,A.uplo) *(A::Bidiagonal, B::Number) = Bidiagonal(A.dv*B, A.ev*B, A.uplo) *(B::Number, A::Bidiagonal) = Bidiagonal(B*A.dv, B*A.ev, A.uplo) /(A::Bidiagonal, B::Number) = Bidiagonal(A.dv/B, A.ev/B, A.uplo) \(B::Number, A::Bidiagonal) = Bidiagonal(B\A.dv, B\A.ev, A.uplo) function ==(A::Bidiagonal, B::Bidiagonal) if A.uplo == B.uplo return A.dv == B.dv && A.ev == B.ev else return iszero(A.ev) && iszero(B.ev) && A.dv == B.dv end end const BandedMatrix = Union{Bidiagonal,Diagonal,Tridiagonal,SymTridiagonal} # or BiDiTriSym const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal} const BiTri = Union{Bidiagonal,Tridiagonal} @inline mul!(C::AbstractVector, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractVector, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::AbstractMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) @inline mul!(C::AbstractMatrix, A::BandedMatrix, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) function check_A_mul_B!_sizes(C, A, B) mA, nA = size(A) mB, nB = size(B) mC, nC = size(C) if mA != mC throw(DimensionMismatch("first dimension of A, $mA, and first dimension of output C, $mC, must match")) elseif nA != mB throw(DimensionMismatch("second dimension of A, $nA, and first dimension of B, $mB, must match")) elseif nB != nC throw(DimensionMismatch("second dimension of output C, $nC, and second dimension of B, $nB, must match")) end end # function to get the internally stored vectors for Bidiagonal and [Sym]Tridiagonal # to avoid allocations in _mul! below (#24324, #24578) _diag(A::Tridiagonal, k) = k == -1 ? A.dl : k == 0 ? A.d : A.du _diag(A::SymTridiagonal, k) = k == 0 ? A.dv : A.ev function _diag(A::Bidiagonal, k) if k == 0 return A.dv elseif (A.uplo == 'L' && k == -1) || (A.uplo == 'U' && k == 1) return A.ev else return diag(A, k) end end function _mul!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym, _add::MulAddMul = MulAddMul()) check_A_mul_B!_sizes(C, A, B) n = size(A,1) n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) # We use `_rmul_or_fill!` instead of `_modify!` here since using # `_modify!` in the following loop will not update the # off-diagonal elements for non-zero beta. _rmul_or_fill!(C, _add.beta) iszero(_add.alpha) && return C Al = _diag(A, -1) Ad = _diag(A, 0) Au = _diag(A, 1) Bl = _diag(B, -1) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin # first row of C C[1,1] += _add(A[1,1]*B[1,1] + A[1, 2]*B[2, 1]) C[1,2] += _add(A[1,1]*B[1,2] + A[1,2]*B[2,2]) C[1,3] += _add(A[1,2]*B[2,3]) # second row of C C[2,1] += _add(A[2,1]*B[1,1] + A[2,2]*B[2,1]) C[2,2] += _add(A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]) C[2,3] += _add(A[2,2]*B[2,3] + A[2,3]*B[3,3]) C[2,4] += _add(A[2,3]*B[3,4]) for j in 3:n-2 Ajjโ‚‹1 = Al[j-1] Ajj = Ad[j] Ajjโ‚Š1 = Au[j] Bjโ‚‹1jโ‚‹2 = Bl[j-2] Bjโ‚‹1jโ‚‹1 = Bd[j-1] Bjโ‚‹1j = Bu[j-1] Bjjโ‚‹1 = Bl[j-1] Bjj = Bd[j] Bjjโ‚Š1 = Bu[j] Bjโ‚Š1j = Bl[j] Bjโ‚Š1jโ‚Š1 = Bd[j+1] Bjโ‚Š1jโ‚Š2 = Bu[j+1] C[j,j-2] += _add( Ajjโ‚‹1*Bjโ‚‹1jโ‚‹2) C[j, j-1] += _add(Ajjโ‚‹1*Bjโ‚‹1jโ‚‹1 + Ajj*Bjjโ‚‹1) C[j, j ] += _add(Ajjโ‚‹1*Bjโ‚‹1j + Ajj*Bjj + Ajjโ‚Š1*Bjโ‚Š1j) C[j, j+1] += _add(Ajj *Bjjโ‚Š1 + Ajjโ‚Š1*Bjโ‚Š1jโ‚Š1) C[j, j+2] += _add(Ajjโ‚Š1*Bjโ‚Š1jโ‚Š2) end # row before last of C C[n-1,n-3] += _add(A[n-1,n-2]*B[n-2,n-3]) C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2] + A[n-1,n-2]*B[n-2,n-2]) C[n-1,n-1] += _add(A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]) C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ] + A[n-1, n]*B[n ,n ]) # last row of C C[n,n-2] += _add(A[n,n-1]*B[n-1,n-2]) C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]) C[n,n ] += _add(A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]) end # inbounds C end function _mul!(C::AbstractMatrix, A::BiTriSym, B::Diagonal, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C) check_A_mul_B!_sizes(C, A, B) n = size(A,1) iszero(n) && return C n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C Al = _diag(A, -1) Ad = _diag(A, 0) Au = _diag(A, 1) Bd = B.diag @inbounds begin # first row of C C[1,1] += _add(A[1,1]*B[1,1]) C[1,2] += _add(A[1,2]*B[2,2]) # second row of C C[2,1] += _add(A[2,1]*B[1,1]) C[2,2] += _add(A[2,2]*B[2,2]) C[2,3] += _add(A[2,3]*B[3,3]) for j in 3:n-2 C[j, j-1] += _add(Al[j-1]*Bd[j-1]) C[j, j ] += _add(Ad[j ]*Bd[j ]) C[j, j+1] += _add(Au[j ]*Bd[j+1]) end # row before last of C C[n-1,n-2] += _add(A[n-1,n-2]*B[n-2,n-2]) C[n-1,n-1] += _add(A[n-1,n-1]*B[n-1,n-1]) C[n-1,n ] += _add(A[n-1, n]*B[n ,n ]) # last row of C C[n,n-1] += _add(A[n,n-1]*B[n-1,n-1]) C[n,n ] += _add(A[n,n ]*B[n, n ]) end # inbounds C end function _mul!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, B) nA = size(A,1) nB = size(B,2) if !(size(C,1) == size(B,1) == nA) throw(DimensionMismatch("A has first dimension $nA, B has $(size(B,1)), C has $(size(C,1)) but all must match")) end if size(C,2) != nB throw(DimensionMismatch("A has second dimension $nA, B has $(size(B,2)), C has $(size(C,2)) but all must match")) end iszero(nA) && return C iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) nA <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) l = _diag(A, -1) d = _diag(A, 0) u = _diag(A, 1) @inbounds begin for j = 1:nB bโ‚€, bโ‚Š = B[1, j], B[2, j] _modify!(_add, d[1]*bโ‚€ + u[1]*bโ‚Š, C, (1, j)) for i = 2:nA - 1 bโ‚‹, bโ‚€, bโ‚Š = bโ‚€, bโ‚Š, B[i + 1, j] _modify!(_add, l[i - 1]*bโ‚‹ + d[i]*bโ‚€ + u[i]*bโ‚Š, C, (i, j)) end _modify!(_add, l[nA - 1]*bโ‚€ + d[nA]*bโ‚Š, C, (nA, j)) end end C end function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C, A) check_A_mul_B!_sizes(C, A, B) iszero(_add.alpha) && return _rmul_or_fill!(C, _add.beta) n = size(A,1) m = size(B,2) if n <= 3 || m <= 1 return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) end Bl = _diag(B, -1) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin # first and last column of C B11 = Bd[1] B21 = Bl[1] Bmm = Bd[m] Bmโ‚‹1m = Bu[m-1] for i in 1:n _modify!(_add, A[i,1] * B11 + A[i, 2] * B21, C, (i, 1)) _modify!(_add, A[i, m-1] * Bmโ‚‹1m + A[i, m] * Bmm, C, (i, m)) end # middle columns of C for j = 2:m-1 Bjโ‚‹1j = Bu[j-1] Bjj = Bd[j] Bjโ‚Š1j = Bl[j] for i = 1:n _modify!(_add, A[i, j-1] * Bjโ‚‹1j + A[i, j]*Bjj + A[i, j+1] * Bjโ‚Š1j, C, (i, j)) end end end # inbounds C end function _mul!(C::AbstractMatrix, A::Diagonal, B::BiTriSym, _add::MulAddMul = MulAddMul()) require_one_based_indexing(C) check_A_mul_B!_sizes(C, A, B) n = size(A,1) n <= 3 && return mul!(C, Array(A), Array(B), _add.alpha, _add.beta) _rmul_or_fill!(C, _add.beta) # see the same use above iszero(_add.alpha) && return C Ad = A.diag Bl = _diag(B, -1) Bd = _diag(B, 0) Bu = _diag(B, 1) @inbounds begin # first row of C C[1,1] += _add(A[1,1]*B[1,1]) C[1,2] += _add(A[1,1]*B[1,2]) # second row of C C[2,1] += _add(A[2,2]*B[2,1]) C[2,2] += _add(A[2,2]*B[2,2]) C[2,3] += _add(A[2,2]*B[2,3]) for j in 3:n-2 Ajj = Ad[j] C[j, j-1] += _add(Ajj*Bl[j-1]) C[j, j ] += _add(Ajj*Bd[j]) C[j, j+1] += _add(Ajj*Bu[j]) end # row before last of C C[n-1,n-2] += _add(A[n-1,n-1]*B[n-1,n-2]) C[n-1,n-1] += _add(A[n-1,n-1]*B[n-1,n-1]) C[n-1,n ] += _add(A[n-1,n-1]*B[n-1,n ]) # last row of C C[n,n-1] += _add(A[n,n]*B[n,n-1]) C[n,n ] += _add(A[n,n]*B[n,n ]) end # inbounds C end function *(A::UpperOrUnitUpperTriangular, B::Bidiagonal) TS = promote_op(matprod, eltype(A), eltype(B)) C = mul!(similar(A, TS, size(A)), A, B) return B.uplo == 'U' ? UpperTriangular(C) : C end function *(A::LowerOrUnitLowerTriangular, B::Bidiagonal) TS = promote_op(matprod, eltype(A), eltype(B)) C = mul!(similar(A, TS, size(A)), A, B) return B.uplo == 'L' ? LowerTriangular(C) : C end function *(A::Bidiagonal, B::UpperOrUnitUpperTriangular) TS = promote_op(matprod, eltype(A), eltype(B)) C = mul!(similar(B, TS, size(B)), A, B) return A.uplo == 'U' ? UpperTriangular(C) : C end function *(A::Bidiagonal, B::LowerOrUnitLowerTriangular) TS = promote_op(matprod, eltype(A), eltype(B)) C = mul!(similar(B, TS, size(B)), A, B) return A.uplo == 'L' ? LowerTriangular(C) : C end function *(A::Diagonal, B::SymTridiagonal) TS = promote_op(*, eltype(A), eltype(B)) out = Tridiagonal(similar(A, TS, size(A, 1)-1), similar(A, TS, size(A, 1)), similar(A, TS, size(A, 1)-1)) mul!(out, A, B) end function *(A::SymTridiagonal, B::Diagonal) TS = promote_op(*, eltype(A), eltype(B)) out = Tridiagonal(similar(A, TS, size(A, 1)-1), similar(A, TS, size(A, 1)), similar(A, TS, size(A, 1)-1)) mul!(out, A, B) end function dot(x::AbstractVector, B::Bidiagonal, y::AbstractVector) require_one_based_indexing(x, y) nx, ny = length(x), length(y) (nx == size(B, 1) == ny) || throw(DimensionMismatch()) if nx โ‰ค 1 nx == 0 && return dot(zero(eltype(x)), zero(eltype(B)), zero(eltype(y))) return dot(x[1], B.dv[1], y[1]) end ev, dv = B.ev, B.dv @inbounds if B.uplo == 'U' xโ‚€ = x[1] r = dot(x[1], dv[1], y[1]) for j in 2:nx-1 xโ‚‹, xโ‚€ = xโ‚€, x[j] r += dot(adjoint(ev[j-1])*xโ‚‹ + adjoint(dv[j])*xโ‚€, y[j]) end r += dot(adjoint(ev[nx-1])*xโ‚€ + adjoint(dv[nx])*x[nx], y[nx]) return r else # B.uplo == 'L' xโ‚€ = x[1] xโ‚Š = x[2] r = dot(adjoint(dv[1])*xโ‚€ + adjoint(ev[1])*xโ‚Š, y[1]) for j in 2:nx-1 xโ‚€, xโ‚Š = xโ‚Š, x[j+1] r += dot(adjoint(dv[j])*xโ‚€ + adjoint(ev[j])*xโ‚Š, y[j]) end r += dot(xโ‚Š, dv[nx], y[nx]) return r end end #Linear solvers #Generic solver using naive substitution ldiv!(A::Bidiagonal, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) function ldiv!(c::AbstractVecOrMat, A::Bidiagonal, b::AbstractVecOrMat) require_one_based_indexing(c, A, b) N = size(A, 2) mb, nb = size(b, 1), size(b, 2) if N != mb throw(DimensionMismatch("second dimension of A, $N, does not match first dimension of b, $mb")) end mc, nc = size(c, 1), size(c, 2) if mc != mb || nc != nb throw(DimensionMismatch("size of result, ($mc, $nc), does not match the size of b, ($mb, $nb)")) end if N == 0 return copyto!(c, b) end zi = findfirst(iszero, A.dv) isnothing(zi) || throw(SingularException(zi)) @inbounds for j in 1:nb if A.uplo == 'L' #do colwise forward substitution c[1,j] = bi1 = A.dv[1] \ b[1,j] for i in 2:N c[i,j] = bi1 = A.dv[i] \ (b[i,j] - A.ev[i - 1] * bi1) end else #do colwise backward substitution c[N,j] = bi1 = A.dv[N] \ b[N,j] for i in (N - 1):-1:1 c[i,j] = bi1 = A.dv[i] \ (b[i,j] - A.ev[i] * bi1) end end end return c end ldiv!(A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = @inline ldiv!(b, A, b) ldiv!(c::AbstractVecOrMat, A::AdjOrTrans{<:Any,<:Bidiagonal}, b::AbstractVecOrMat) = (t = wrapperop(A); _rdiv!(t(c), t(b), t(A)); return c) ### Generic promotion methods and fallbacks \(A::Bidiagonal, B::AbstractVecOrMat) = ldiv!(_initarray(\, eltype(A), eltype(B), B), A, B) \(xA::AdjOrTrans{<:Any,<:Bidiagonal}, B::AbstractVecOrMat) = copy(xA) \ B ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function \(B::Bidiagonal, U::$tri) A = ldiv!(_initarray(\, eltype(B), eltype(U), U), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function \(U::$tri, B::Bidiagonal) A = ldiv!(_initarray(\, eltype(U), eltype(B), U), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function \(B::Bidiagonal, L::$tri) A = ldiv!(_initarray(\, eltype(B), eltype(L), L), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function \(L::$tri, B::Bidiagonal) A = ldiv!(_initarray(\, eltype(L), eltype(B), L), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end end ### Diagonal specialization function \(B::Bidiagonal, D::Diagonal) A = ldiv!(_initarray(\, eltype(B), eltype(D), D), B, D) return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) end function _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::Bidiagonal) require_one_based_indexing(C, A, B) m, n = size(A) if size(B, 1) != n throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))")) end mc, nc = size(C) if mc != m || nc != n throw(DimensionMismatch("expect output to have size ($m, $n), but got ($mc, $nc)")) end zi = findfirst(iszero, B.dv) isnothing(zi) || throw(SingularException(zi)) if B.uplo == 'L' diagB = B.dv[n] for i in 1:m C[i,n] = A[i,n] / diagB end for j in n-1:-1:1 diagB = B.dv[j] offdiagB = B.ev[j] for i in 1:m C[i,j] = (A[i,j] - C[i,j+1]*offdiagB)/diagB end end else diagB = B.dv[1] for i in 1:m C[i,1] = A[i,1] / diagB end for j in 2:n diagB = B.dv[j] offdiagB = B.ev[j-1] for i = 1:m C[i,j] = (A[i,j] - C[i,j-1]*offdiagB)/diagB end end end C end rdiv!(A::AbstractMatrix, B::Bidiagonal) = @inline _rdiv!(A, A, B) rdiv!(A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = @inline _rdiv!(A, A, B) _rdiv!(C::AbstractMatrix, A::AbstractMatrix, B::AdjOrTrans{<:Any,<:Bidiagonal}) = (t = wrapperop(B); ldiv!(t(C), t(B), t(A)); return C) /(A::AbstractMatrix, B::Bidiagonal) = _rdiv!(_initarray(/, eltype(A), eltype(B), A), A, B) ### Triangular specializations for tri in (:UpperTriangular, :UnitUpperTriangular) @eval function /(U::$tri, B::Bidiagonal) A = _rdiv!(_initarray(/, eltype(U), eltype(B), U), U, B) return B.uplo == 'U' ? UpperTriangular(A) : A end @eval function /(B::Bidiagonal, U::$tri) A = _rdiv!(_initarray(/, eltype(B), eltype(U), U), B, U) return B.uplo == 'U' ? UpperTriangular(A) : A end end for tri in (:LowerTriangular, :UnitLowerTriangular) @eval function /(L::$tri, B::Bidiagonal) A = _rdiv!(_initarray(/, eltype(L), eltype(B), L), L, B) return B.uplo == 'L' ? LowerTriangular(A) : A end @eval function /(B::Bidiagonal, L::$tri) A = _rdiv!(_initarray(/, eltype(B), eltype(L), L), B, L) return B.uplo == 'L' ? LowerTriangular(A) : A end end ### Diagonal specialization function /(D::Diagonal, B::Bidiagonal) A = _rdiv!(_initarray(/, eltype(D), eltype(B), D), D, B) return B.uplo == 'U' ? UpperTriangular(A) : LowerTriangular(A) end /(A::AbstractMatrix, B::Transpose{<:Any,<:Bidiagonal}) = A / copy(B) /(A::AbstractMatrix, B::Adjoint{<:Any,<:Bidiagonal}) = A / copy(B) # disambiguation /(A::AdjointAbsVec, B::Bidiagonal) = adjoint(adjoint(B) \ parent(A)) /(A::TransposeAbsVec, B::Bidiagonal) = transpose(transpose(B) \ parent(A)) /(A::AdjointAbsVec, B::Transpose{<:Any,<:Bidiagonal}) = adjoint(adjoint(B) \ parent(A)) /(A::TransposeAbsVec, B::Transpose{<:Any,<:Bidiagonal}) = transpose(transpose(B) \ parent(A)) /(A::AdjointAbsVec, B::Adjoint{<:Any,<:Bidiagonal}) = adjoint(adjoint(B) \ parent(A)) /(A::TransposeAbsVec, B::Adjoint{<:Any,<:Bidiagonal}) = transpose(transpose(B) \ parent(A)) factorize(A::Bidiagonal) = A function inv(B::Bidiagonal{T}) where T n = size(B, 1) dest = zeros(typeof(inv(oneunit(T))), (n, n)) ldiv!(dest, B, Diagonal{typeof(one(T)/one(T))}(I, n)) return B.uplo == 'U' ? UpperTriangular(dest) : LowerTriangular(dest) end # Eigensystems eigvals(M::Bidiagonal) = copy(M.dv) function eigvecs(M::Bidiagonal{T}) where T n = length(M.dv) Q = Matrix{T}(undef, n,n) blks = [0; findall(iszero, M.ev); n] v = zeros(T, n) if M.uplo == 'U' for idx_block = 1:length(blks) - 1, i = blks[idx_block] + 1:blks[idx_block + 1] #index of eigenvector fill!(v, zero(T)) v[blks[idx_block] + 1] = one(T) for j = blks[idx_block] + 1:i - 1 #Starting from j=i, eigenvector elements will be 0 v[j+1] = (M.dv[i] - M.dv[j])/M.ev[j] * v[j] end c = norm(v) for j = 1:n Q[j, i] = v[j] / c end end else for idx_block = 1:length(blks) - 1, i = blks[idx_block + 1]:-1:blks[idx_block] + 1 #index of eigenvector fill!(v, zero(T)) v[blks[idx_block+1]] = one(T) for j = (blks[idx_block+1] - 1):-1:max(1, (i - 1)) #Starting from j=i, eigenvector elements will be 0 v[j] = (M.dv[i] - M.dv[j+1])/M.ev[j] * v[j+1] end c = norm(v) for j = 1:n Q[j, i] = v[j] / c end end end Q #Actually Triangular end eigen(M::Bidiagonal) = Eigen(eigvals(M), eigvecs(M)) Base._sum(A::Bidiagonal, ::Colon) = sum(A.dv) + sum(A.ev) function Base._sum(A::Bidiagonal, dims::Integer) res = Base.reducedim_initarray(A, dims, zero(eltype(A))) n = length(A.dv) if n == 0 # Just to be sure. This shouldn't happen since there is a check whether # length(A.dv) == length(A.ev) + 1 in the constructor. return res elseif n == 1 res[1] = A.dv[1] return res end @inbounds begin if (dims == 1 && A.uplo == 'U') || (dims == 2 && A.uplo == 'L') res[1] = A.dv[1] for i = 2:length(A.dv) res[i] = A.ev[i-1] + A.dv[i] end elseif (dims == 1 && A.uplo == 'L') || (dims == 2 && A.uplo == 'U') for i = 1:length(A.dv)-1 res[i] = A.ev[i] + A.dv[i] end res[end] = A.dv[end] elseif dims >= 3 if A.uplo == 'U' for i = 1:length(A.dv)-1 res[i,i] = A.dv[i] res[i,i+1] = A.ev[i] end else for i = 1:length(A.dv)-1 res[i,i] = A.dv[i] res[i+1,i] = A.ev[i] end end res[end,end] = A.dv[end] end end res end /cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/uniformscaling.jlR9# This file is a part of Julia. License is MIT: https://julialang.org/license import Base: copy, adjoint, getindex, show, transpose, one, zero, inv, hcat, vcat, hvcat, ^ """ UniformScaling{T<:Number} Generically sized uniform scaling operator defined as a scalar times the identity operator, `ฮป*I`. Although without an explicit `size`, it acts similarly to a matrix in many cases and includes support for some indexing. See also [`I`](@ref). !!! compat "Julia 1.6" Indexing using ranges is available as of Julia 1.6. # Examples ```jldoctest julia> J = UniformScaling(2.) UniformScaling{Float64} 2.0*I julia> A = [1. 2.; 3. 4.] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> J*A 2ร—2 Matrix{Float64}: 2.0 4.0 6.0 8.0 julia> J[1:2, 1:2] 2ร—2 Matrix{Float64}: 2.0 0.0 0.0 2.0 ``` """ struct UniformScaling{T<:Number} ฮป::T end """ I An object of type [`UniformScaling`](@ref), representing an identity matrix of any size. # Examples ```jldoctest julia> fill(1, (5,6)) * I == fill(1, (5,6)) true julia> [1 2im 3; 1im 2 3] * I 2ร—3 Matrix{Complex{Int64}}: 1+0im 0+2im 3+0im 0+1im 2+0im 3+0im ``` """ const I = UniformScaling(true) """ (I::UniformScaling)(n::Integer) Construct a `Diagonal` matrix from a `UniformScaling`. !!! compat "Julia 1.2" This method is available as of Julia 1.2. # Examples ```jldoctest julia> I(3) 3ร—3 Diagonal{Bool, Vector{Bool}}: 1 โ‹… โ‹… โ‹… 1 โ‹… โ‹… โ‹… 1 julia> (0.7*I)(3) 3ร—3 Diagonal{Float64, Vector{Float64}}: 0.7 โ‹… โ‹… โ‹… 0.7 โ‹… โ‹… โ‹… 0.7 ``` """ (I::UniformScaling)(n::Integer) = Diagonal(fill(I.ฮป, n)) eltype(::Type{UniformScaling{T}}) where {T} = T ndims(J::UniformScaling) = 2 Base.has_offset_axes(::UniformScaling) = false getindex(J::UniformScaling, i::Integer,j::Integer) = ifelse(i==j,J.ฮป,zero(J.ฮป)) getindex(J::UniformScaling, n::Integer, m::AbstractVector{<:Integer}) = getindex(J, m, n) function getindex(J::UniformScaling{T}, n::AbstractVector{<:Integer}, m::Integer) where T v = zeros(T, axes(n)) @inbounds for (i,ii) in pairs(n) if ii == m v[i] = J.ฮป end end return v end function getindex(J::UniformScaling{T}, n::AbstractVector{<:Integer}, m::AbstractVector{<:Integer}) where T A = zeros(T, axes(n)..., axes(m)...) @inbounds for (j,jj) in pairs(m), (i,ii) in pairs(n) if ii == jj A[i,j] = J.ฮป end end return A end function show(io::IO, ::MIME"text/plain", J::UniformScaling) s = "$(J.ฮป)" if occursin(r"\w+\s*[\+\-]\s*\w+", s) s = "($s)" end print(io, typeof(J), "\n$s*I") end copy(J::UniformScaling) = UniformScaling(J.ฮป) Base.convert(::Type{UniformScaling{T}}, J::UniformScaling) where {T} = UniformScaling(convert(T, J.ฮป))::UniformScaling{T} conj(J::UniformScaling) = UniformScaling(conj(J.ฮป)) real(J::UniformScaling) = UniformScaling(real(J.ฮป)) imag(J::UniformScaling) = UniformScaling(imag(J.ฮป)) transpose(J::UniformScaling) = J adjoint(J::UniformScaling) = UniformScaling(conj(J.ฮป)) one(::Type{UniformScaling{T}}) where {T} = UniformScaling(one(T)) one(J::UniformScaling{T}) where {T} = one(UniformScaling{T}) oneunit(::Type{UniformScaling{T}}) where {T} = UniformScaling(oneunit(T)) oneunit(J::UniformScaling{T}) where {T} = oneunit(UniformScaling{T}) zero(::Type{UniformScaling{T}}) where {T} = UniformScaling(zero(T)) zero(J::UniformScaling{T}) where {T} = zero(UniformScaling{T}) isdiag(::UniformScaling) = true istriu(::UniformScaling) = true istril(::UniformScaling) = true issymmetric(::UniformScaling) = true ishermitian(J::UniformScaling) = isreal(J.ฮป) isposdef(J::UniformScaling) = isposdef(J.ฮป) (+)(J::UniformScaling, x::Number) = J.ฮป + x (+)(x::Number, J::UniformScaling) = x + J.ฮป (-)(J::UniformScaling, x::Number) = J.ฮป - x (-)(x::Number, J::UniformScaling) = x - J.ฮป (+)(J::UniformScaling) = UniformScaling(+J.ฮป) (+)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.ฮป+J2.ฮป) (+)(B::BitArray{2}, J::UniformScaling) = Array(B) + J (+)(J::UniformScaling, B::BitArray{2}) = J + Array(B) (+)(J::UniformScaling, A::AbstractMatrix) = A + J (-)(J::UniformScaling) = UniformScaling(-J.ฮป) (-)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.ฮป-J2.ฮป) (-)(B::BitArray{2}, J::UniformScaling) = Array(B) - J (-)(J::UniformScaling, B::BitArray{2}) = J - Array(B) (-)(A::AbstractMatrix, J::UniformScaling) = A + (-J) # matrix functions for f in ( :exp, :log, :expm1, :log1p, :sqrt, :cbrt, :sin, :cos, :tan, :asin, :acos, :atan, :csc, :sec, :cot, :acsc, :asec, :acot, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, :csch, :sech, :coth, :acsch, :asech, :acoth ) @eval Base.$f(J::UniformScaling) = UniformScaling($f(J.ฮป)) end # Unit{Lower/Upper}Triangular matrices become {Lower/Upper}Triangular under # addition with a UniformScaling for (t1, t2) in ((:UnitUpperTriangular, :UpperTriangular), (:UnitLowerTriangular, :LowerTriangular)) @eval begin function (+)(UL::$t1, J::UniformScaling) ULnew = copymutable_oftype(UL.data, Base.promote_op(+, eltype(UL), typeof(J))) for i in axes(ULnew, 1) ULnew[i,i] = one(ULnew[i,i]) + J end return ($t2)(ULnew) end end end # Adding a complex UniformScaling to the diagonal of a Hermitian # matrix breaks the hermiticity, if the UniformScaling is non-real. # However, to preserve type stability, we do not special-case a # UniformScaling{<:Complex} that happens to be real. function (+)(A::Hermitian, J::UniformScaling{<:Complex}) TS = Base.promote_op(+, eltype(A), typeof(J)) B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) for i in diagind(B) B[i] = A[i] + J end return B end function (-)(J::UniformScaling{<:Complex}, A::Hermitian) TS = Base.promote_op(+, eltype(A), typeof(J)) B = copytri!(copymutable_oftype(parent(A), TS), A.uplo, true) B .= .-B for i in diagind(B) B[i] = J - A[i] end return B end function (+)(A::AbstractMatrix, J::UniformScaling) checksquare(A) B = copymutable_oftype(A, Base.promote_op(+, eltype(A), typeof(J))) for i in intersect(axes(A,1), axes(A,2)) @inbounds B[i,i] += J end return B end function (-)(J::UniformScaling, A::AbstractMatrix) checksquare(A) B = convert(AbstractMatrix{Base.promote_op(+, eltype(A), typeof(J))}, -A) for i in intersect(axes(A,1), axes(A,2)) @inbounds B[i,i] += J end return B end inv(J::UniformScaling) = UniformScaling(inv(J.ฮป)) opnorm(J::UniformScaling, p::Real=2) = opnorm(J.ฮป, p) pinv(J::UniformScaling) = ifelse(iszero(J.ฮป), UniformScaling(zero(inv(J.ฮป))), # type stability UniformScaling(inv(J.ฮป))) function det(J::UniformScaling{T}) where T if isone(J.ฮป) one(T) elseif iszero(J.ฮป) zero(T) else throw(ArgumentError("Determinant of UniformScaling is only well-defined when ฮป = 0 or 1.")) end end function tr(J::UniformScaling{T}) where T if iszero(J.ฮป) zero(T) else throw(ArgumentError("Trace of UniformScaling is only well-defined when ฮป = 0")) end end *(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.ฮป*J2.ฮป) *(B::BitArray{2}, J::UniformScaling) = *(Array(B), J::UniformScaling) *(J::UniformScaling, B::BitArray{2}) = *(J::UniformScaling, Array(B)) *(A::AbstractMatrix, J::UniformScaling) = A*J.ฮป *(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) * J *(J::UniformScaling, A::AbstractVecOrMat) = J.ฮป*A *(x::Number, J::UniformScaling) = UniformScaling(x*J.ฮป) *(J::UniformScaling, x::Number) = UniformScaling(J.ฮป*x) /(J1::UniformScaling, J2::UniformScaling) = J2.ฮป == 0 ? throw(SingularException(1)) : UniformScaling(J1.ฮป/J2.ฮป) /(J::UniformScaling, A::AbstractMatrix) = (invA = inv(A); lmul!(J.ฮป, convert(AbstractMatrix{promote_type(eltype(J),eltype(invA))}, invA))) /(A::AbstractMatrix, J::UniformScaling) = J.ฮป == 0 ? throw(SingularException(1)) : A/J.ฮป /(v::AbstractVector, J::UniformScaling) = reshape(v, length(v), 1) / J /(J::UniformScaling, x::Number) = UniformScaling(J.ฮป/x) \(J1::UniformScaling, J2::UniformScaling) = J1.ฮป == 0 ? throw(SingularException(1)) : UniformScaling(J1.ฮป\J2.ฮป) \(J::UniformScaling, A::AbstractVecOrMat) = J.ฮป == 0 ? throw(SingularException(1)) : J.ฮป\A \(A::AbstractMatrix, J::UniformScaling) = (invA = inv(A); rmul!(convert(AbstractMatrix{promote_type(eltype(invA),eltype(J))}, invA), J.ฮป)) \(F::Factorization, J::UniformScaling) = F \ J(size(F,1)) \(x::Number, J::UniformScaling) = UniformScaling(x\J.ฮป) @inline mul!(C::AbstractMatrix, A::AbstractMatrix, J::UniformScaling, alpha::Number, beta::Number) = mul!(C, A, J.ฮป, alpha, beta) @inline mul!(C::AbstractVecOrMat, J::UniformScaling, B::AbstractVecOrMat, alpha::Number, beta::Number) = mul!(C, J.ฮป, B, alpha, beta) function mul!(out::AbstractMatrix{T}, a::Number, B::UniformScaling, ฮฑ::Number, ฮฒ::Number) where {T} checksquare(out) if iszero(ฮฒ) # zero contribution of the out matrix fill!(out, zero(T)) elseif !isone(ฮฒ) rmul!(out, ฮฒ) end s = convert(T, a*B.ฮป*ฮฑ) if !iszero(s) @inbounds for i in diagind(out) out[i] += s end end return out end @inline mul!(out::AbstractMatrix, A::UniformScaling, b::Number, ฮฑ::Number, ฮฒ::Number)= mul!(out, A.ฮป, UniformScaling(b), ฮฑ, ฮฒ) rmul!(A::AbstractMatrix, J::UniformScaling) = rmul!(A, J.ฮป) lmul!(J::UniformScaling, B::AbstractVecOrMat) = lmul!(J.ฮป, B) rdiv!(A::AbstractMatrix, J::UniformScaling) = rdiv!(A, J.ฮป) ldiv!(J::UniformScaling, B::AbstractVecOrMat) = ldiv!(J.ฮป, B) ldiv!(Y::AbstractVecOrMat, J::UniformScaling, B::AbstractVecOrMat) = (Y .= J.ฮป .\ B) Broadcast.broadcasted(::typeof(*), x::Number,J::UniformScaling) = UniformScaling(x*J.ฮป) Broadcast.broadcasted(::typeof(*), J::UniformScaling,x::Number) = UniformScaling(J.ฮป*x) Broadcast.broadcasted(::typeof(/), J::UniformScaling,x::Number) = UniformScaling(J.ฮป/x) Broadcast.broadcasted(::typeof(\), x::Number,J::UniformScaling) = UniformScaling(x\J.ฮป) (^)(J::UniformScaling, x::Number) = UniformScaling((J.ฮป)^x) Base.literal_pow(::typeof(^), J::UniformScaling, x::Val) = UniformScaling(Base.literal_pow(^, J.ฮป, x)) Broadcast.broadcasted(::typeof(^), J::UniformScaling, x::Number) = UniformScaling(J.ฮป^x) function Broadcast.broadcasted(::typeof(Base.literal_pow), ::typeof(^), J::UniformScaling, x::Val) UniformScaling(Base.literal_pow(^, J.ฮป, x)) end ==(J1::UniformScaling,J2::UniformScaling) = (J1.ฮป == J2.ฮป) ## equality comparison with UniformScaling ==(J::UniformScaling, A::AbstractMatrix) = A == J function ==(A::AbstractMatrix, J::UniformScaling) require_one_based_indexing(A) size(A, 1) == size(A, 2) || return false iszero(J.ฮป) && return iszero(A) isone(J.ฮป) && return isone(A) return A == J.ฮป*one(A) end function ==(A::StridedMatrix, J::UniformScaling) size(A, 1) == size(A, 2) || return false iszero(J.ฮป) && return iszero(A) isone(J.ฮป) && return isone(A) for j in axes(A, 2), i in axes(A, 1) ifelse(i == j, A[i, j] == J.ฮป, iszero(A[i, j])) || return false end return true end isequal(A::AbstractMatrix, J::UniformScaling) = false isequal(J::UniformScaling, A::AbstractMatrix) = false function isapprox(J1::UniformScaling{T}, J2::UniformScaling{S}; atol::Real=0, rtol::Real=Base.rtoldefault(T,S,atol), nans::Bool=false) where {T<:Number,S<:Number} isapprox(J1.ฮป, J2.ฮป, rtol=rtol, atol=atol, nans=nans) end function isapprox(J::UniformScaling, A::AbstractMatrix; atol::Real = 0, rtol::Real = Base.rtoldefault(promote_leaf_eltypes(A), eltype(J), atol), nans::Bool = false, norm::Function = norm) n = checksquare(A) normJ = norm === opnorm ? abs(J.ฮป) : norm === LinearAlgebra.norm ? abs(J.ฮป) * sqrt(n) : norm(Diagonal(fill(J.ฮป, n))) return norm(A - J) <= max(atol, rtol * max(norm(A), normJ)) end isapprox(A::AbstractMatrix, J::UniformScaling; kwargs...) = isapprox(J, A; kwargs...) """ copyto!(dest::AbstractMatrix, src::UniformScaling) Copies a [`UniformScaling`](@ref) onto a matrix. !!! compat "Julia 1.1" In Julia 1.0 this method only supported a square destination matrix. Julia 1.1. added support for a rectangular matrix. """ function copyto!(A::AbstractMatrix, J::UniformScaling) require_one_based_indexing(A) fill!(A, 0) ฮป = J.ฮป for i = 1:min(size(A,1),size(A,2)) @inbounds A[i,i] = ฮป end return A end function copyto!(A::Diagonal, J::UniformScaling) A.diag .= J.ฮป return A end function copyto!(A::Union{Bidiagonal, SymTridiagonal}, J::UniformScaling) A.ev .= 0 A.dv .= J.ฮป return A end function copyto!(A::Tridiagonal, J::UniformScaling) A.dl .= 0 A.du .= 0 A.d .= J.ฮป return A end function cond(J::UniformScaling{T}) where T onereal = inv(one(real(J.ฮป))) return J.ฮป โ‰  zero(T) ? onereal : oftype(onereal, Inf) end ## Matrix construction from UniformScaling function Matrix{T}(s::UniformScaling, dims::Dims{2}) where {T} A = zeros(T, dims) v = T(s.ฮป) for i in diagind(dims...) @inbounds A[i] = v end return A end Matrix{T}(s::UniformScaling, m::Integer, n::Integer) where {T} = Matrix{T}(s, Dims((m, n))) Matrix(s::UniformScaling, m::Integer, n::Integer) = Matrix(s, Dims((m, n))) Matrix(s::UniformScaling, dims::Dims{2}) = Matrix{eltype(s)}(s, dims) Array{T}(s::UniformScaling, dims::Dims{2}) where {T} = Matrix{T}(s, dims) Array{T}(s::UniformScaling, m::Integer, n::Integer) where {T} = Matrix{T}(s, m, n) Array(s::UniformScaling, m::Integer, n::Integer) = Matrix(s, m, n) Array(s::UniformScaling, dims::Dims{2}) = Matrix(s, dims) dot(A::AbstractMatrix, J::UniformScaling) = dot(tr(A), J.ฮป) dot(J::UniformScaling, A::AbstractMatrix) = dot(J.ฮป, tr(A)) dot(x::AbstractVector, J::UniformScaling, y::AbstractVector) = dot(x, J.ฮป, y) dot(x::AbstractVector, a::Number, y::AbstractVector) = sum(t -> dot(t[1], a, t[2]), zip(x, y)) dot(x::AbstractVector, a::Union{Real,Complex}, y::AbstractVector) = a*dot(x, y) # muladd Base.muladd(A::UniformScaling, B::UniformScaling, z::UniformScaling) = UniformScaling(A.ฮป * B.ฮป + z.ฮป) s/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/qr.jltk# This file is a part of Julia. License is MIT: https://julialang.org/license # QR Factorization """ QR <: Factorization A QR matrix factorization stored in a packed format, typically obtained from [`qr`](@ref). If ``A`` is an `m`ร—`n` matrix, then ```math A = Q R ``` where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. The matrix ``Q`` is stored as a sequence of Householder reflectors ``v_i`` and coefficients ``\\tau_i`` where: ```math Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T). ``` Iterating the decomposition produces the components `Q` and `R`. The object has two fields: * `factors` is an `m`ร—`n` matrix. - The upper triangular part contains the elements of ``R``, that is `R = triu(F.factors)` for a `QR` object `F`. - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`. * `ฯ„` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``. """ struct QR{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: Factorization{T} factors::S ฯ„::C function QR{T,S,C}(factors, ฯ„) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} require_one_based_indexing(factors) new{T,S,C}(factors, ฯ„) end end QR(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T} = QR{T,typeof(factors),typeof(ฯ„)}(factors, ฯ„) QR{T}(factors::AbstractMatrix, ฯ„::AbstractVector) where {T} = QR(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, ฯ„)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(QR{T,S}(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T,S}, QR{T,S,typeof(ฯ„)}(factors, ฯ„), false) # iteration for destructuring into components Base.iterate(S::QR) = (S.Q, Val(:R)) Base.iterate(S::QR, ::Val{:R}) = (S.R, Val(:done)) Base.iterate(S::QR, ::Val{:done}) = nothing # Note. For QRCompactWY factorization without pivoting, the WY representation based method introduced in LAPACK 3.4 """ QRCompactWY <: Factorization A QR matrix factorization stored in a compact blocked format, typically obtained from [`qr`](@ref). If ``A`` is an `m`ร—`n` matrix, then ```math A = Q R ``` where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. It is similar to the [`QR`](@ref) format except that the orthogonal/unitary matrix ``Q`` is stored in *Compact WY* format [^Schreiber1989]. For the block size ``n_b``, it is stored as a `m`ร—`n` lower trapezoidal matrix ``V`` and a matrix ``T = (T_1 \\; T_2 \\; ... \\; T_{b-1} \\; T_b')`` composed of ``b = \\lceil \\min(m,n) / n_b \\rceil`` upper triangular matrices ``T_j`` of size ``n_b``ร—``n_b`` (``j = 1, ..., b-1``) and an upper trapezoidal ``n_b``ร—``\\min(m,n) - (b-1) n_b`` matrix ``T_b'`` (``j=b``) whose upper square part denoted with ``T_b`` satisfying ```math Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T) = \\prod_{j=1}^{b} (I - V_j T_j V_j^T) ``` such that ``v_i`` is the ``i``th column of ``V``, ``\\tau_i`` is the ``i``th element of `[diag(T_1); diag(T_2); โ€ฆ; diag(T_b)]`, and ``(V_1 \\; V_2 \\; ... \\; V_b)`` is the left `m`ร—`min(m, n)` block of ``V``. When constructed using [`qr`](@ref), the block size is given by ``n_b = \\min(m, n, 36)``. Iterating the decomposition produces the components `Q` and `R`. The object has two fields: * `factors`, as in the [`QR`](@ref) type, is an `m`ร—`n` matrix. - The upper triangular part contains the elements of ``R``, that is `R = triu(F.factors)` for a `QR` object `F`. - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format such that `V = I + tril(F.factors, -1)`. * `T` is a ``n_b``-by-``\\min(m,n)`` matrix as described above. The subdiagonal elements for each triangular matrix ``T_j`` are ignored. !!! note This format should not to be confused with the older *WY* representation [^Bischof1987]. [^Bischof1987]: C Bischof and C Van Loan, "The WY representation for products of Householder matrices", SIAM J Sci Stat Comput 8 (1987), s2-s13. [doi:10.1137/0908009](https://doi.org/10.1137/0908009) [^Schreiber1989]: R Schreiber and C Van Loan, "A storage-efficient WY representation for products of Householder transformations", SIAM J Sci Stat Comput 10 (1989), 53-57. [doi:10.1137/0910005](https://doi.org/10.1137/0910005) """ struct QRCompactWY{S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} <: Factorization{S} factors::M T::C function QRCompactWY{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} require_one_based_indexing(factors) new{S,M,C}(factors, T) end end QRCompactWY(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = QRCompactWY{S,typeof(factors),typeof(T)}(factors, T) QRCompactWY{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = QRCompactWY(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(QRCompactWY{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, QRCompactWY{S,M,typeof(T)}(factors, T), false) # iteration for destructuring into components Base.iterate(S::QRCompactWY) = (S.Q, Val(:R)) Base.iterate(S::QRCompactWY, ::Val{:R}) = (S.R, Val(:done)) Base.iterate(S::QRCompactWY, ::Val{:done}) = nothing # returns upper triangular views of all non-undef values of `qr(A).T`: # # julia> sparse(qr(A).T .== qr(A).T) # 36ร—100 SparseMatrixCSC{Bool, Int64} with 1767 stored entries: # โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟ # โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟ # โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟ # โ €โ €โ €โ €โ €โ ‚โ ›โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟ # โ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โข€โ โ ™โขฟโฃฟโฃฟโฃฟโฃฟ # โ €โ €โ โ €โ €โ €โ €โ €โ €โข€โข™โฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ โ €โก€โ €โ ™โขฟโฃฟโฃฟ # โ €โ €โ โ €โ €โ €โ €โ €โ €โ €โ „โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โก€โ €โ €โข€โ €โ €โ ™โขฟ # โ €โก€โ €โ €โ €โ €โ €โ €โ ‚โ ’โ ’โ €โ €โ €โ ™โขฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโฃฟโฃฟโ €โ €โ €โ €โ €โ €โ €โข€โ €โ €โ €โก€โ €โ € # โ €โ €โ €โ €โ €โ €โ €โ €โฃˆโก€โ €โ €โ €โ €โ €โ €โ ™โขฟโ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ €โ ™โขฟโ €โ €โ €โ €โ €โ €โ €โ €โ €โก€โ ‚โ €โข€โ € # function _triuppers_qr(T) blocksize, cols = size(T) return Iterators.map(0:div(cols - 1, blocksize)) do i n = min(blocksize, cols - i * blocksize) return UpperTriangular(view(T, 1:n, (1:n) .+ i * blocksize)) end end function Base.hash(F::QRCompactWY, h::UInt) return hash(F.factors, foldr(hash, _triuppers_qr(F.T); init=hash(QRCompactWY, h))) end function Base.:(==)(A::QRCompactWY, B::QRCompactWY) return A.factors == B.factors && all(splat(==), zip(_triuppers_qr.((A.T, B.T))...)) end function Base.isequal(A::QRCompactWY, B::QRCompactWY) return isequal(A.factors, B.factors) && all(zip(_triuppers_qr.((A.T, B.T))...)) do (a, b) isequal(a, b)::Bool end end """ QRPivoted <: Factorization A QR matrix factorization with column pivoting in a packed format, typically obtained from [`qr`](@ref). If ``A`` is an `m`ร—`n` matrix, then ```math A P = Q R ``` where ``P`` is a permutation matrix, ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. The matrix ``Q`` is stored as a sequence of Householder reflectors: ```math Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T). ``` Iterating the decomposition produces the components `Q`, `R`, and `p`. The object has three fields: * `factors` is an `m`ร—`n` matrix. - The upper triangular part contains the elements of ``R``, that is `R = triu(F.factors)` for a `QR` object `F`. - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`. * `ฯ„` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``. * `jpvt` is an integer vector of length `n` corresponding to the permutation ``P``. """ struct QRPivoted{T,S<:AbstractMatrix{T},C<:AbstractVector{T},P<:AbstractVector{<:Integer}} <: Factorization{T} factors::S ฯ„::C jpvt::P function QRPivoted{T,S,C,P}(factors, ฯ„, jpvt) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T},P<:AbstractVector{<:Integer}} require_one_based_indexing(factors, ฯ„, jpvt) new{T,S,C,P}(factors, ฯ„, jpvt) end end QRPivoted(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}, jpvt::AbstractVector{<:Integer}) where {T} = QRPivoted{T,typeof(factors),typeof(ฯ„),typeof(jpvt)}(factors, ฯ„, jpvt) QRPivoted{T}(factors::AbstractMatrix, ฯ„::AbstractVector, jpvt::AbstractVector{<:Integer}) where {T} = QRPivoted(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, ฯ„), jpvt) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(QRPivoted{T,S}(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}, jpvt::AbstractVector{<:Integer}) where {T,S}, QRPivoted{T,S,typeof(ฯ„),typeof(jpvt)}(factors, ฯ„, jpvt), false) # iteration for destructuring into components Base.iterate(S::QRPivoted) = (S.Q, Val(:R)) Base.iterate(S::QRPivoted, ::Val{:R}) = (S.R, Val(:p)) Base.iterate(S::QRPivoted, ::Val{:p}) = (S.p, Val(:done)) Base.iterate(S::QRPivoted, ::Val{:done}) = nothing function qrfactUnblocked!(A::AbstractMatrix{T}) where {T} require_one_based_indexing(A) m, n = size(A) ฯ„ = zeros(T, min(m,n)) for k = 1:min(m - 1 + !(T<:Real), n) x = view(A, k:m, k) ฯ„k = reflector!(x) ฯ„[k] = ฯ„k reflectorApply!(x, ฯ„k, view(A, k:m, k + 1:n)) end QR(A, ฯ„) end # Find index for columns with largest two norm function indmaxcolumn(A::AbstractMatrix) mm = norm(view(A, :, 1)) ii = 1 for i = 2:size(A, 2) mi = norm(view(A, :, i)) if abs(mi) > mm mm = mi ii = i end end return ii end function qrfactPivotedUnblocked!(A::AbstractMatrix) m, n = size(A) piv = Vector(UnitRange{BlasInt}(1,n)) ฯ„ = Vector{eltype(A)}(undef, min(m,n)) for j = 1:min(m,n) # Find column with maximum norm in trailing submatrix jm = indmaxcolumn(view(A, j:m, j:n)) + j - 1 if jm != j # Flip elements in pivoting vector tmpp = piv[jm] piv[jm] = piv[j] piv[j] = tmpp # Update matrix with for i = 1:m tmp = A[i,jm] A[i,jm] = A[i,j] A[i,j] = tmp end end # Compute reflector of columns j x = view(A, j:m, j) ฯ„j = reflector!(x) ฯ„[j] = ฯ„j # Update trailing submatrix with reflector reflectorApply!(x, ฯ„j, view(A, j:m, j+1:n)) end return QRPivoted{eltype(A), typeof(A), typeof(ฯ„), typeof(piv)}(A, ฯ„, piv) end # LAPACK version qr!(A::StridedMatrix{<:BlasFloat}, ::NoPivot; blocksize=36) = QRCompactWY(LAPACK.geqrt!(A, min(min(size(A)...), blocksize))...) qr!(A::StridedMatrix{<:BlasFloat}, ::ColumnNorm) = QRPivoted(LAPACK.geqp3!(A)...) # Generic fallbacks """ qr!(A, pivot = NoPivot(); blocksize) `qr!` is the same as [`qr`](@ref) when `A` is a subtype of [`AbstractMatrix`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the factorization produces a number not representable by the element type of `A`, e.g. for integer types. !!! compat "Julia 1.4" The `blocksize` keyword argument requires Julia 1.4 or later. # Examples ```jldoctest julia> a = [1. 2.; 3. 4.] 2ร—2 Matrix{Float64}: 1.0 2.0 3.0 4.0 julia> qr!(a) LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} Q factor: 2ร—2 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} R factor: 2ร—2 Matrix{Float64}: -3.16228 -4.42719 0.0 -0.632456 julia> a = [1 2; 3 4] 2ร—2 Matrix{Int64}: 1 2 3 4 julia> qr!(a) ERROR: InexactError: Int64(3.1622776601683795) Stacktrace: [...] ``` """ qr!(A::AbstractMatrix, ::NoPivot) = qrfactUnblocked!(A) qr!(A::AbstractMatrix, ::ColumnNorm) = qrfactPivotedUnblocked!(A) qr!(A::AbstractMatrix) = qr!(A, NoPivot()) # TODO: Remove in Julia v2.0 @deprecate qr!(A::AbstractMatrix, ::Val{true}) qr!(A, ColumnNorm()) @deprecate qr!(A::AbstractMatrix, ::Val{false}) qr!(A, NoPivot()) _qreltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T)))) """ qr(A, pivot = NoPivot(); blocksize) -> F Compute the QR factorization of the matrix `A`: an orthogonal (or unitary if `A` is complex-valued) matrix `Q`, and an upper triangular matrix `R` such that ```math A = Q R ``` The returned object `F` stores the factorization in a packed format: - if `pivot == ColumnNorm()` then `F` is a [`QRPivoted`](@ref) object, - otherwise if the element type of `A` is a BLAS type ([`Float32`](@ref), [`Float64`](@ref), `ComplexF32` or `ComplexF64`), then `F` is a [`QRCompactWY`](@ref) object, - otherwise `F` is a [`QR`](@ref) object. The individual components of the decomposition `F` can be retrieved via property accessors: - `F.Q`: the orthogonal/unitary matrix `Q` - `F.R`: the upper triangular matrix `R` - `F.p`: the permutation vector of the pivot ([`QRPivoted`](@ref) only) - `F.P`: the permutation matrix of the pivot ([`QRPivoted`](@ref) only) Iterating the decomposition produces the components `Q`, `R`, and if extant `p`. The following functions are available for the `QR` objects: [`inv`](@ref), [`size`](@ref), and [`\\`](@ref). When `A` is rectangular, `\\` will return a least squares solution and if the solution is not unique, the one with smallest norm is returned. When `A` is not full rank, factorization with (column) pivoting is required to obtain a minimum norm solution. Multiplication with respect to either full/square or non-full/square `Q` is allowed, i.e. both `F.Q*F.R` and `F.Q*A` are supported. A `Q` matrix can be converted into a regular matrix with [`Matrix`](@ref). This operation returns the "thin" Q factor, i.e., if `A` is `m`ร—`n` with `m>=n`, then `Matrix(F.Q)` yields an `m`ร—`n` matrix with orthonormal columns. To retrieve the "full" Q factor, an `m`ร—`m` orthogonal matrix, use `F.Q*I` or `collect(F.Q)`. If `m<=n`, then `Matrix(F.Q)` yields an `m`ร—`m` orthogonal matrix. The block size for QR decomposition can be specified by keyword argument `blocksize :: Integer` when `pivot == NoPivot()` and `A isa StridedMatrix{<:BlasFloat}`. It is ignored when `blocksize > minimum(size(A))`. See [`QRCompactWY`](@ref). !!! compat "Julia 1.4" The `blocksize` keyword argument requires Julia 1.4 or later. # Examples ```jldoctest julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0] 3ร—2 Matrix{Float64}: 3.0 -6.0 4.0 -8.0 0.0 1.0 julia> F = qr(A) LinearAlgebra.QRCompactWY{Float64, Matrix{Float64}, Matrix{Float64}} Q factor: 3ร—3 LinearAlgebra.QRCompactWYQ{Float64, Matrix{Float64}, Matrix{Float64}} R factor: 2ร—2 Matrix{Float64}: -5.0 10.0 0.0 -1.0 julia> F.Q * F.R == A true ``` !!! note `qr` returns multiple types because LAPACK uses several representations that minimize the memory storage requirements of products of Householder elementary reflectors, so that the `Q` and `R` matrices can be stored compactly rather as two separate dense matrices. """ function qr(A::AbstractMatrix{T}, arg...; kwargs...) where T require_one_based_indexing(A) AA = copy_similar(A, _qreltype(T)) return qr!(AA, arg...; kwargs...) end # TODO: remove in Julia v2.0 @deprecate qr(A::AbstractMatrix, ::Val{false}; kwargs...) qr(A, NoPivot(); kwargs...) @deprecate qr(A::AbstractMatrix, ::Val{true}; kwargs...) qr(A, ColumnNorm(); kwargs...) qr(x::Number) = qr(fill(x,1,1)) function qr(v::AbstractVector) require_one_based_indexing(v) qr(reshape(v, (length(v), 1))) end # Conversions QR{T}(A::QR) where {T} = QR(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.ฯ„)) Factorization{T}(A::QR{T}) where {T} = A Factorization{T}(A::QR) where {T} = QR{T}(A) QRCompactWY{T}(A::QRCompactWY) where {T} = QRCompactWY(convert(AbstractMatrix{T}, A.factors), convert(AbstractMatrix{T}, A.T)) Factorization{T}(A::QRCompactWY{T}) where {T} = A Factorization{T}(A::QRCompactWY) where {T} = QRCompactWY{T}(A) AbstractMatrix(F::Union{QR,QRCompactWY}) = F.Q * F.R AbstractArray(F::Union{QR,QRCompactWY}) = AbstractMatrix(F) Matrix(F::Union{QR,QRCompactWY}) = Array(AbstractArray(F)) Array(F::Union{QR,QRCompactWY}) = Matrix(F) QRPivoted{T}(A::QRPivoted) where {T} = QRPivoted(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.ฯ„), A.jpvt) Factorization{T}(A::QRPivoted{T}) where {T} = A Factorization{T}(A::QRPivoted) where {T} = QRPivoted{T}(A) AbstractMatrix(F::QRPivoted) = (F.Q * F.R)[:,invperm(F.p)] AbstractArray(F::QRPivoted) = AbstractMatrix(F) Matrix(F::QRPivoted) = Array(AbstractArray(F)) Array(F::QRPivoted) = Matrix(F) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Union{QR, QRCompactWY, QRPivoted}) summary(io, F); println(io) print(io, "Q factor: ") show(io, mime, F.Q) println(io, "\nR factor:") show(io, mime, F.R) if F isa QRPivoted println(io, "\npermutation:") show(io, mime, F.p) end end function getproperty(F::QR, d::Symbol) m, n = size(F) if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) elseif d === :Q return QRPackedQ(getfield(F, :factors), F.ฯ„) else getfield(F, d) end end function getproperty(F::QRCompactWY, d::Symbol) m, n = size(F) if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) elseif d === :Q return QRCompactWYQ(getfield(F, :factors), F.T) else getfield(F, d) end end Base.propertynames(F::Union{QR,QRCompactWY}, private::Bool=false) = (:R, :Q, (private ? fieldnames(typeof(F)) : ())...) function getproperty(F::QRPivoted{T}, d::Symbol) where T m, n = size(F) if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) elseif d === :Q return QRPackedQ(getfield(F, :factors), F.ฯ„) elseif d === :p return getfield(F, :jpvt) elseif d === :P p = F.p n = length(p) P = zeros(T, n, n) for i in 1:n P[p[i],i] = one(T) end return P else getfield(F, d) end end Base.propertynames(F::QRPivoted, private::Bool=false) = (:R, :Q, :p, :P, (private ? fieldnames(typeof(F)) : ())...) transpose(F::Union{QR{<:Real},QRPivoted{<:Real},QRCompactWY{<:Real}}) = F' transpose(::Union{QR,QRPivoted,QRCompactWY}) = throw(ArgumentError("transpose of QR decomposition is not supported, consider using adjoint")) size(F::Union{QR,QRCompactWY,QRPivoted}) = size(getfield(F, :factors)) size(F::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(getfield(F, :factors), dim) function ldiv!(A::QRCompactWY{T}, b::AbstractVector{T}) where {T} require_one_based_indexing(b) m, n = size(A) ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), b), 1:size(A, 2))) return b end function ldiv!(A::QRCompactWY{T}, B::AbstractMatrix{T}) where {T} require_one_based_indexing(B) m, n = size(A) ldiv!(UpperTriangular(view(A.factors, 1:min(m,n), 1:n)), view(lmul!(adjoint(A.Q), B), 1:size(A, 2), 1:size(B, 2))) return B end # Julia implementation similar to xgelsy function ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}, rcond::Real) where {T<:BlasFloat} require_one_based_indexing(B) m, n = size(A) if m > size(B, 1) || n > size(B, 1) throw(DimensionMismatch("B has leading dimension $(size(B, 1)) but needs at least $(max(m, n))")) end if length(A.factors) == 0 || length(B) == 0 return B, 0 end @inbounds begin smin = smax = abs(A.factors[1]) if smax == 0 return fill!(B, 0), 0 end mn = min(m, n) # allocate temporary work space tmp = Vector{T}(undef, 2mn) wmin = view(tmp, 1:mn) wmax = view(tmp, mn+1:2mn) rnk = 1 wmin[1] = 1 wmax[1] = 1 while rnk < mn i = rnk + 1 smin, s1, c1 = LAPACK.laic1!(2, view(wmin, 1:rnk), smin, view(A.factors, 1:rnk, i), A.factors[i,i]) smax, s2, c2 = LAPACK.laic1!(1, view(wmax, 1:rnk), smax, view(A.factors, 1:rnk, i), A.factors[i,i]) if smax*rcond > smin break end for j in 1:rnk wmin[j] *= s1 wmax[j] *= s2 end wmin[i] = c1 wmax[i] = c2 rnk += 1 end if rnk < n C, ฯ„ = LAPACK.tzrzf!(A.factors[1:rnk, :]) work = vec(C) else C, ฯ„ = A.factors, A.ฯ„ work = resize!(tmp, n) end lmul!(adjoint(A.Q), view(B, 1:m, :)) ldiv!(UpperTriangular(view(C, 1:rnk, 1:rnk)), view(B, 1:rnk, :)) if rnk < n B[rnk+1:n,:] .= zero(T) LAPACK.ormrz!('L', T <: Complex ? 'C' : 'T', C, ฯ„, view(B, 1:n, :)) end for j in axes(B, 2) for i in 1:n work[A.p[i]] = B[i,j] end for i in 1:n B[i,j] = work[i] end end end return B, rnk end ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractVector{T}) where {T<:BlasFloat} = vec(ldiv!(A, reshape(B, length(B), 1))) ldiv!(A::QRPivoted{T,<:StridedMatrix}, B::AbstractMatrix{T}) where {T<:BlasFloat} = ldiv!(A, B, min(size(A)...)*eps(real(T)))[1] function _wide_qr_ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T m, n = size(A) minmn = min(m,n) mB, nB = size(B) lmul!(adjoint(A.Q), view(B, 1:m, :)) R = A.R # makes a copy, used as a buffer below @inbounds begin if n > m # minimum norm solution ฯ„ = zeros(T,m) for k = m:-1:1 # Trapezoid to triangular by elementary operation x = view(R, k, [k; m + 1:n]) ฯ„k = reflector!(x) ฯ„[k] = conj(ฯ„k) for i = 1:k - 1 vRi = R[i,k] for j = m + 1:n vRi += R[i,j]*x[j - m + 1]' end vRi *= ฯ„k R[i,k] -= vRi for j = m + 1:n R[i,j] -= vRi*x[j - m + 1] end end end end ldiv!(UpperTriangular(view(R, :, 1:minmn)), view(B, 1:minmn, :)) if n > m # Apply elementary transformation to solution B[m + 1:mB,1:nB] .= zero(T) for j = 1:nB for k = 1:m vBj = B[k,j]' for i = m + 1:n vBj += B[i,j]'*R[k,i]' end vBj *= ฯ„[k] B[k,j] -= vBj' for i = m + 1:n B[i,j] -= R[k,i]'*vBj' end end end end end return B end function ldiv!(A::QR{T}, B::AbstractMatrix{T}) where T m, n = size(A) m < n && return _wide_qr_ldiv!(A, B) lmul!(adjoint(A.Q), view(B, 1:m, :)) R = A.factors ldiv!(UpperTriangular(view(R,1:n,:)), view(B, 1:n, :)) return B end function ldiv!(A::QR, B::AbstractVector) ldiv!(A, reshape(B, length(B), 1)) return B end function ldiv!(A::QRPivoted, b::AbstractVector) ldiv!(QR(A.factors,A.ฯ„), b) b[1:size(A.factors, 2)] = view(b, 1:size(A.factors, 2))[invperm(A.jpvt)] b end function ldiv!(A::QRPivoted, B::AbstractMatrix) ldiv!(QR(A.factors, A.ฯ„), B) B[1:size(A.factors, 2),:] = view(B, 1:size(A.factors, 2), :)[invperm(A.jpvt),:] B end function _apply_permutation!(F::QRPivoted, B::AbstractVecOrMat) # Apply permutation but only to the top part of the solution vector since # it's padded with zeros for underdetermined problems B[1:length(F.p), :] = B[F.p, :] return B end _apply_permutation!(::Factorization, B::AbstractVecOrMat) = B function ldiv!(Fadj::AdjointFactorization{<:Any,<:Union{QR,QRCompactWY,QRPivoted}}, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(Fadj) # We don't allow solutions overdetermined systems if m > n throw(DimensionMismatch("overdetermined systems are not supported")) end if n != size(B, 1) throw(DimensionMismatch("inputs should have the same number of rows")) end F = parent(Fadj) B = _apply_permutation!(F, B) # For underdetermined system, the triangular solve should only be applied to the top # part of B that contains the rhs. For square problems, the view corresponds to B itself ldiv!(LowerTriangular(adjoint(F.R)), view(B, 1:size(F.R, 2), :)) lmul!(F.Q, B) return B end # With a real lhs and complex rhs with the same precision, we can reinterpret the complex # rhs as a real rhs with twice the number of columns. # convenience methods to compute the return size correctly for vectors and matrices _ret_size(A::Factorization, b::AbstractVector) = (max(size(A, 2), length(b)),) _ret_size(A::Factorization, B::AbstractMatrix) = (max(size(A, 2), size(B, 1)), size(B, 2)) function (\)(A::Union{QR{T},QRCompactWY{T},QRPivoted{T}}, BIn::VecOrMat{Complex{T}}) where T<:BlasReal require_one_based_indexing(BIn) m, n = size(A) m == size(BIn, 1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(BIn,1)) rows")) # |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3| # |z2|z4| -> |y1|y2|y3|y4| -> |x2|y2| -> |x2|y2|x4|y4| # |x3|y3| # |x4|y4| B = reshape(copy(transpose(reinterpret(T, reshape(BIn, (1, length(BIn)))))), size(BIn, 1), 2*size(BIn, 2)) X = _zeros(T, B, n) X[1:size(B, 1), :] = B ldiv!(A, X) # |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3| # |z2|z4| <- |y1|y2|y3|y4| <- |x2|y2| <- |x2|y2|x4|y4| # |x3|y3| # |x4|y4| XX = reshape(collect(reinterpret(Complex{T}, copy(transpose(reshape(X, div(length(X), 2), 2))))), _ret_size(A, BIn)) return _cut_B(XX, 1:n) end ##TODO: Add methods for rank(A::QRP{T}) and adjust the (\) method accordingly ## Add rcond methods for Cholesky, LU, QR and QRP types ## Lower priority: Add LQ, QL and RQ factorizations # FIXME! Should add balancing option through xgebal s/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/lq.jlk# This file is a part of Julia. License is MIT: https://julialang.org/license # LQ Factorizations """ LQ <: Factorization Matrix factorization type of the `LQ` factorization of a matrix `A`. The `LQ` decomposition is the [`QR`](@ref) decomposition of `transpose(A)`. This is the return type of [`lq`](@ref), the corresponding matrix factorization function. If `S::LQ` is the factorization object, the lower triangular component can be obtained via `S.L`, and the orthogonal/unitary component via `S.Q`, such that `A โ‰ˆ S.L*S.Q`. Iterating the decomposition produces the components `S.L` and `S.Q`. # Examples ```jldoctest julia> A = [5. 7.; -2. -4.] 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> S = lq(A) LQ{Float64, Matrix{Float64}, Vector{Float64}} L factor: 2ร—2 Matrix{Float64}: -8.60233 0.0 4.41741 -0.697486 Q factor: 2ร—2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} julia> S.L * S.Q 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> l, q = S; # destructuring via iteration julia> l == S.L && q == S.Q true ``` """ struct LQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: Factorization{T} factors::S ฯ„::C function LQ{T,S,C}(factors, ฯ„) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} require_one_based_indexing(factors) new{T,S,C}(factors, ฯ„) end end LQ(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T} = LQ{T,typeof(factors),typeof(ฯ„)}(factors, ฯ„) LQ{T}(factors::AbstractMatrix, ฯ„::AbstractVector) where {T} = LQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, ฯ„)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(LQ{T,S}(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T,S}, LQ{T,S,typeof(ฯ„)}(factors, ฯ„), false) # iteration for destructuring into components Base.iterate(S::LQ) = (S.L, Val(:Q)) Base.iterate(S::LQ, ::Val{:Q}) = (S.Q, Val(:done)) Base.iterate(S::LQ, ::Val{:done}) = nothing """ lq!(A) -> LQ Compute the [`LQ`](@ref) factorization of `A`, using the input matrix as a workspace. See also [`lq`](@ref). """ lq!(A::StridedMatrix{<:BlasFloat}) = LQ(LAPACK.gelqf!(A)...) """ lq(A) -> S::LQ Compute the LQ decomposition of `A`. The decomposition's lower triangular component can be obtained from the [`LQ`](@ref) object `S` via `S.L`, and the orthogonal/unitary component via `S.Q`, such that `A โ‰ˆ S.L*S.Q`. Iterating the decomposition produces the components `S.L` and `S.Q`. The LQ decomposition is the QR decomposition of `transpose(A)`, and it is useful in order to compute the minimum-norm solution `lq(A) \\ b` to an underdetermined system of equations (`A` has more columns than rows, but has full row rank). # Examples ```jldoctest julia> A = [5. 7.; -2. -4.] 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> S = lq(A) LQ{Float64, Matrix{Float64}, Vector{Float64}} L factor: 2ร—2 Matrix{Float64}: -8.60233 0.0 4.41741 -0.697486 Q factor: 2ร—2 LinearAlgebra.LQPackedQ{Float64, Matrix{Float64}, Vector{Float64}} julia> S.L * S.Q 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> l, q = S; # destructuring via iteration julia> l == S.L && q == S.Q true ``` """ lq(A::AbstractMatrix{T}) where {T} = lq!(copy_similar(A, lq_eltype(T))) lq(x::Number) = lq!(fill(convert(lq_eltype(typeof(x)), x), 1, 1)) lq_eltype(::Type{T}) where {T} = typeof(zero(T) / sqrt(abs2(one(T)))) copy(A::LQ) = LQ(copy(A.factors), copy(A.ฯ„)) LQ{T}(A::LQ) where {T} = LQ(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.ฯ„)) Factorization{T}(A::LQ) where {T} = LQ{T}(A) AbstractMatrix(A::LQ) = A.L*A.Q AbstractArray(A::LQ) = AbstractMatrix(A) Matrix(A::LQ) = Array(AbstractArray(A)) Array(A::LQ) = Matrix(A) transpose(F::LQ{<:Real}) = F' transpose(::LQ) = throw(ArgumentError("transpose of LQ decomposition is not supported, consider using adjoint")) Base.copy(F::AdjointFactorization{T,<:LQ{T}}) where {T} = QR{T,typeof(F.parent.factors),typeof(F.parent.ฯ„)}(copy(adjoint(F.parent.factors)), copy(F.parent.ฯ„)) function getproperty(F::LQ, d::Symbol) m, n = size(F) if d === :L return tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) elseif d === :Q return LQPackedQ(getfield(F, :factors), getfield(F, :ฯ„)) else return getfield(F, d) end end Base.propertynames(F::LQ, private::Bool=false) = (:L, :Q, (private ? fieldnames(typeof(F)) : ())...) # getindex(A::LQPackedQ, i::Integer, j::Integer) = # lmul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i] function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LQ) summary(io, F); println(io) println(io, "L factor:") show(io, mime, F.L) print(io, "\nQ factor: ") show(io, mime, F.Q) end size(F::LQ, dim::Integer) = size(getfield(F, :factors), dim) size(F::LQ) = size(getfield(F, :factors)) ## Multiplication by LQ function lmul!(A::LQ, B::AbstractVecOrMat) lmul!(LowerTriangular(A.L), view(lmul!(A.Q, B), 1:size(A,1), axes(B,2))) return B end function *(A::LQ{TA}, B::AbstractVecOrMat{TB}) where {TA,TB} TAB = promote_type(TA, TB) _cut_B(lmul!(convert(Factorization{TAB}, A), copy_similar(B, TAB)), 1:size(A,1)) end # With a real lhs and complex rhs with the same precision, we can reinterpret # the complex rhs as a real rhs with twice the number of columns function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal require_one_based_indexing(B) X = zeros(T, size(F,2), 2*size(B,2)) X[1:size(B,1), 1:size(B,2)] .= real.(B) X[1:size(B,1), size(B,2)+1:size(X,2)] .= imag.(B) ldiv!(F, X) return reshape(copy(reinterpret(Complex{T}, copy(transpose(reshape(X, div(length(X), 2), 2))))), isa(B, AbstractVector) ? (size(F,2),) : (size(F,2), size(B,2))) end function ldiv!(A::LQ, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(A) m โ‰ค n || throw(DimensionMismatch("LQ solver does not support overdetermined systems (more rows than columns)")) ldiv!(LowerTriangular(A.L), view(B, 1:size(A,1), axes(B,2))) return lmul!(adjoint(A.Q), B) end function ldiv!(Fadj::AdjointFactorization{<:Any,<:LQ}, B::AbstractVecOrMat) require_one_based_indexing(B) m, n = size(Fadj) m >= n || throw(DimensionMismatch("solver does not support underdetermined systems (more columns than rows)")) F = parent(Fadj) lmul!(F.Q, B) ldiv!(UpperTriangular(adjoint(F.L)), view(B, 1:size(F,1), axes(B,2))) return B end {/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/hessenberg.jlqU# This file is a part of Julia. License is MIT: https://julialang.org/license ###################################################################################### # Upper-Hessenberg matrices H+ฮผI, analogous to the UpperTriangular type """ UpperHessenberg(A::AbstractMatrix) Construct an `UpperHessenberg` view of the matrix `A`. Entries of `A` below the first subdiagonal are ignored. !!! compat "Julia 1.3" This type was added in Julia 1.3. Efficient algorithms are implemented for `H \\ b`, `det(H)`, and similar. See also the [`hessenberg`](@ref) function to factor any matrix into a similar upper-Hessenberg matrix. If `F::Hessenberg` is the factorization object, the unitary matrix can be accessed with `F.Q` and the Hessenberg matrix with `F.H`. When `Q` is extracted, the resulting type is the `HessenbergQ` object, and may be converted to a regular matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). Iterating the decomposition produces the factors `F.Q` and `F.H`. # Examples ```jldoctest julia> A = [1 2 3 4; 5 6 7 8; 9 10 11 12; 13 14 15 16] 4ร—4 Matrix{Int64}: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 julia> UpperHessenberg(A) 4ร—4 UpperHessenberg{Int64, Matrix{Int64}}: 1 2 3 4 5 6 7 8 โ‹… 10 11 12 โ‹… โ‹… 15 16 ``` """ struct UpperHessenberg{T,S<:AbstractMatrix{T}} <: AbstractMatrix{T} data::S function UpperHessenberg{T,S}(data) where {T,S<:AbstractMatrix{T}} require_one_based_indexing(data) new{T,S}(data) end end UpperHessenberg(H::UpperHessenberg) = H UpperHessenberg{T}(A::AbstractMatrix) where {T} = UpperHessenberg(convert(AbstractMatrix{T}, A)) UpperHessenberg{T}(H::UpperHessenberg) where {T} = UpperHessenberg{T}(H.data) UpperHessenberg(A::AbstractMatrix) = UpperHessenberg{eltype(A),typeof(A)}(A) Matrix(H::UpperHessenberg{T}) where {T} = Matrix{T}(H) Array(H::UpperHessenberg) = Matrix(H) size(H::UpperHessenberg, d) = size(H.data, d) size(H::UpperHessenberg) = size(H.data) parent(H::UpperHessenberg) = H.data # similar behaves like UpperTriangular similar(H::UpperHessenberg, ::Type{T}) where {T} = UpperHessenberg(similar(H.data, T)) similar(H::UpperHessenberg, ::Type{T}, dims::Dims{N}) where {T,N} = similar(H.data, T, dims) AbstractMatrix{T}(H::UpperHessenberg) where {T} = UpperHessenberg(AbstractMatrix{T}(H.data)) copy(H::UpperHessenberg) = UpperHessenberg(copy(H.data)) real(H::UpperHessenberg{<:Real}) = H real(H::UpperHessenberg{<:Complex}) = UpperHessenberg(triu!(real(H.data),-1)) imag(H::UpperHessenberg) = UpperHessenberg(triu!(imag(H.data),-1)) function istriu(A::UpperHessenberg, k::Integer=0) k <= -1 && return true return _istriu(A, k) end function Matrix{T}(H::UpperHessenberg) where T m,n = size(H) return triu!(copyto!(Matrix{T}(undef, m, n), H.data), -1) end Base.isassigned(H::UpperHessenberg, i::Int, j::Int) = i <= j+1 ? isassigned(H.data, i, j) : true getindex(H::UpperHessenberg{T}, i::Integer, j::Integer) where {T} = i <= j+1 ? convert(T, H.data[i,j]) : zero(T) function setindex!(A::UpperHessenberg, x, i::Integer, j::Integer) if i > j+1 x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " * "($i, $j) of an UpperHessenberg matrix to a nonzero value ($x)")) else A.data[i,j] = x end return A end function Base.replace_in_print_matrix(A::UpperHessenberg, i::Integer, j::Integer, s::AbstractString) return i <= j+1 ? s : Base.replace_with_centered_mark(s) end Base.copy(A::Adjoint{<:Any,<:UpperHessenberg}) = tril!(adjoint!(similar(A.parent.data), A.parent.data), 1) Base.copy(A::Transpose{<:Any,<:UpperHessenberg}) = tril!(transpose!(similar(A.parent.data), A.parent.data), 1) -(A::UpperHessenberg) = UpperHessenberg(-A.data) rmul!(H::UpperHessenberg, x::Number) = (rmul!(H.data, x); H) lmul!(x::Number, H::UpperHessenberg) = (lmul!(x, H.data); H) fillstored!(H::UpperHessenberg, x) = (fillband!(H.data, x, -1, size(H,2)-1); H) +(A::UpperHessenberg, B::UpperHessenberg) = UpperHessenberg(A.data+B.data) -(A::UpperHessenberg, B::UpperHessenberg) = UpperHessenberg(A.data-B.data) for T = (:UniformScaling, :Diagonal, :Bidiagonal, :Tridiagonal, :SymTridiagonal, :UpperTriangular, :UnitUpperTriangular) for op = (:+, :-) @eval begin $op(H::UpperHessenberg, x::$T) = UpperHessenberg($op(H.data, x)) $op(x::$T, H::UpperHessenberg) = UpperHessenberg($op(x, H.data)) end end end for T = (:Number, :UniformScaling, :Diagonal) @eval begin *(H::UpperHessenberg, x::$T) = UpperHessenberg(H.data * x) *(x::$T, H::UpperHessenberg) = UpperHessenberg(x * H.data) /(H::UpperHessenberg, x::$T) = UpperHessenberg(H.data / x) \(x::$T, H::UpperHessenberg) = UpperHessenberg(x \ H.data) end end function *(H::UpperHessenberg, U::UpperOrUnitUpperTriangular) HH = mul!(_initarray(*, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function *(U::UpperOrUnitUpperTriangular, H::UpperHessenberg) HH = mul!(_initarray(*, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UpperTriangular) HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function /(H::UpperHessenberg, U::UnitUpperTriangular) HH = _rdiv!(_initarray(/, eltype(H), eltype(U), H), H, U) UpperHessenberg(HH) end function \(U::UpperTriangular, H::UpperHessenberg) HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end function \(U::UnitUpperTriangular, H::UpperHessenberg) HH = ldiv!(_initarray(\, eltype(U), eltype(H), H), U, H) UpperHessenberg(HH) end # Solving (H+ยตI)x = b: we can do this in O(mยฒ) time and O(m) memory # (in-place in x) by the RQ algorithm from: # # G. Henry, "The shifted Hessenberg system solve computation," Tech. Rep. 94โ€“163, # Center for Appl. Math., Cornell University (1994). # # as reviewed in # # C. Beattie et al., "A note on shifted Hessenberg systems and frequency # response computation," ACM Trans. Math. Soft. 38, pp. 12:6โ€“12:16 (2011) # # (Note, however, that there is apparently a typo in Algorithm 1 of the # Beattie paper: the Givens rotation uses u(k), not H(k,k) - ฯƒ.) # # Essentially, it works by doing a Givens RQ factorization of H+ยตI from # right to left, and doing backsubstitution *simultaneously*. # solve (H+ฮผI)X = B, storing result in B function ldiv!(F::UpperHessenberg, B::AbstractVecOrMat; shift::Number=false) checksquare(F) m = size(F,1) m != size(B,1) && throw(DimensionMismatch("wrong right-hand-side # rows != $m")) require_one_based_indexing(B) n = size(B,2) H = F.data ฮผ = shift u = Vector{typeof(zero(eltype(H))+ฮผ)}(undef, m) # for last rotated col of H-ฮผI copyto!(u, 1, H, m*(m-1)+1, m) # u .= H[:,m] u[m] += ฮผ X = B # not a copy, just rename to match paper cs = Vector{Tuple{real(eltype(u)),eltype(u)}}(undef, length(u)) # store Givens rotations @inbounds for k = m:-1:2 c, s, ฯ = givensAlgorithm(u[k], H[k,k-1]) cs[k] = (c, s) for i = 1:n X[k,i] /= ฯ tโ‚ = s * X[k,i]; tโ‚‚ = c * X[k,i] @simd for j = 1:k-2 X[j,i] -= u[j]*tโ‚‚ + H[j,k-1]*tโ‚ end X[k-1,i] -= u[k-1]*tโ‚‚ + (H[k-1,k-1] + ฮผ) * tโ‚ end @simd for j = 1:k-2 u[j] = H[j,k-1]*c - u[j]*s' end u[k-1] = (H[k-1,k-1] + ฮผ) * c - u[k-1]*s' end for i = 1:n ฯ„โ‚ = X[1,i] / u[1] @inbounds for j = 2:m ฯ„โ‚‚ = X[j,i] c, s = cs[j] X[j-1,i] = c*ฯ„โ‚ + s*ฯ„โ‚‚ ฯ„โ‚ = c*ฯ„โ‚‚ - s'ฯ„โ‚ end X[m,i] = ฯ„โ‚ end return X end # solve X(H+ฮผI) = B, storing result in B # # Note: this can be derived from the Henry (1994) algorithm # by transformation to F(Hแต€+ยตI)F FXแต€ = FBแต€, where # F is the permutation matrix that reverses the order # of rows/cols. Essentially, we take the ldiv! algorithm, # swap indices of H and X to transpose, and reverse the # order of the H indices (or the order of the loops). function rdiv!(B::AbstractMatrix, F::UpperHessenberg; shift::Number=false) checksquare(F) m = size(F,1) m != size(B,2) && throw(DimensionMismatch("wrong right-hand-side # cols != $m")) require_one_based_indexing(B) n = size(B,1) H = F.data ฮผ = shift u = Vector{typeof(zero(eltype(H))+ฮผ)}(undef, m) # for last rotated row of H-ฮผI u .= @view H[1,:] u[1] += ฮผ X = B # not a copy, just rename to match paper cs = Vector{Tuple{real(eltype(u)),eltype(u)}}(undef, length(u)) # store Givens rotations @inbounds for k = 1:m-1 c, s, ฯ = givensAlgorithm(u[k], H[k+1,k]) cs[k] = (c, s) for i = 1:n X[i,k] /= ฯ tโ‚ = s * X[i,k]; tโ‚‚ = c * X[i,k] @simd for j = k+2:m X[i,j] -= u[j]*tโ‚‚ + H[k+1,j]*tโ‚ end X[i,k+1] -= u[k+1]*tโ‚‚ + (H[k+1,k+1] + ฮผ) * tโ‚ end @simd for j = k+2:m u[j] = H[k+1,j]*c - u[j]*s' end u[k+1] = (H[k+1,k+1] + ฮผ) * c - u[k+1]*s' end for i = 1:n ฯ„โ‚ = X[i,m] / u[m] @inbounds for j = m-1:-1:1 ฯ„โ‚‚ = X[i,j] c, s = cs[j] X[i,j+1] = c*ฯ„โ‚ + s*ฯ„โ‚‚ ฯ„โ‚ = c*ฯ„โ‚‚ - s'ฯ„โ‚ end X[i,1] = ฯ„โ‚ end return X end # Hessenberg-matrix determinant formula for H+ฮผI based on: # # N. D. Cahill, J. R. Dโ€™Errico, D. A. Narayan, and J. Y. Narayan, "Fibonacci determinants," # College Math. J. 33, pp. 221-225 (2003). # # as reviewed in Theorem 2.1 of: # # K. Kaygisiz and A. Sahin, "Determinant and permanent of Hessenberg matrix and generalized Lucas polynomials," # arXiv:1111.4067 (2011). # # Cost is O(mยฒ) with O(m) storage. function det(F::UpperHessenberg; shift::Number=false) checksquare(F) H = F.data m = size(H,1) ฮผ = shift m == 0 && return one(zero(eltype(H)) + ฮผ) determinant = H[1,1] + ฮผ prevdeterminant = one(determinant) m == 1 && return determinant prods = Vector{typeof(determinant)}(undef, m-1) # temporary storage for partial products @inbounds for n = 2:m prods[n-1] = prevdeterminant prevdeterminant = determinant determinant *= H[n,n] + ฮผ h = H[n,n-1] @simd for r = n-1:-2:2 determinant -= H[r,n] * (prods[r] *= h) - H[r-1,n] * (prods[r-1] *= h) end if iseven(n) determinant -= H[1,n] * (prods[1] *= h) end end return determinant end # O(mยฒ) log-determinant based on first doing Givens RQ to put H+ฮผI into upper-triangular form and then # taking the product of the diagonal entries. The trick is that we only need O(m) temporary storage, # because we don't need to store the whole Givens-rotated matrix, only the most recent column. # We do RQ (column rotations) rather than QR (row rotations) for more consecutive memory access. # (We could also use it for det instead of the Cahill algorithm above. Cahill is slightly faster # for very small matrices where you are likely to use det, and also uses only ยฑ and * so it can # be applied to Hessenberg matrices over other number fields.) function logabsdet(F::UpperHessenberg; shift::Number=false) checksquare(F) H = F.data m = size(H,1) ฮผ = shift P = one(zero(eltype(H)) + ฮผ) logdeterminant = zero(real(P)) m == 0 && return (logdeterminant, P) g = Vector{typeof(P)}(undef, m) # below, g is the k-th col of Givens-rotated H+ฮผI matrix copyto!(g, 1, H, m*(m-1)+1, m) # g .= H[:,m] g[m] += ฮผ @inbounds for k = m:-1:2 c, s, ฯ = givensAlgorithm(g[k], H[k,k-1]) logdeterminant += log(abs(ฯ)) P *= sign(ฯ) g[k-1] = c*(H[k-1,k-1] + ฮผ) - s'*g[k-1] @simd for j = 1:k-2 g[j] = c*H[j,k-1] - s'*g[j] end end logdeterminant += log(abs(g[1])) P *= sign(g[1]) return (logdeterminant, P) end function dot(x::AbstractVector, H::UpperHessenberg, y::AbstractVector) require_one_based_indexing(x, y) m = size(H, 1) (length(x) == m == length(y)) || throw(DimensionMismatch()) if iszero(m) return dot(zero(eltype(x)), zero(eltype(H)), zero(eltype(y))) end xโ‚ = x[1] r = dot(xโ‚, H[1,1], y[1]) r += dot(x[2], H[2,1], y[1]) @inbounds for j in 2:m-1 yj = y[j] if !iszero(yj) temp = adjoint(H[1,j]) * xโ‚ @simd for i in 2:j+1 temp += adjoint(H[i,j]) * x[i] end r += dot(temp, yj) end end ym = y[m] if !iszero(ym) temp = adjoint(H[1,m]) * xโ‚ @simd for i in 2:m temp += adjoint(H[i,m]) * x[i] end r += dot(temp, ym) end return r end ###################################################################################### # Hessenberg factorizations Q(H+ฮผI)Q' of A+ฮผI: """ Hessenberg <: Factorization A `Hessenberg` object represents the Hessenberg factorization `QHQ'` of a square matrix, or a shift `Q(H+ฮผI)Q'` thereof, which is produced by the [`hessenberg`](@ref) function. """ struct Hessenberg{T,SH<:AbstractMatrix,S<:AbstractMatrix,W<:AbstractVector,V<:Number} <: Factorization{T} H::SH # UpperHessenberg or SymTridiagonal uplo::Char factors::S # reflector data in uplo triangle, may share data with H ฯ„::W # more Q (reflector) data ฮผ::V # diagonal shift for copy-free (F+ฮผI) \ b solves and similar end Hessenberg(factors::AbstractMatrix, ฯ„::AbstractVector, H::AbstractMatrix=UpperHessenberg(factors), uplo::AbstractChar='L'; ฮผ::Number=false) = Hessenberg{typeof(zero(eltype(factors))+ฮผ),typeof(H),typeof(factors),typeof(ฯ„),typeof(ฮผ)}(H, uplo, factors, ฯ„, ฮผ) Hessenberg(F::Hessenberg) = F Hessenberg(F::Hessenberg, ฮผ::Number) = Hessenberg(F.factors, F.ฯ„, F.H, F.uplo; ฮผ=ฮผ) copy(F::Hessenberg{<:Any,<:UpperHessenberg}) = Hessenberg(copy(F.factors), copy(F.ฯ„); ฮผ=F.ฮผ) copy(F::Hessenberg{<:Any,<:SymTridiagonal}) = Hessenberg(copy(F.factors), copy(F.ฯ„), copy(F.H), F.uplo; ฮผ=F.ฮผ) size(F::Hessenberg, d::Integer) = size(F.H, d) size(F::Hessenberg) = size(F.H) transpose(F::Hessenberg{<:Real}) = F' transpose(::Hessenberg) = throw(ArgumentError("transpose of Hessenberg decomposition is not supported, consider using adjoint")) # iteration for destructuring into components Base.iterate(S::Hessenberg) = (S.Q, Val(:H)) Base.iterate(S::Hessenberg, ::Val{:H}) = (S.H, Val(:ฮผ)) Base.iterate(S::Hessenberg, ::Val{:ฮผ}) = (S.ฮผ, Val(:done)) Base.iterate(S::Hessenberg, ::Val{:done}) = nothing hessenberg!(A::StridedMatrix{<:BlasFloat}) = Hessenberg(LAPACK.gehrd!(A)...) function hessenberg!(A::Union{Symmetric{<:BlasReal,<:StridedMatrix},Hermitian{<:BlasFloat,<:StridedMatrix}}) factors, ฯ„, d, e = LAPACK.hetrd!(A.uplo, A.data) return Hessenberg(factors, ฯ„, SymTridiagonal(d, e), A.uplo) end """ hessenberg!(A) -> Hessenberg `hessenberg!` is the same as [`hessenberg`](@ref), but saves space by overwriting the input `A`, instead of creating a copy. """ hessenberg!(A::AbstractMatrix) """ hessenberg(A) -> Hessenberg Compute the Hessenberg decomposition of `A` and return a `Hessenberg` object. If `F` is the factorization object, the unitary matrix can be accessed with `F.Q` (of type `LinearAlgebra.HessenbergQ`) and the Hessenberg matrix with `F.H` (of type [`UpperHessenberg`](@ref)), either of which may be converted to a regular matrix with `Matrix(F.H)` or `Matrix(F.Q)`. If `A` is [`Hermitian`](@ref) or real-[`Symmetric`](@ref), then the Hessenberg decomposition produces a real-symmetric tridiagonal matrix and `F.H` is of type [`SymTridiagonal`](@ref). Note that the shifted factorization `A+ฮผI = Q (H+ฮผI) Q'` can be constructed efficiently by `F + ฮผ*I` using the [`UniformScaling`](@ref) object [`I`](@ref), which creates a new `Hessenberg` object with shared storage and a modified shift. The shift of a given `F` is obtained by `F.ฮผ`. This is useful because multiple shifted solves `(F + ฮผ*I) \\ b` (for different `ฮผ` and/or `b`) can be performed efficiently once `F` is created. Iterating the decomposition produces the factors `F.Q, F.H, F.ฮผ`. # Examples ```jldoctest julia> A = [4. 9. 7.; 4. 4. 1.; 4. 3. 2.] 3ร—3 Matrix{Float64}: 4.0 9.0 7.0 4.0 4.0 1.0 4.0 3.0 2.0 julia> F = hessenberg(A) Hessenberg{Float64, UpperHessenberg{Float64, Matrix{Float64}}, Matrix{Float64}, Vector{Float64}, Bool} Q factor: 3ร—3 LinearAlgebra.HessenbergQ{Float64, Matrix{Float64}, Vector{Float64}, false} H factor: 3ร—3 UpperHessenberg{Float64, Matrix{Float64}}: 4.0 -11.3137 -1.41421 -5.65685 5.0 2.0 โ‹… -8.88178e-16 1.0 julia> F.Q * F.H * F.Q' 3ร—3 Matrix{Float64}: 4.0 9.0 7.0 4.0 4.0 1.0 4.0 3.0 2.0 julia> q, h = F; # destructuring via iteration julia> q == F.Q && h == F.H true ``` """ hessenberg(A::AbstractMatrix{T}) where T = hessenberg!(eigencopy_oftype(A, eigtype(T))) function show(io::IO, mime::MIME"text/plain", F::Hessenberg) summary(io, F) if !iszero(F.ฮผ) print("\nwith shift ฮผI for ฮผ = ", F.ฮผ) end print(io, "\nQ factor: ") show(io, mime, F.Q) println(io, "\nH factor:") show(io, mime, F.H) end function getproperty(F::Hessenberg, d::Symbol) d === :Q && return HessenbergQ(F) return getfield(F, d) end Base.propertynames(F::Hessenberg, private::Bool=false) = (:Q, :H, :ฮผ, (private ? (:ฯ„, :factors, :uplo) : ())...) AbstractArray(F::Hessenberg) = AbstractMatrix(F) Matrix(F::Hessenberg) = Array(AbstractArray(F)) Array(F::Hessenberg) = Matrix(F) function AbstractMatrix(F::Hessenberg) Q = F.Q A = rmul!(lmul!(Q, Matrix{eltype(Q)}(F.H)), Q') ฮผ = F.ฮผ if iszero(ฮผ) return A elseif typeof(zero(eltype(A))+ฮผ) <: eltype(A) # can shift A in-place for i = 1:size(A,1) @inbounds A[i,i] += ฮผ end return A else return A + ฮผ*I # allocate another matrix, e.g. if A is real and ฮผ is complex end end # multiply x by the entries of M in the upper-k triangle, which contains # the entries of the upper-Hessenberg matrix H for k=-1 function rmul_triu!(M::AbstractMatrix, x, k::Integer=0) require_one_based_indexing(M) m, n = size(M) for j = 1:n, i = 1:min(j-k,m) @inbounds M[i,j] *= x end return M end function lmul_triu!(x, M::AbstractMatrix, k::Integer=0) require_one_based_indexing(M) m, n = size(M) for j = 1:n, i = 1:min(j-k,m) @inbounds M[i,j] = x * M[i,j] end return M end # when H is UpperHessenberg, it shares data with F.factors # multiply Hessenberg by scalar (but don't modify lower triangle of F.H.data) rmul!(F::Hessenberg{<:Any,<:UpperHessenberg{T}}, x::T) where {T<:Number} = Hessenberg(rmul_triu!(F.factors, x, -1), F.ฯ„; ฮผ=F.ฮผ*x) lmul!(x::T, F::Hessenberg{<:Any,<:UpperHessenberg{T}}) where {T<:Number} = Hessenberg(lmul_triu!(x, F.factors, -1), F.ฯ„; ฮผ=x*F.ฮผ) rmul!(F::Hessenberg{<:Any,<:SymTridiagonal{T}}, x::T) where {T<:Number} = Hessenberg(F.factors, F.ฯ„, SymTridiagonal(F.H.dv*x, F.H.ev*x), F.uplo; ฮผ=F.ฮผ*x) lmul!(x::T, F::Hessenberg{<:Any,<:SymTridiagonal{T}}) where {T<:Number} = Hessenberg(F.factors, F.ฯ„, SymTridiagonal(x*F.H.dv, x*F.H.ev), F.uplo; ฮผ=x*F.ฮผ) # Promote F * x or x * F. In general, we don't know how to do promotions # that would change the element type of F.H, however. function (*)(F::Hessenberg{<:Any,<:AbstractMatrix{T}}, x::S) where {T,S<:Number} TS = typeof(zero(T) * x) if TS === T return rmul!(copy(F), convert(T, x)) else throw(MethodError(*, (F, x))) end end function (*)(x::S, F::Hessenberg{<:Any,<:AbstractMatrix{T}}) where {T,S<:Number} TS = typeof(zero(T) * x) if TS === T return lmul!(convert(T, x), copy(F)) else throw(MethodError(*, (x, F))) end end -(F::Hessenberg) = F * -one(eltype(F.H)) # shift Hessenberg by ฮปI +(F::Hessenberg, J::UniformScaling) = Hessenberg(F, F.ฮผ + J.ฮป) +(J::UniformScaling, F::Hessenberg) = Hessenberg(F, J.ฮป + F.ฮผ) -(F::Hessenberg, J::UniformScaling) = Hessenberg(F, F.ฮผ - J.ฮป) -(J::UniformScaling, F::Hessenberg) = Hessenberg(-F, J.ฮป - F.ฮผ) function ldiv!(F::Hessenberg, B::AbstractVecOrMat) Q = F.Q if iszero(F.ฮผ) return lmul!(Q, ldiv!(F.H, lmul!(Q', B))) else return lmul!(Q, ldiv!(F.H, lmul!(Q', B); shift=F.ฮผ)) end end function rdiv!(B::AbstractMatrix, F::Hessenberg) Q = F.Q return rmul!(rdiv!(rmul!(B, Q), F.H; shift=F.ฮผ), Q') end # handle case of real H and complex ฮผ โ€” we need to work around the # fact that we can't multiple a real F.Q by a complex matrix directly in LAPACK function ldiv!(F::Hessenberg{<:Complex,<:Any,<:AbstractMatrix{<:Real}}, B::AbstractVecOrMat{<:Complex}) Q = F.Q Br = lmul!(Q', real(B)) Bi = lmul!(Q', imag(B)) ldiv!(F.H, B .= Complex.(Br,Bi); shift=F.ฮผ) Br .= real.(B); Bi .= imag.(B) Br = lmul!(Q, Br) Bi = lmul!(Q, Bi) return B .= Complex.(Br,Bi) end function rdiv!(B::AbstractVecOrMat{<:Complex}, F::Hessenberg{<:Complex,<:Any,<:AbstractMatrix{<:Real}}) Q = F.Q Br = rmul!(real(B), Q) Bi = rmul!(imag(B), Q) rdiv!(B .= Complex.(Br,Bi), F.H; shift=F.ฮผ) Br .= real.(B); Bi .= imag.(B) Br = rmul!(Br, Q') Bi = rmul!(Bi, Q') return B .= Complex.(Br,Bi) end ldiv!(F::AdjointFactorization{<:Any,<:Hessenberg}, B::AbstractVecOrMat) = rdiv!(B', F')' det(F::Hessenberg) = det(F.H; shift=F.ฮผ) logabsdet(F::Hessenberg) = logabsdet(F.H; shift=F.ฮผ) function logdet(F::Hessenberg) d,s = logabsdet(F) return d + log(s) end z/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/abstractq.jld# This file is a part of Julia. License is MIT: https://julialang.org/license abstract type AbstractQ{T} end struct AdjointQ{T,S<:AbstractQ{T}} <: AbstractQ{T} Q::S end parent(adjQ::AdjointQ) = adjQ.Q eltype(::Type{<:AbstractQ{T}}) where {T} = T Base.eltypeof(Q::AbstractQ) = eltype(Q) ndims(::AbstractQ) = 2 # inversion/adjoint/transpose inv(Q::AbstractQ) = Q' adjoint(Q::AbstractQ) = AdjointQ(Q) transpose(Q::AbstractQ{<:Real}) = AdjointQ(Q) transpose(Q::AbstractQ) = error("transpose not implemented for $(typeof(Q)). Consider using adjoint instead of transpose.") adjoint(adjQ::AdjointQ) = adjQ.Q # promotion with AbstractMatrix, at least for equal eltypes promote_rule(::Type{<:AbstractMatrix{T}}, ::Type{<:AbstractQ{T}}) where {T} = (@inline; Union{AbstractMatrix{T},AbstractQ{T}}) # conversion # the following eltype promotion should be defined for each subtype `QType` # convert(::Type{AbstractQ{T}}, Q::QType) where {T} = QType{T}(Q) # and then care has to be taken that # QType{T}(Q::QType{T}) where T = ... # is implemented as a no-op # the following conversion method ensures functionality when the above method is not defined # (as for HessenbergQ), but no eltype conversion is required either (say, in multiplication) convert(::Type{AbstractQ{T}}, Q::AbstractQ{T}) where {T} = Q convert(::Type{AbstractQ{T}}, adjQ::AdjointQ{T}) where {T} = adjQ convert(::Type{AbstractQ{T}}, adjQ::AdjointQ) where {T} = convert(AbstractQ{T}, adjQ.Q)' # ... to matrix collect(Q::AbstractQ) = copyto!(Matrix{eltype(Q)}(undef, size(Q)), Q) Matrix{T}(Q::AbstractQ) where {T} = convert(Matrix{T}, Q*I) # generic fallback, yields square matrix Matrix{T}(adjQ::AdjointQ{S}) where {T,S} = convert(Matrix{T}, lmul!(adjQ, Matrix{S}(I, size(adjQ)))) Matrix(Q::AbstractQ{T}) where {T} = Matrix{T}(Q) Array{T}(Q::AbstractQ) where {T} = Matrix{T}(Q) Array(Q::AbstractQ) = Matrix(Q) convert(::Type{T}, Q::AbstractQ) where {T<:AbstractArray} = T(Q) # legacy @deprecate(convert(::Type{AbstractMatrix{T}}, Q::AbstractQ) where {T}, convert(LinearAlgebra.AbstractQ{T}, Q)) function size(Q::AbstractQ, dim::Integer) if dim < 1 throw(BoundsError()) elseif dim <= 2 # && 1 <= dim return size(Q)[dim] else # 2 < dim return 1 end end size(adjQ::AdjointQ) = reverse(size(adjQ.Q)) # comparison (==)(Q::AbstractQ, A::AbstractMatrix) = lmul!(Q, Matrix{eltype(Q)}(I, size(A))) == A (==)(A::AbstractMatrix, Q::AbstractQ) = Q == A (==)(Q::AbstractQ, P::AbstractQ) = Matrix(Q) == Matrix(P) isapprox(Q::AbstractQ, A::AbstractMatrix; kwargs...) = isapprox(lmul!(Q, Matrix{eltype(Q)}(I, size(A))), A, kwargs...) isapprox(A::AbstractMatrix, Q::AbstractQ; kwargs...) = isapprox(Q, A, kwargs...) isapprox(Q::AbstractQ, P::AbstractQ; kwargs...) = isapprox(Matrix(Q), Matrix(P), kwargs...) # pseudo-array behaviour, required for indexing with `begin` or `end` axes(Q::AbstractQ) = map(Base.oneto, size(Q)) axes(Q::AbstractQ, d::Integer) = d in (1, 2) ? axes(Q)[d] : Base.OneTo(1) copymutable(Q::AbstractQ{T}) where {T} = lmul!(Q, Matrix{T}(I, size(Q))) copy(Q::AbstractQ) = copymutable(Q) # getindex @inline function getindex(Q::AbstractQ, inds...) @boundscheck Base.checkbounds_indices(Bool, axes(Q), inds) || Base.throw_boundserror(Q, inds) return _getindex(Q, inds...) end @inline getindex(Q::AbstractQ, ::Colon) = copymutable(Q)[:] @inline getindex(Q::AbstractQ, ::Colon, ::Colon) = copy(Q) @inline _getindex(Q::AbstractQ, inds...) = @inbounds copymutable(Q)[inds...] @inline function _getindex(Q::AbstractQ, ::Colon, J::AbstractVector{<:Integer}) Y = zeros(eltype(Q), size(Q, 2), length(J)) @inbounds for (i,j) in enumerate(J) Y[j,i] = oneunit(eltype(Q)) end lmul!(Q, Y) end @inline _getindex(Q::AbstractQ, I::AbstractVector{Int}, J::AbstractVector{Int}) = @inbounds Q[:,J][I,:] @inline function _getindex(Q::AbstractQ, ::Colon, j::Int) y = zeros(eltype(Q), size(Q, 2)) y[j] = oneunit(eltype(Q)) lmul!(Q, y) end @inline _getindex(Q::AbstractQ, i::Int, j::Int) = @inbounds Q[:,j][i] # needed because AbstractQ does not subtype AbstractMatrix qr(Q::AbstractQ{T}, arg...; kwargs...) where {T} = qr!(Matrix{_qreltype(T)}(Q), arg...; kwargs...) lq(Q::AbstractQ{T}, arg...; kwargs...) where {T} = lq!(Matrix{lq_eltype(T)}(Q), arg...; kwargs...) hessenberg(Q::AbstractQ{T}) where {T} = hessenberg!(Matrix{eigtype(T)}(Q)) # needed when used interchangeably with AbstractMatrix (analogous to views of ranges) view(A::AbstractQ, I...) = getindex(A, I...) # specialization avoiding the fallback using slow `getindex` function copyto!(dest::AbstractMatrix, src::AbstractQ) copyto!(dest, I) lmul!(src, dest) end # needed to resolve method ambiguities function copyto!(dest::PermutedDimsArray{T,2,perm}, src::AbstractQ) where {T,perm} if perm == (1, 2) copyto!(parent(dest), src) else @assert perm == (2, 1) # there are no other permutations of two indices if T <: Real copyto!(parent(dest), I) lmul!(src', parent(dest)) else # LAPACK does not offer inplace lmul!(transpose(Q), B) for complex Q tmp = similar(parent(dest)) copyto!(tmp, I) rmul!(tmp, src) permutedims!(parent(dest), tmp, (2, 1)) end end return dest end # used in concatenations: Base.__cat_offset1! Base._copy_or_fill!(A, inds, Q::AbstractQ) = (A[inds...] = collect(Q)) # overloads of helper functions Base.cat_size(A::AbstractQ) = size(A) Base.cat_size(A::AbstractQ, d) = size(A, d) Base.cat_length(a::AbstractQ) = prod(size(a)) Base.cat_ndims(a::AbstractQ) = ndims(a) Base.cat_indices(A::AbstractQ, d) = axes(A, d) Base.cat_similar(A::AbstractQ, T::Type, shape::Tuple) = Array{T}(undef, shape) Base.cat_similar(A::AbstractQ, T::Type, shape::Vector) = Array{T}(undef, shape...) function show(io::IO, ::MIME{Symbol("text/plain")}, Q::AbstractQ) print(io, Base.dims2string(size(Q)), ' ', summary(Q)) end # multiplication # generically, treat AbstractQ like a matrix with its definite size qsize_check(Q::AbstractQ, B::AbstractVecOrMat) = size(Q, 2) == size(B, 1) || throw(DimensionMismatch("second dimension of Q, $(size(Q,2)), must coincide with first dimension of B, $(size(B,1))")) qsize_check(A::AbstractVecOrMat, Q::AbstractQ) = size(A, 2) == size(Q, 1) || throw(DimensionMismatch("second dimension of A, $(size(A,2)), must coincide with first dimension of Q, $(size(Q,1))")) qsize_check(Q::AbstractQ, P::AbstractQ) = size(Q, 2) == size(P, 1) || throw(DimensionMismatch("second dimension of A, $(size(Q,2)), must coincide with first dimension of B, $(size(P,1))")) # mimic the AbstractArray fallback *(Q::AbstractQ{<:Number}) = Q (*)(Q::AbstractQ, J::UniformScaling) = Q*J.ฮป function (*)(Q::AbstractQ, b::Number) T = promote_type(eltype(Q), typeof(b)) lmul!(convert(AbstractQ{T}, Q), Matrix{T}(b*I, size(Q))) end function (*)(Q::AbstractQ, B::AbstractVector) T = promote_type(eltype(Q), eltype(B)) qsize_check(Q, B) mul!(similar(B, T, size(Q, 1)), convert(AbstractQ{T}, Q), B) end function (*)(Q::AbstractQ, B::AbstractMatrix) T = promote_type(eltype(Q), eltype(B)) qsize_check(Q, B) mul!(similar(B, T, (size(Q, 1), size(B, 2))), convert(AbstractQ{T}, Q), B) end (*)(J::UniformScaling, Q::AbstractQ) = J.ฮป*Q function (*)(a::Number, Q::AbstractQ) T = promote_type(typeof(a), eltype(Q)) rmul!(Matrix{T}(a*I, size(Q)), convert(AbstractQ{T}, Q)) end function (*)(A::AbstractVector, Q::AbstractQ) T = promote_type(eltype(A), eltype(Q)) qsize_check(A, Q) return mul!(similar(A, T, length(A)), A, convert(AbstractQ{T}, Q)) end function (*)(A::AbstractMatrix, Q::AbstractQ) T = promote_type(eltype(A), eltype(Q)) qsize_check(A, Q) return mul!(similar(A, T, (size(A, 1), size(Q, 2))), A, convert(AbstractQ{T}, Q)) end (*)(u::AdjointAbsVec, Q::AbstractQ) = (Q'u')' ### Q*Q (including adjoints) (*)(Q::AbstractQ, P::AbstractQ) = Q * (P*I) ### mul! function mul!(C::AbstractVecOrMat{T}, Q::AbstractQ{T}, B::Union{AbstractVecOrMat,AbstractQ}) where {T} require_one_based_indexing(C, B) mB, nB = size(B, 1), size(B, 2) mC, nC = size(C, 1), size(C, 2) qsize_check(Q, B) nB != nC && throw(DimensionMismatch()) if mB < mC inds = CartesianIndices(axes(B)) copyto!(view(C, inds), B) C[CartesianIndices((mB+1:mC, axes(C, 2)))] .= zero(T) return lmul!(Q, C) else return lmul!(Q, copyto!(C, B)) end end function mul!(C::AbstractVecOrMat{T}, A::AbstractVecOrMat, Q::AbstractQ{T}) where {T} require_one_based_indexing(C, A) mA, nA = size(A, 1), size(A, 2) mC, nC = size(C, 1), size(C, 2) mA != mC && throw(DimensionMismatch()) qsize_check(A, Q) if nA < nC inds = CartesianIndices(axes(A)) copyto!(view(C, inds), A) C[CartesianIndices((axes(C, 1), nA+1:nC))] .= zero(T) return rmul!(C, Q) else return rmul!(copyto!(C, A), Q) end end ### division \(Q::AbstractQ, A::AbstractVecOrMat) = Q'*A /(A::AbstractVecOrMat, Q::AbstractQ) = A*Q' ldiv!(Q::AbstractQ, A::AbstractVecOrMat) = lmul!(Q', A) ldiv!(C::AbstractVecOrMat, Q::AbstractQ, A::AbstractVecOrMat) = mul!(C, Q', A) rdiv!(A::AbstractVecOrMat, Q::AbstractQ) = rmul!(A, Q') logabsdet(Q::AbstractQ) = (d = det(Q); return log(abs(d)), sign(d)) function logdet(A::AbstractQ) d, s = logabsdet(A) return d + log(s) end ########################################################### ################ Q from QR decompositions ################# ########################################################### """ QRPackedQ <: LinearAlgebra.AbstractQ The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or [`QRPivoted`](@ref) format. """ struct QRPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} factors::S ฯ„::C function QRPackedQ{T,S,C}(factors, ฯ„) where {T,S<:AbstractMatrix{T},C<:AbstractVector{T}} require_one_based_indexing(factors, ฯ„) new{T,S,C}(factors, ฯ„) end end QRPackedQ(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T} = QRPackedQ{T,typeof(factors),typeof(ฯ„)}(factors, ฯ„) QRPackedQ{T}(factors::AbstractMatrix, ฯ„::AbstractVector) where {T} = QRPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, ฯ„)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(QRPackedQ{T,S}(factors::AbstractMatrix{T}, ฯ„::AbstractVector{T}) where {T,S}, QRPackedQ{T,S,typeof(ฯ„)}(factors, ฯ„), false) """ QRCompactWYQ <: LinearAlgebra.AbstractQ The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref) format. """ struct QRCompactWYQ{S, M<:AbstractMatrix{S}, C<:AbstractMatrix{S}} <: AbstractQ{S} factors::M T::C function QRCompactWYQ{S,M,C}(factors, T) where {S,M<:AbstractMatrix{S},C<:AbstractMatrix{S}} require_one_based_indexing(factors, T) new{S,M,C}(factors, T) end end QRCompactWYQ(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = QRCompactWYQ{S,typeof(factors),typeof(T)}(factors, T) QRCompactWYQ{S}(factors::AbstractMatrix, T::AbstractMatrix) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, factors), convert(AbstractMatrix{S}, T)) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M}, QRCompactWYQ{S,M,typeof(T)}(factors, T), false) QRPackedQ{T}(Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.ฯ„)) QRCompactWYQ{S}(Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) # override generic square fallback Matrix{T}(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {T,S} = convert(Matrix{T}, lmul!(Q, Matrix{S}(I, size(Q, 1), min(size(Q.factors)...)))) Matrix(Q::Union{QRCompactWYQ{S},QRPackedQ{S}}) where {S} = Matrix{S}(Q) convert(::Type{AbstractQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ{T}(Q) convert(::Type{AbstractQ{T}}, Q::QRCompactWYQ) where {T} = QRCompactWYQ{T}(Q) size(Q::Union{QRCompactWYQ,QRPackedQ}, dim::Integer) = size(Q.factors, dim == 2 ? 1 : dim) size(Q::Union{QRCompactWYQ,QRPackedQ}) = (n = size(Q.factors, 1); (n, n)) ## Multiplication ### QB lmul!(A::QRCompactWYQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.gemqrt!('L', 'N', A.factors, A.T, B) lmul!(A::QRPackedQ{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormqr!('L', 'N', A.factors, A.ฯ„, B) function lmul!(A::QRPackedQ, B::AbstractVecOrMat) require_one_based_indexing(B) mA, nA = size(A.factors) mB, nB = size(B,1), size(B,2) if mA != mB throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) end Afactors = A.factors @inbounds begin for k = min(mA,nA):-1:1 for j = 1:nB vBj = B[k,j] for i = k+1:mB vBj += conj(Afactors[i,k])*B[i,j] end vBj = A.ฯ„[k]*vBj B[k,j] -= vBj for i = k+1:mB B[i,j] -= Afactors[i,k]*vBj end end end end B end ### QcB lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = (Q = adjQ.Q; LAPACK.gemqrt!('L', 'T', Q.factors, Q.T, B)) lmul!(adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = (Q = adjQ.Q; LAPACK.gemqrt!('L', 'C', Q.factors, Q.T, B)) lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = (Q = adjQ.Q; LAPACK.ormqr!('L', 'T', Q.factors, Q.ฯ„, B)) lmul!(adjQ::AdjointQ{<:Any,<:QRPackedQ{T,<:StridedMatrix}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = (Q = adjQ.Q; LAPACK.ormqr!('L', 'C', Q.factors, Q.ฯ„, B)) function lmul!(adjA::AdjointQ{<:Any,<:QRPackedQ}, B::AbstractVecOrMat) require_one_based_indexing(B) A = adjA.Q mA, nA = size(A.factors) mB, nB = size(B,1), size(B,2) if mA != mB throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)")) end Afactors = A.factors @inbounds begin for k = 1:min(mA,nA) for j = 1:nB vBj = B[k,j] for i = k+1:mB vBj += conj(Afactors[i,k])*B[i,j] end vBj = conj(A.ฯ„[k])*vBj B[k,j] -= vBj for i = k+1:mB B[i,j] -= Afactors[i,k]*vBj end end end end B end ### AQ rmul!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T,<:StridedMatrix}) where {T<:BlasFloat} = LAPACK.gemqrt!('R', 'N', B.factors, B.T, A) rmul!(A::StridedVecOrMat{T}, B::QRPackedQ{T,<:StridedMatrix}) where {T<:BlasFloat} = LAPACK.ormqr!('R', 'N', B.factors, B.ฯ„, A) function rmul!(A::AbstractVecOrMat, Q::QRPackedQ) require_one_based_indexing(A) mQ, nQ = size(Q.factors) mA, nA = size(A,1), size(A,2) if nA != mQ throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) end Qfactors = Q.factors @inbounds begin for k = 1:min(mQ,nQ) for i = 1:mA vAi = A[i,k] for j = k+1:mQ vAi += A[i,j]*Qfactors[j,k] end vAi = vAi*Q.ฯ„[k] A[i,k] -= vAi for j = k+1:nA A[i,j] -= vAi*conj(Qfactors[j,k]) end end end end A end ### AQc rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasReal} = (Q = adjQ.Q; LAPACK.gemqrt!('R', 'T', Q.factors, Q.T, A)) rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRCompactWYQ{T}}) where {T<:BlasComplex} = (Q = adjQ.Q; LAPACK.gemqrt!('R', 'C', Q.factors, Q.T, A)) rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasReal} = (Q = adjQ.Q; LAPACK.ormqr!('R', 'T', Q.factors, Q.ฯ„, A)) rmul!(A::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:QRPackedQ{T}}) where {T<:BlasComplex} = (Q = adjQ.Q; LAPACK.ormqr!('R', 'C', Q.factors, Q.ฯ„, A)) function rmul!(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:QRPackedQ}) require_one_based_indexing(A) Q = adjQ.Q mQ, nQ = size(Q.factors) mA, nA = size(A,1), size(A,2) if nA != mQ throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)")) end Qfactors = Q.factors @inbounds begin for k = min(mQ,nQ):-1:1 for i = 1:mA vAi = A[i,k] for j = k+1:mQ vAi += A[i,j]*Qfactors[j,k] end vAi = vAi*conj(Q.ฯ„[k]) A[i,k] -= vAi for j = k+1:nA A[i,j] -= vAi*conj(Qfactors[j,k]) end end end end A end det(Q::QRPackedQ) = _det_tau(Q.ฯ„) det(Q::QRCompactWYQ) = prod(i -> _det_tau(_diagview(Q.T[:, i:min(i + size(Q.T, 1), size(Q.T, 2))])), 1:size(Q.T, 1):size(Q.T, 2)) _diagview(A) = @view A[diagind(A)] # Compute `det` from the number of Householder reflections. Handle # the case `Q.ฯ„` contains zeros. _det_tau(ฯ„s::AbstractVector{<:Real}) = isodd(count(!iszero, ฯ„s)) ? -one(eltype(ฯ„s)) : one(eltype(ฯ„s)) # In complex case, we need to compute the non-unit eigenvalue `ฮป = 1 - c*ฯ„` # (where `c = v'v`) of each Householder reflector. As we know that the # reflector must have the determinant of 1, it must satisfy `abs2(ฮป) == 1`. # Combining this with the constraint `c > 0`, it turns out that the eigenvalue # (hence the determinant) can be computed as `ฮป = -sign(ฯ„)^2`. # See: https://github.com/JuliaLang/julia/pull/32887#issuecomment-521935716 _det_tau(ฯ„s) = prod(ฯ„ -> iszero(ฯ„) ? one(ฯ„) : -sign(ฯ„)^2, ฯ„s) ########################################################### ######## Q from Hessenberg decomposition ################## ########################################################### """ HessenbergQ <: AbstractQ Given a [`Hessenberg`](@ref) factorization object `F`, `F.Q` returns a `HessenbergQ` object, which is an implicit representation of the unitary matrix `Q` in the Hessenberg factorization `QHQ'` represented by `F`. This `F.Q` object can be efficiently multiplied by matrices or vectors, and can be converted to an ordinary matrix type with `Matrix(F.Q)`. """ struct HessenbergQ{T,S<:AbstractMatrix,W<:AbstractVector,sym} <: AbstractQ{T} uplo::Char factors::S ฯ„::W function HessenbergQ{T,S,W,sym}(uplo::AbstractChar, factors, ฯ„) where {T,S<:AbstractMatrix,W<:AbstractVector,sym} new(uplo, factors, ฯ„) end end HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,false}(F.uplo, F.factors, F.ฯ„) HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.ฯ„) size(Q::HessenbergQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim) size(Q::HessenbergQ) = size(Q, 1), size(Q, 2) # HessenbergQ from LAPACK/BLAS (as opposed to Julia libraries like GenericLinearAlgebra) const BlasHessenbergQ{T,sym} = HessenbergQ{T,<:StridedMatrix{T},<:StridedVector{T},sym} where {T<:BlasFloat,sym} ## reconstruct the original matrix Matrix{T}(Q::BlasHessenbergQ{<:Any,false}) where {T} = convert(Matrix{T}, LAPACK.orghr!(1, size(Q.factors, 1), copy(Q.factors), Q.ฯ„)) Matrix{T}(Q::BlasHessenbergQ{<:Any,true}) where {T} = convert(Matrix{T}, LAPACK.orgtr!(Q.uplo, copy(Q.factors), Q.ฯ„)) lmul!(Q::BlasHessenbergQ{T,false}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.ฯ„, X) rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,false}) where {T<:BlasFloat} = LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.ฯ„, X) lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = (Q = adjQ.Q; LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.ฯ„, X)) rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,false}}) where {T<:BlasFloat} = (Q = adjQ.Q; LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.ฯ„, X)) lmul!(Q::BlasHessenbergQ{T,true}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormtr!('L', Q.uplo, 'N', Q.factors, Q.ฯ„, X) rmul!(X::StridedVecOrMat{T}, Q::BlasHessenbergQ{T,true}) where {T<:BlasFloat} = LAPACK.ormtr!('R', Q.uplo, 'N', Q.factors, Q.ฯ„, X) lmul!(adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}, X::StridedVecOrMat{T}) where {T<:BlasFloat} = (Q = adjQ.Q; LAPACK.ormtr!('L', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.ฯ„, X)) rmul!(X::StridedVecOrMat{T}, adjQ::AdjointQ{<:Any,<:BlasHessenbergQ{T,true}}) where {T<:BlasFloat} = (Q = adjQ.Q; LAPACK.ormtr!('R', Q.uplo, ifelse(T<:Real, 'T', 'C'), Q.factors, Q.ฯ„, X)) lmul!(Q::HessenbergQ{T}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', Q')' rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, Q::HessenbergQ{T}) where {T} = lmul!(Q', X')' lmul!(adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}, X::Adjoint{T,<:StridedVecOrMat{T}}) where {T} = rmul!(X', adjQ')' rmul!(X::Adjoint{T,<:StridedVecOrMat{T}}, adjQ::AdjointQ{<:Any,<:HessenbergQ{T}}) where {T} = lmul!(adjQ', X')' # flexible left-multiplication (and adjoint right-multiplication) qsize_check(Q::Union{QRPackedQ,QRCompactWYQ,HessenbergQ}, B::AbstractVecOrMat) = size(B, 1) in size(Q.factors) || throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of Q, $(size(Q.factors))")) qsize_check(A::AbstractVecOrMat, adjQ::AdjointQ{<:Any,<:Union{QRPackedQ,QRCompactWYQ,HessenbergQ}}) = (Q = adjQ.Q; size(A, 2) in size(Q.factors) || throw(DimensionMismatch("second dimension of A, $(size(A,2)), must equal one of the dimensions of Q, $(size(Q.factors))"))) det(Q::HessenbergQ) = _det_tau(Q.ฯ„) ########################################################### ################ Q from LQ decomposition ################## ########################################################### struct LQPackedQ{T,S<:AbstractMatrix{T},C<:AbstractVector{T}} <: AbstractQ{T} factors::S ฯ„::C end LQPackedQ{T}(Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.ฯ„)) @deprecate(AbstractMatrix{T}(Q::LQPackedQ) where {T}, convert(AbstractQ{T}, Q), false) Matrix{T}(A::LQPackedQ) where {T} = convert(Matrix{T}, LAPACK.orglq!(copy(A.factors), A.ฯ„)) convert(::Type{AbstractQ{T}}, Q::LQPackedQ) where {T} = LQPackedQ{T}(Q) # size(Q::LQPackedQ) yields the shape of Q's square form size(Q::LQPackedQ) = (n = size(Q.factors, 2); return n, n) ## Multiplication # out-of-place right application of LQPackedQs # # these methods: (1) check whether the applied-to matrix's (A's) appropriate dimension # (columns for A_*, rows for Ac_*) matches the number of columns (nQ) of the LQPackedQ (Q), # and if so effectively apply Q's square form to A without additional shenanigans; and # (2) if the preceding dimensions do not match, check whether the appropriate dimension of # A instead matches the number of rows of the matrix of which Q is a factor (i.e. # size(Q.factors, 1)), and if so implicitly apply Q's truncated form to A by zero extending # A as necessary for check (1) to pass (if possible) and then applying Q's square form qsize_check(adjQ::AdjointQ{<:Any,<:LQPackedQ}, B::AbstractVecOrMat) = size(B, 1) in size(adjQ.Q.factors) || throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of Q, $(size(adjQ.Q.factors))")) qsize_check(A::AbstractVecOrMat, Q::LQPackedQ) = size(A, 2) in size(Q.factors) || throw(DimensionMismatch("second dimension of A, $(size(A,2)), must equal one of the dimensions of Q, $(size(Q.factors))")) # in-place right-application of LQPackedQs # these methods require that the applied-to matrix's (A's) number of columns # match the number of columns (nQ) of the LQPackedQ (Q) (necessary for in-place # operation, and the underlying LAPACK routine (ormlq) treats the implicit Q # as its (nQ-by-nQ) square form) rmul!(A::StridedVecOrMat{T}, B::LQPackedQ{T}) where {T<:BlasFloat} = LAPACK.ormlq!('R', 'N', B.factors, B.ฯ„, A) rmul!(A::StridedVecOrMat{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasReal} = (B = adjB.Q; LAPACK.ormlq!('R', 'T', B.factors, B.ฯ„, A)) rmul!(A::StridedVecOrMat{T}, adjB::AdjointQ{<:Any,<:LQPackedQ{T}}) where {T<:BlasComplex} = (B = adjB.Q; LAPACK.ormlq!('R', 'C', B.factors, B.ฯ„, A)) ### QB / QcB lmul!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.ฯ„,B) lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasReal} = (A = adjA.Q; LAPACK.ormlq!('L', 'T', A.factors, A.ฯ„, B)) lmul!(adjA::AdjointQ{<:Any,<:LQPackedQ{T}}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = (A = adjA.Q; LAPACK.ormlq!('L', 'C', A.factors, A.ฯ„, B)) # In LQ factorization, `Q` is expressed as the product of the adjoint of the # reflectors. Thus, `det` has to be conjugated. det(Q::LQPackedQ) = conj(_det_tau(Q.ฯ„)) w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/givens.jlp1# This file is a part of Julia. License is MIT: https://julialang.org/license # givensAlgorithm functions are derived from LAPACK, see below abstract type AbstractRotation{T} end struct AdjointRotation{T,S<:AbstractRotation{T}} <: AbstractRotation{T} R::S end transpose(R::AbstractRotation) = error("transpose not implemented for $(typeof(R)). Consider using adjoint instead of transpose.") (*)(R::AbstractRotation, A::AbstractVector) = _rot_mul_vecormat(R, A) (*)(R::AbstractRotation, A::AbstractMatrix) = _rot_mul_vecormat(R, A) function _rot_mul_vecormat(R::AbstractRotation{T}, A::AbstractVecOrMat{S}) where {T,S} TS = typeof(zero(T)*zero(S) + zero(T)*zero(S)) lmul!(convert(AbstractRotation{TS}, R), copy_similar(A, TS)) end (*)(A::AbstractVector, R::AbstractRotation) = _vecormat_mul_rot(A, R) (*)(A::AbstractMatrix, R::AbstractRotation) = _vecormat_mul_rot(A, R) function _vecormat_mul_rot(A::AbstractVecOrMat{T}, R::AbstractRotation{S}) where {T,S} TS = typeof(zero(T)*zero(S) + zero(T)*zero(S)) rmul!(copy_similar(A, TS), convert(AbstractRotation{TS}, R)) end """ LinearAlgebra.Givens(i1,i2,c,s) -> G A Givens rotation linear operator. The fields `c` and `s` represent the cosine and sine of the rotation angle, respectively. The `Givens` type supports left multiplication `G*A` and conjugated transpose right multiplication `A*G'`. The type doesn't have a `size` and can therefore be multiplied with matrices of arbitrary size as long as `i2<=size(A,2)` for `G*A` or `i2<=size(A,1)` for `A*G'`. See also [`givens`](@ref). """ struct Givens{T} <: AbstractRotation{T} i1::Int i2::Int c::T s::T end struct Rotation{T} <: AbstractRotation{T} rotations::Vector{Givens{T}} end convert(::Type{T}, r::T) where {T<:AbstractRotation} = r convert(::Type{T}, r::AbstractRotation) where {T<:AbstractRotation} = T(r)::T convert(::Type{AbstractRotation{T}}, r::AdjointRotation) where {T} = convert(AbstractRotation{T}, r.R)' convert(::Type{AbstractRotation{T}}, r::AdjointRotation{T}) where {T} = r Givens(i1, i2, c, s) = Givens(i1, i2, promote(c, s)...) Givens{T}(G::Givens{T}) where {T} = G Givens{T}(G::Givens) where {T} = Givens(G.i1, G.i2, convert(T, G.c), convert(T, G.s)) Rotation{T}(R::Rotation{T}) where {T} = R Rotation{T}(R::Rotation) where {T} = Rotation{T}([Givens{T}(g) for g in R.rotations]) AbstractRotation{T}(G::Givens) where {T} = Givens{T}(G) AbstractRotation{T}(R::Rotation) where {T} = Rotation{T}(R) adjoint(G::Givens) = Givens(G.i1, G.i2, G.c', -G.s) adjoint(R::AbstractRotation) = AdjointRotation(R) adjoint(adjR::AdjointRotation) = adjR.R Base.copy(aR::AdjointRotation{T,Rotation{T}}) where {T} = Rotation{T}([r' for r in Iterators.reverse(aR.R.rotations)]) floatmin2(::Type{Float32}) = reinterpret(Float32, 0x26000000) floatmin2(::Type{Float64}) = reinterpret(Float64, 0x21a0000000000000) floatmin2(::Type{T}) where {T} = (twopar = 2one(T); twopar^trunc(Integer,log(floatmin(T)/eps(T))/log(twopar)/twopar)) # derived from LAPACK's dlartg # Copyright: # Univ. of Tennessee # Univ. of California Berkeley # Univ. of Colorado Denver # NAG Ltd. function givensAlgorithm(f::T, g::T) where T<:AbstractFloat onepar = one(T) twopar = 2one(T) T0 = typeof(onepar) # dimensionless zeropar = T0(zero(T)) # must be dimensionless # need both dimensionful and dimensionless versions of these: safmn2 = floatmin2(T0) safmn2u = floatmin2(T) safmx2 = one(T)/safmn2 safmx2u = oneunit(T)/safmn2 if g == 0 cs = onepar sn = zeropar r = f elseif f == 0 cs = zeropar sn = onepar r = g else f1 = f g1 = g scalepar = max(abs(f1), abs(g1)) if scalepar >= safmx2u count = 0 while true count += 1 f1 *= safmn2 g1 *= safmn2 scalepar = max(abs(f1), abs(g1)) if scalepar < safmx2u break end end r = sqrt(f1*f1 + g1*g1) cs = f1/r sn = g1/r for i = 1:count r *= safmx2 end elseif scalepar <= safmn2u count = 0 while true count += 1 f1 *= safmx2 g1 *= safmx2 scalepar = max(abs(f1), abs(g1)) if scalepar > safmn2u break end end r = sqrt(f1*f1 + g1*g1) cs = f1/r sn = g1/r for i = 1:count r *= safmn2 end else r = sqrt(f1*f1 + g1*g1) cs = f1/r sn = g1/r end if abs(f) > abs(g) && cs < 0 cs = -cs sn = -sn r = -r end end return cs, sn, r end # derived from LAPACK's zlartg # Copyright: # Univ. of Tennessee # Univ. of California Berkeley # Univ. of Colorado Denver # NAG Ltd. function givensAlgorithm(f::Complex{T}, g::Complex{T}) where T<:AbstractFloat twopar, onepar = 2one(T), one(T) T0 = typeof(onepar) # dimensionless zeropar = T0(zero(T)) # must be dimensionless czero = complex(zeropar) abs1(ff) = max(abs(real(ff)), abs(imag(ff))) safmin = floatmin(T0) safmn2 = floatmin2(T0) safmn2u = floatmin2(T) safmx2 = one(T)/safmn2 safmx2u = oneunit(T)/safmn2 scalepar = max(abs1(f), abs1(g)) fs = f gs = g count = 0 if scalepar >= safmx2u while true count += 1 fs *= safmn2 gs *= safmn2 scalepar *= safmn2 if scalepar < safmx2u break end end elseif scalepar <= safmn2u if g == 0 cs = onepar sn = czero r = f return cs, sn, r end while true count -= 1 fs *= safmx2 gs *= safmx2 scalepar *= safmx2 if scalepar > safmn2u break end end end f2 = abs2(fs) g2 = abs2(gs) if f2 <= max(g2, oneunit(T))*safmin # This is a rare case: F is very small. if f == 0 cs = zero(T) r = complex(hypot(real(g), imag(g))) # do complex/real division explicitly with two real divisions d = hypot(real(gs), imag(gs)) sn = complex(real(gs)/d, -imag(gs)/d) return cs, sn, r end f2s = hypot(real(fs), imag(fs)) # g2 and g2s are accurate # g2 is at least safmin, and g2s is at least safmn2 g2s = sqrt(g2) # error in cs from underflow in f2s is at most # unfl / safmn2 .lt. sqrt(unfl*eps) .lt. eps # if max(g2,one)=g2, then f2 .lt. g2*safmin, # and so cs .lt. sqrt(safmin) # if max(g2,one)=one, then f2 .lt. safmin # and so cs .lt. sqrt(safmin)/safmn2 = sqrt(eps) # therefore, cs = f2s/g2s / sqrt( 1 + (f2s/g2s)**2 ) = f2s/g2s cs = f2s/g2s # make sure abs(ff) = 1 # do complex/real division explicitly with 2 real divisions if abs1(f) > 1 d = hypot(real(f), imag(f)) ff = complex(real(f)/d, imag(f)/d) else dr = safmx2*real(f) di = safmx2*imag(f) d = hypot(dr, di) ff = complex(dr/d, di/d) end sn = ff*complex(real(gs)/g2s, -imag(gs)/g2s) r = cs*f + sn*g else # This is the most common case. # Neither F2 nor F2/G2 are less than SAFMIN # F2S cannot overflow, and it is accurate f2s = sqrt(onepar + g2/f2) # do the f2s(real)*fs(complex) multiply with two real multiplies r = complex(f2s*real(fs), f2s*imag(fs)) cs = onepar/f2s d = f2 + g2 # do complex/real division explicitly with two real divisions sn = complex(real(r)/d, imag(r)/d) sn *= conj(gs) if count != 0 if count > 0 for i = 1:count r *= safmx2 end else for i = 1:-count r *= safmn2 end end end end return cs, sn, r end # enable for unitful quantities function givensAlgorithm(f::T, g::T) where T fs = f / oneunit(T) gs = g / oneunit(T) typeof(fs) === T && typeof(gs) === T && !isa(fs, Union{AbstractFloat,Complex{<:AbstractFloat}}) && throw(MethodError(givensAlgorithm, (fs, gs))) c, s, r = givensAlgorithm(fs, gs) return c, s, r * oneunit(T) end givensAlgorithm(f, g) = givensAlgorithm(promote(float(f), float(g))...) """ givens(f::T, g::T, i1::Integer, i2::Integer) where {T} -> (G::Givens, r::T) Computes the Givens rotation `G` and scalar `r` such that for any vector `x` where ``` x[i1] = f x[i2] = g ``` the result of the multiplication ``` y = G*x ``` has the property that ``` y[i1] = r y[i2] = 0 ``` See also [`LinearAlgebra.Givens`](@ref). """ function givens(f::T, g::T, i1::Integer, i2::Integer) where T if i1 == i2 throw(ArgumentError("Indices must be distinct.")) end c, s, r = givensAlgorithm(f, g) if i1 > i2 s = -conj(s) i1, i2 = i2, i1 end Givens(i1, i2, c, s), r end """ givens(A::AbstractArray, i1::Integer, i2::Integer, j::Integer) -> (G::Givens, r) Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication ``` B = G*A ``` has the property that ``` B[i1,j] = r B[i2,j] = 0 ``` See also [`LinearAlgebra.Givens`](@ref). """ givens(A::AbstractMatrix, i1::Integer, i2::Integer, j::Integer) = givens(A[i1,j], A[i2,j], i1, i2) """ givens(x::AbstractVector, i1::Integer, i2::Integer) -> (G::Givens, r) Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication ``` B = G*x ``` has the property that ``` B[i1] = r B[i2] = 0 ``` See also [`LinearAlgebra.Givens`](@ref). """ givens(x::AbstractVector, i1::Integer, i2::Integer) = givens(x[i1], x[i2], i1, i2) function getindex(G::Givens, i::Integer, j::Integer) if i == j if i == G.i1 || i == G.i2 G.c else oneunit(G.c) end elseif i == G.i1 && j == G.i2 G.s elseif i == G.i2 && j == G.i1 -conj(G.s) else zero(G.s) end end @inline function lmul!(G::Givens, A::AbstractVecOrMat) require_one_based_indexing(A) m, n = size(A, 1), size(A, 2) if G.i2 > m throw(DimensionMismatch("column indices for rotation are outside the matrix")) end @inbounds for i = 1:n a1, a2 = A[G.i1,i], A[G.i2,i] A[G.i1,i] = G.c *a1 + G.s*a2 A[G.i2,i] = -conj(G.s)*a1 + G.c*a2 end return A end @inline function rmul!(A::AbstractMatrix, G::Givens) require_one_based_indexing(A) m, n = size(A, 1), size(A, 2) if G.i2 > n throw(DimensionMismatch("column indices for rotation are outside the matrix")) end @inbounds for i = 1:m a1, a2 = A[i,G.i1], A[i,G.i2] A[i,G.i1] = a1*G.c - a2*G.s' A[i,G.i2] = a1*G.s + a2*G.c end return A end function lmul!(G::Givens, R::Rotation) push!(R.rotations, G) return R end function rmul!(R::Rotation, G::Givens) pushfirst!(R.rotations, G) return R end function lmul!(R::Rotation, A::AbstractVecOrMat) @inbounds for i in eachindex(R.rotations) lmul!(R.rotations[i], A) end return A end function rmul!(A::AbstractMatrix, R::Rotation) @inbounds for i in eachindex(R.rotations) rmul!(A, R.rotations[i]) end return A end function lmul!(adjR::AdjointRotation{<:Any,<:Rotation}, A::AbstractVecOrMat) R = adjR.R @inbounds for i in eachindex(R.rotations) lmul!(adjoint(R.rotations[i]), A) end return A end function rmul!(A::AbstractMatrix, adjR::AdjointRotation{<:Any,<:Rotation}) R = adjR.R @inbounds for i in eachindex(R.rotations) rmul!(A, adjoint(R.rotations[i])) end return A end function *(G1::Givens{S}, G2::Givens{T}) where {S,T} TS = promote_type(T, S) Rotation{TS}([convert(AbstractRotation{TS}, G2), convert(AbstractRotation{TS}, G1)]) end function *(G::Givens{T}, Gs::Givens{T}...) where {T} return Rotation([reverse(Gs)..., G]) end function *(G::Givens{S}, R::Rotation{T}) where {S,T} TS = promote_type(T, S) Rotation(vcat(convert(AbstractRotation{TS}, R).rotations, convert(AbstractRotation{TS}, G))) end function *(R::Rotation{S}, G::Givens{T}) where {S,T} TS = promote_type(T, S) Rotation(vcat(convert(AbstractRotation{TS}, G), convert(AbstractRotation{TS}, R).rotations)) end x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/special.jlzM# This file is a part of Julia. License is MIT: https://julialang.org/license # Methods operating on different special matrix types # Interconversion between special matrix types # conversions from Diagonal to other special matrix types Bidiagonal(A::Diagonal) = Bidiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0), :U) SymTridiagonal(A::Diagonal) = SymTridiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0)) Tridiagonal(A::Diagonal) = Tridiagonal(fill!(similar(A.diag, length(A.diag)-1), 0), A.diag, fill!(similar(A.diag, length(A.diag)-1), 0)) # conversions from Bidiagonal to other special matrix types Diagonal(A::Bidiagonal) = Diagonal(A.dv) SymTridiagonal(A::Bidiagonal) = iszero(A.ev) ? SymTridiagonal(A.dv, A.ev) : throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) Tridiagonal(A::Bidiagonal) = Tridiagonal(A.uplo == 'U' ? fill!(similar(A.ev), 0) : A.ev, A.dv, A.uplo == 'U' ? A.ev : fill!(similar(A.ev), 0)) # conversions from SymTridiagonal to other special matrix types Diagonal(A::SymTridiagonal) = Diagonal(A.dv) # These can fail when ev has the same length as dv # TODO: Revisit when a good solution for #42477 is found Bidiagonal(A::SymTridiagonal) = iszero(A.ev) ? Bidiagonal(A.dv, A.ev, :U) : throw(ArgumentError("matrix cannot be represented as Bidiagonal")) Tridiagonal(A::SymTridiagonal) = Tridiagonal(copy(A.ev), A.dv, A.ev) # conversions from Tridiagonal to other special matrix types Diagonal(A::Tridiagonal) = Diagonal(A.d) Bidiagonal(A::Tridiagonal) = iszero(A.dl) ? Bidiagonal(A.d, A.du, :U) : iszero(A.du) ? Bidiagonal(A.d, A.dl, :L) : throw(ArgumentError("matrix cannot be represented as Bidiagonal")) # conversions from AbstractTriangular to special matrix types Bidiagonal(A::AbstractTriangular) = isbanded(A, 0, 1) ? Bidiagonal(diag(A, 0), diag(A, 1), :U) : # is upper bidiagonal isbanded(A, -1, 0) ? Bidiagonal(diag(A, 0), diag(A, -1), :L) : # is lower bidiagonal throw(ArgumentError("matrix cannot be represented as Bidiagonal")) _lucopy(A::Bidiagonal, T) = copymutable_oftype(Tridiagonal(A), T) _lucopy(A::Diagonal, T) = copymutable_oftype(Tridiagonal(A), T) function _lucopy(A::SymTridiagonal, T) du = copy_similar(_evview(A), T) dl = copy.(transpose.(du)) d = copy_similar(A.dv, T) return Tridiagonal(dl, d, du) end const ConvertibleSpecialMatrix = Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,AbstractTriangular} const PossibleTriangularMatrix = Union{Diagonal, Bidiagonal, AbstractTriangular} convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:Diagonal} = m isa T ? m : isdiag(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as Diagonal")) convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:SymTridiagonal} = m isa T ? m : issymmetric(m) && isbanded(m, -1, 1) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) convert(::Type{T}, m::ConvertibleSpecialMatrix) where {T<:Tridiagonal} = m isa T ? m : isbanded(m, -1, 1) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as Tridiagonal")) convert(::Type{T}, m::Union{LowerTriangular,UnitLowerTriangular}) where {T<:LowerTriangular} = m isa T ? m : T(m)::T convert(::Type{T}, m::Union{UpperTriangular,UnitUpperTriangular}) where {T<:UpperTriangular} = m isa T ? m : T(m)::T convert(::Type{T}, m::PossibleTriangularMatrix) where {T<:LowerTriangular} = m isa T ? m : istril(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as LowerTriangular")) convert(::Type{T}, m::PossibleTriangularMatrix) where {T<:UpperTriangular} = m isa T ? m : istriu(m) ? T(m)::T : throw(ArgumentError("matrix cannot be represented as UpperTriangular")) # Constructs two method definitions taking into account (assumed) commutativity # e.g. @commutative f(x::S, y::T) where {S,T} = x+y is the same is defining # f(x::S, y::T) where {S,T} = x+y # f(y::T, x::S) where {S,T} = f(x, y) macro commutative(myexpr) @assert Base.is_function_def(myexpr) # Make sure it is a function definition y = copy(myexpr.args[1].args[2:end]) reverse!(y) reversed_call = Expr(:(=), Expr(:call,myexpr.args[1].args[1],y...), myexpr.args[1]) esc(Expr(:block, myexpr, reversed_call)) end for op in (:+, :-) for (matrixtype, uplo, converttype) in ((:UpperTriangular, 'U', :UpperTriangular), (:UnitUpperTriangular, 'U', :UpperTriangular), (:LowerTriangular, 'L', :LowerTriangular), (:UnitLowerTriangular, 'L', :LowerTriangular)) @eval begin function ($op)(A::$matrixtype, B::Bidiagonal) if B.uplo == $uplo ($op)(A, convert($converttype, B)) else ($op).(A, B) end end function ($op)(A::Bidiagonal, B::$matrixtype) if A.uplo == $uplo ($op)(convert($converttype, A), B) else ($op).(A, B) end end end end end # disambiguation between triangular and banded matrices, banded ones "dominate" mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix) = _mul!(C, A, B, MulAddMul()) mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractTriangular) = _mul!(C, A, B, MulAddMul()) mul!(C::AbstractMatrix, A::AbstractTriangular, B::BandedMatrix, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) mul!(C::AbstractMatrix, A::BandedMatrix, B::AbstractTriangular, alpha::Number, beta::Number) = _mul!(C, A, B, MulAddMul(alpha, beta)) function *(H::UpperHessenberg, B::Bidiagonal) T = promote_op(matprod, eltype(H), eltype(B)) A = mul!(similar(H, T, size(H)), H, B) return B.uplo == 'U' ? UpperHessenberg(A) : A end function *(B::Bidiagonal, H::UpperHessenberg) T = promote_op(matprod, eltype(B), eltype(H)) A = mul!(similar(H, T, size(H)), B, H) return B.uplo == 'U' ? UpperHessenberg(A) : A end function /(H::UpperHessenberg, B::Bidiagonal) T = typeof(oneunit(eltype(H))/oneunit(eltype(B))) A = _rdiv!(similar(H, T, size(H)), H, B) return B.uplo == 'U' ? UpperHessenberg(A) : A end function \(B::Bidiagonal, H::UpperHessenberg) T = typeof(oneunit(eltype(B))\oneunit(eltype(H))) A = ldiv!(similar(H, T, size(H)), B, H) return B.uplo == 'U' ? UpperHessenberg(A) : A end # specialized +/- for structured matrices. If these are removed, it falls # back to broadcasting which has ~2-10x speed regressions. # For the other structure matrix pairs, broadcasting works well. # For structured matrix types with different non-zero diagonals the underlying # representations must be promoted to the same type. # For example, in Diagonal + Bidiagonal only the main diagonal is touched so # the off diagonal could be a different type after the operation resulting in # an error. See issue #28994 @commutative function (+)(A::Bidiagonal, B::Diagonal) newdv = A.dv + B.diag Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) end function (-)(A::Bidiagonal, B::Diagonal) newdv = A.dv - B.diag Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) end function (-)(A::Diagonal, B::Bidiagonal) newdv = A.diag - B.dv Bidiagonal(newdv, typeof(newdv)(-B.ev), B.uplo) end @commutative function (+)(A::Diagonal, B::SymTridiagonal) newdv = A.diag + B.dv SymTridiagonal(A.diag + B.dv, typeof(newdv)(B.ev)) end function (-)(A::Diagonal, B::SymTridiagonal) newdv = A.diag - B.dv SymTridiagonal(newdv, typeof(newdv)(-B.ev)) end function (-)(A::SymTridiagonal, B::Diagonal) newdv = A.dv - B.diag SymTridiagonal(newdv, typeof(newdv)(A.ev)) end # this set doesn't have the aforementioned problem @commutative (+)(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl+_evview(B), A.d+B.dv, A.du+_evview(B)) -(A::Tridiagonal, B::SymTridiagonal) = Tridiagonal(A.dl-_evview(B), A.d-B.dv, A.du-_evview(B)) -(A::SymTridiagonal, B::Tridiagonal) = Tridiagonal(_evview(A)-B.dl, A.dv-B.d, _evview(A)-B.du) @commutative function (+)(A::Diagonal, B::Tridiagonal) newdv = A.diag + B.d Tridiagonal(typeof(newdv)(B.dl), newdv, typeof(newdv)(B.du)) end function (-)(A::Diagonal, B::Tridiagonal) newdv = A.diag - B.d Tridiagonal(typeof(newdv)(-B.dl), newdv, typeof(newdv)(-B.du)) end function (-)(A::Tridiagonal, B::Diagonal) newdv = A.d - B.diag Tridiagonal(typeof(newdv)(A.dl), newdv, typeof(newdv)(A.du)) end @commutative function (+)(A::Bidiagonal, B::Tridiagonal) newdv = A.dv + B.d Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(B.dl), newdv, A.ev+B.du) : (A.ev+B.dl, newdv, typeof(newdv)(B.du)))...) end function (-)(A::Bidiagonal, B::Tridiagonal) newdv = A.dv - B.d Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-B.dl), newdv, A.ev-B.du) : (A.ev-B.dl, newdv, typeof(newdv)(-B.du)))...) end function (-)(A::Tridiagonal, B::Bidiagonal) newdv = A.d - B.dv Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(A.dl), newdv, A.du-B.ev) : (A.dl-B.ev, newdv, typeof(newdv)(A.du)))...) end @commutative function (+)(A::Bidiagonal, B::SymTridiagonal) newdv = A.dv + B.dv Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(_evview(B)), A.dv+B.dv, A.ev+_evview(B)) : (A.ev+_evview(B), A.dv+B.dv, typeof(newdv)(_evview(B))))...) end function (-)(A::Bidiagonal, B::SymTridiagonal) newdv = A.dv - B.dv Tridiagonal((A.uplo == 'U' ? (typeof(newdv)(-_evview(B)), newdv, A.ev-_evview(B)) : (A.ev-_evview(B), newdv, typeof(newdv)(-_evview(B))))...) end function (-)(A::SymTridiagonal, B::Bidiagonal) newdv = A.dv - B.dv Tridiagonal((B.uplo == 'U' ? (typeof(newdv)(_evview(A)), newdv, _evview(A)-B.ev) : (_evview(A)-B.ev, newdv, typeof(newdv)(_evview(A))))...) end @commutative function (+)(A::Tridiagonal, B::UniformScaling) newd = A.d .+ Ref(B) Tridiagonal(typeof(newd)(A.dl), newd, typeof(newd)(A.du)) end @commutative function (+)(A::SymTridiagonal, B::UniformScaling) newdv = A.dv .+ Ref(B) SymTridiagonal(newdv, typeof(newdv)(A.ev)) end @commutative function (+)(A::Bidiagonal, B::UniformScaling) newdv = A.dv .+ Ref(B) Bidiagonal(newdv, typeof(newdv)(A.ev), A.uplo) end @commutative function (+)(A::Diagonal, B::UniformScaling) Diagonal(A.diag .+ Ref(B)) end # StructuredMatrix - UniformScaling = StructuredMatrix + (-UniformScaling) => # no need to define reversed order function (-)(A::UniformScaling, B::Tridiagonal) d = Ref(A) .- B.d Tridiagonal(convert(typeof(d), -B.dl), d, convert(typeof(d), -B.du)) end function (-)(A::UniformScaling, B::SymTridiagonal) dv = Ref(A) .- B.dv SymTridiagonal(dv, convert(typeof(dv), -B.ev)) end function (-)(A::UniformScaling, B::Bidiagonal) dv = Ref(A) .- B.dv Bidiagonal(dv, convert(typeof(dv), -B.ev), B.uplo) end function (-)(A::UniformScaling, B::Diagonal) Diagonal(Ref(A) .- B.diag) end ## Diagonal construction from UniformScaling Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.ฮป), m)) Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) Base.muladd(A::Union{Diagonal, UniformScaling}, B::Union{Diagonal, UniformScaling}, z::Union{Diagonal, UniformScaling}) = Diagonal(_diag_or_value(A) .* _diag_or_value(B) .+ _diag_or_value(z)) _diag_or_value(A::Diagonal) = A.diag _diag_or_value(A::UniformScaling) = A.ฮป # fill[stored]! methods fillstored!(A::Diagonal, x) = (fill!(A.diag, x); A) fillstored!(A::Bidiagonal, x) = (fill!(A.dv, x); fill!(A.ev, x); A) fillstored!(A::Tridiagonal, x) = (fill!(A.dl, x); fill!(A.d, x); fill!(A.du, x); A) fillstored!(A::SymTridiagonal, x) = (fill!(A.dv, x); fill!(A.ev, x); A) _small_enough(A::Union{Diagonal, Bidiagonal}) = size(A, 1) <= 1 _small_enough(A::Tridiagonal) = size(A, 1) <= 2 _small_enough(A::SymTridiagonal) = size(A, 1) <= 2 function fill!(A::Union{Diagonal,Bidiagonal,Tridiagonal,SymTridiagonal}, x) xT = convert(eltype(A), x) (iszero(xT) || _small_enough(A)) && return fillstored!(A, xT) throw(ArgumentError("array of type $(typeof(A)) and size $(size(A)) can not be filled with $x, since some of its entries are constrained.")) end one(D::Diagonal) = Diagonal(one.(D.diag)) one(A::Bidiagonal{T}) where T = Bidiagonal(fill!(similar(A.dv, typeof(one(T))), one(T)), fill!(similar(A.ev, typeof(one(T))), zero(one(T))), A.uplo) one(A::Tridiagonal{T}) where T = Tridiagonal(fill!(similar(A.du, typeof(one(T))), zero(one(T))), fill!(similar(A.d, typeof(one(T))), one(T)), fill!(similar(A.dl, typeof(one(T))), zero(one(T)))) one(A::SymTridiagonal{T}) where T = SymTridiagonal(fill!(similar(A.dv, typeof(one(T))), one(T)), fill!(similar(A.ev, typeof(one(T))), zero(one(T)))) for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular, :UnitUpperTriangular) @eval one(A::$t) = $t(one(parent(A))) @eval oneunit(A::$t) = $t(oneunit(parent(A))) end zero(D::Diagonal) = Diagonal(zero.(D.diag)) oneunit(D::Diagonal) = Diagonal(oneunit.(D.diag)) isdiag(A::HermOrSym{<:Any,<:Diagonal}) = isdiag(parent(A)) dot(x::AbstractVector, A::RealHermSymComplexSym{<:Real,<:Diagonal}, y::AbstractVector) = dot(x, A.data, y) # equals and approx equals methods for structured matrices # SymTridiagonal == Tridiagonal is already defined in tridiag.jl ==(A::Diagonal, B::Bidiagonal) = iszero(B.ev) && A.diag == B.dv ==(A::Diagonal, B::SymTridiagonal) = iszero(_evview(B)) && A.diag == B.dv ==(B::Bidiagonal, A::Diagonal) = A == B ==(A::Diagonal, B::Tridiagonal) = iszero(B.dl) && iszero(B.du) && A.diag == B.d ==(B::Tridiagonal, A::Diagonal) = A == B function ==(A::Bidiagonal, B::Tridiagonal) if A.uplo == 'U' return iszero(B.dl) && A.dv == B.d && A.ev == B.du else return iszero(B.du) && A.dv == B.d && A.ev == B.dl end end ==(B::Tridiagonal, A::Bidiagonal) = A == B ==(A::Bidiagonal, B::SymTridiagonal) = iszero(_evview(B)) && iszero(A.ev) && A.dv == B.dv ==(B::SymTridiagonal, A::Bidiagonal) = A == B # TODO: remove these deprecations (used by SparseArrays in the past) const _DenseConcatGroup = Union{} const _SpecialArrays = Union{} promote_to_array_type(::Tuple) = Matrix # promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices # in A to matrices of type T and sizes given by n[k:end]. n is an array # so that the same promotion code can be used for hvcat. We pass the type T # so that we can re-use this code for sparse-matrix hcat etcetera. promote_to_arrays_(n::Int, ::Type, a::Number) = a promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = Matrix(J, n, n) promote_to_arrays_(n::Int, ::Type, A::AbstractArray) = A promote_to_arrays_(n::Int, ::Type, A::AbstractQ) = collect(A) promote_to_arrays(n,k, ::Type) = () promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),) promote_to_arrays(n,k, ::Type{T}, A, B) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B)) promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C)) promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} = (promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...) _us2number(A) = A _us2number(J::UniformScaling) = J.ฮป for (f, _f, dim, name) in ((:hcat, :_hcat, 1, "rows"), (:vcat, :_vcat, 2, "cols")) @eval begin @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling}...) = $_f(A...) # if there's a Number present, J::UniformScaling must be 1x1-dimensional @inline $f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = $f(map(_us2number, A)...) function $_f(A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A)) n = -1 for a in A if !isa(a, UniformScaling) require_one_based_indexing(a) na = size(a,$dim) n >= 0 && n != na && throw(DimensionMismatch(string("number of ", $name, " of each array must match (got ", n, " and ", na, ")"))) n = na end end n == -1 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size"))) return cat(promote_to_arrays(fill(n, length(A)), 1, array_type, A...)..., dims=Val(3-$dim)) end end end hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling}...) = _hvcat(rows, A...) hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...) = _hvcat(rows, A...) function _hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractArray,AbstractQ,UniformScaling,Number}...; array_type = promote_to_array_type(A)) require_one_based_indexing(A...) nr = length(rows) sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments")) n = fill(-1, length(A)) needcols = false # whether we also need to infer some sizes from the column count j = 0 for i = 1:nr # infer UniformScaling sizes from row counts, if possible: ni = -1 # number of rows in this block-row, -1 indicates unknown for k = 1:rows[i] if !isa(A[j+k], UniformScaling) na = size(A[j+k], 1) ni >= 0 && ni != na && throw(DimensionMismatch("mismatch in number of rows")) ni = na end end if ni >= 0 for k = 1:rows[i] n[j+k] = ni end else # row consisted only of UniformScaling objects needcols = true end j += rows[i] end if needcols # some sizes still unknown, try to infer from column count nc = -1 j = 0 for i = 1:nr nci = 0 rows[i] > 0 && n[j+1] == -1 && (j += rows[i]; continue) for k = 1:rows[i] nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2) end nc >= 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns")) nc = nci j += rows[i] end nc == -1 && throw(ArgumentError("sizes of UniformScalings could not be inferred")) j = 0 for i = 1:nr if rows[i] > 0 && n[j+1] == -1 # this row consists entirely of UniformScalings nci, r = divrem(nc, rows[i]) r != 0 && throw(DimensionMismatch("indivisible UniformScaling sizes")) for k = 1:rows[i] n[j+k] = nci end end j += rows[i] end end Amat = promote_to_arrays(n, 1, array_type, A...) # We have two methods for promote_to_array_type, one returning Matrix and # another one returning SparseMatrixCSC (in SparseArrays.jl). In the dense # case, we cannot call hvcat for the promoted UniformScalings because this # causes a stack overflow. In the sparse case, however, we cannot call # typed_hvcat because we need a sparse output. if array_type == Matrix return typed_hvcat(promote_eltype(Amat...), rows, Amat...) else return hvcat(rows, Amat...) end end # factorizations function cholesky(S::RealHermSymComplexHerm{<:Real,<:SymTridiagonal}, ::NoPivot = NoPivot(); check::Bool = true) T = choltype(eltype(S)) B = Bidiagonal{T}(diag(S, 0), diag(S, S.uplo == 'U' ? 1 : -1), sym_uplo(S.uplo)) cholesky!(Hermitian(B, sym_uplo(S.uplo)), NoPivot(); check = check) end y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/bitarray.jl2# This file is a part of Julia. License is MIT: https://julialang.org/license function dot(x::BitVector, y::BitVector) # simplest way to mimic Array dot behavior length(x) == length(y) || throw(DimensionMismatch()) s = 0 xc = x.chunks yc = y.chunks @inbounds for i = 1:length(xc) s += count_ones(xc[i] & yc[i]) end s end ## slower than the unpacked version, which is MUCH slower # than blas'd (this one saves storage though, keeping it commented # just in case) #function aTb(A::BitMatrix, B::BitMatrix) #(mA, nA) = size(A) #(mB, nB) = size(B) #C = falses(nA, nB) #if mA != mB; throw(DimensionMismatch()) end #if mA == 0; return C; end #col_ch = num_bit_chunks(mA) ## TODO: avoid using aux chunks and copy (?) #aux_chunksA = zeros(UInt64, col_ch) #aux_chunksB = [zeros(UInt64, col_ch) for j=1:nB] #for j = 1:nB #Base.copy_chunks!(aux_chunksB[j], 1, B.chunks, (j-1)*mA+1, mA) #end #for i = 1:nA #Base.copy_chunks!(aux_chunksA, 1, A.chunks, (i-1)*mA+1, mA) #for j = 1:nB #for k = 1:col_ch ## TODO: improve #C[i, j] += count_ones(aux_chunksA[k] & aux_chunksB[j][k]) #end #end #end #C #end #aCb(A::BitMatrix{T}, B::BitMatrix{S}) where {T,S} = aTb(A, B) function triu(B::BitMatrix, k::Integer=0) m,n = size(B) if !(-m + 1 <= k <= n + 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least", "$(-m + 1) and at most $(n + 1) in an $m-by-$n matrix"))) end A = falses(m,n) Ac = A.chunks Bc = B.chunks for i = max(k+1,1):n j = clamp((i - 1) * m + 1, 1, i * m) Base.copy_chunks!(Ac, j, Bc, j, min(i-k, m)) end A end function tril(B::BitMatrix, k::Integer=0) m,n = size(B) if !(-m - 1 <= k <= n - 1) throw(ArgumentError(string("the requested diagonal, $k, must be at least ", "$(-m - 1) and at most $(n - 1) in an $m-by-$n matrix"))) end A = falses(m, n) Ac = A.chunks Bc = B.chunks for i = 1:min(n, m+k) j = clamp((i - 1) * m + i - k, 1, i * m) Base.copy_chunks!(Ac, j, Bc, j, max(m-i+k+1, 0)) end A end ## diag function diag(B::BitMatrix) n = minimum(size(B)) v = similar(B, n) for i = 1:n v[i] = B[i,i] end v end ## norm and rank svd(A::BitMatrix) = svd(float(A)) qr(A::BitMatrix) = qr(float(A)) ## kron @inline function kron!(R::BitVector, a::BitVector, b::BitVector) m = length(a) n = length(b) @boundscheck length(R) == n*m || throw(DimensionMismatch()) Rc = R.chunks bc = b.chunks for j = 1:m a[j] && Base.copy_chunks!(Rc, (j-1)*n+1, bc, 1, n) end return R end function kron(a::BitVector, b::BitVector) m = length(a) n = length(b) R = falses(n * m) return @inbounds kron!(R, a, b) end function kron!(R::BitMatrix, a::BitMatrix, b::BitMatrix) mA,nA = size(a) mB,nB = size(b) @boundscheck size(R) == (mA*mB, nA*nB) || throw(DimensionMismatch()) for i = 1:mA ri = (1:mB) .+ ((i-1)*mB) for j = 1:nA if a[i,j] rj = (1:nB) .+ ((j-1)*nB) R[ri,rj] = b end end end return R end function kron(a::BitMatrix, b::BitMatrix) mA,nA = size(a) mB,nB = size(b) R = falses(mA*mB, nA*nB) return @inbounds kron!(R, a, b) end ## Structure query functions issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && count(!iszero, A - copy(A'))==0 ishermitian(A::BitMatrix) = issymmetric(A) function nonzero_chunks(chunks::Vector{UInt64}, pos0::Int, pos1::Int) k0, l0 = Base.get_chunks_id(pos0) k1, l1 = Base.get_chunks_id(pos1) delta_k = k1 - k0 z = UInt64(0) u = ~z if delta_k == 0 msk_0 = (u << l0) & ~(u << l1 << 1) else msk_0 = (u << l0) msk_1 = ~(u << l1 << 1) end @inbounds begin (chunks[k0] & msk_0) == z || return true delta_k == 0 && return false for i = k0 + 1 : k1 - 1 chunks[i] == z || return true end (chunks[k1] & msk_1)==z || return true end return false end function istriu(A::BitMatrix) m, n = size(A) for j = 1:min(n,m-1) stride = (j-1) * m nonzero_chunks(A.chunks, stride+j+1, stride+m) && return false end return true end function istril(A::BitMatrix) m, n = size(A) (m == 0 || n == 0) && return true for j = 2:n stride = (j-1) * m nonzero_chunks(A.chunks, stride+1, stride+min(j-1,m)) && return false end return true end # fast 8x8 bit transpose from Henry S. Warrens's "Hacker's Delight" # http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt function transpose8x8(x::UInt64) y = x t = xor(y, y >>> 7) & 0x00aa00aa00aa00aa y = xor(y, t, t << 7) t = xor(y, y >>> 14) & 0x0000cccc0000cccc y = xor(y, t, t << 14) t = xor(y, y >>> 28) & 0x00000000f0f0f0f0 return xor(y, t, t << 28) end function form_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) x = UInt64(0) k, l = Base.get_chunks_id(i1 + (i2 - 1) * m) r = 0 for j = 1:8 k > nc && break x |= ((Bc[k] >>> l) & msk8) << r if l + 8 >= 64 && nc > k r0 = 8 - Base._mod64(l + 8) x |= (Bc[k + 1] & (msk8 >>> r0)) << (r + r0) end k += cgap + (l + cinc >= 64 ? 1 : 0) l = Base._mod64(l + cinc) r += 8 end return x end # note: assumes B is filled with 0's function put_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, x::UInt64, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) k, l = Base.get_chunks_id(i1 + (i2 - 1) * m) r = 0 for j = 1:8 k > nc && break Bc[k] |= ((x >>> r) & msk8) << l if l + 8 >= 64 && nc > k r0 = 8 - Base._mod64(l + 8) Bc[k + 1] |= ((x >>> (r + r0)) & (msk8 >>> r0)) end k += cgap + (l + cinc >= 64 ? 1 : 0) l = Base._mod64(l + cinc) r += 8 end return end adjoint(B::Union{BitVector,BitMatrix}) = Adjoint(B) transpose(B::Union{BitVector,BitMatrix}) = Transpose(B) Base.copy(B::Adjoint{Bool,BitMatrix}) = transpose!(falses(size(B)), B.parent) Base.copy(B::Transpose{Bool,BitMatrix}) = transpose!(falses(size(B)), B.parent) function transpose!(C::BitMatrix, B::BitMatrix) @boundscheck size(C) == reverse(size(B)) || throw(DimensionMismatch()) l1, l2 = size(B) cgap1, cinc1 = Base._div64(l1), Base._mod64(l1) cgap2, cinc2 = Base._div64(l2), Base._mod64(l2) Bc = B.chunks Cc = C.chunks nc = length(Bc) for i = 1:8:l1 msk8_1 = UInt64(0xff) if (l1 < i + 7) msk8_1 >>>= i + 7 - l1 end for j = 1:8:l2 x = form_8x8_chunk(Bc, i, j, l1, cgap1, cinc1, nc, msk8_1) x = transpose8x8(x) msk8_2 = UInt64(0xff) if (l2 < j + 7) msk8_2 >>>= j + 7 - l2 end put_8x8_chunk(Cc, j, i, x, l2, cgap2, cinc2, nc, msk8_2) end end return C end u/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/ldlt.jl/# This file is a part of Julia. License is MIT: https://julialang.org/license """ LDLt <: Factorization Matrix factorization type of the `LDLt` factorization of a real [`SymTridiagonal`](@ref) matrix `S` such that `S = L*Diagonal(d)*L'`, where `L` is a [`UnitLowerTriangular`](@ref) matrix and `d` is a vector. The main use of an `LDLt` factorization `F = ldlt(S)` is to solve the linear system of equations `Sx = b` with `F\\b`. This is the return type of [`ldlt`](@ref), the corresponding matrix factorization function. The individual components of the factorization `F::LDLt` can be accessed via `getproperty`: | Component | Description | |:---------:|:--------------------------------------------| | `F.L` | `L` (unit lower triangular) part of `LDLt` | | `F.D` | `D` (diagonal) part of `LDLt` | | `F.Lt` | `Lt` (unit upper triangular) part of `LDLt` | | `F.d` | diagonal values of `D` as a `Vector` | # Examples ```jldoctest julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 3.0 1.0 โ‹… 1.0 4.0 2.0 โ‹… 2.0 5.0 julia> F = ldlt(S) LDLt{Float64, SymTridiagonal{Float64, Vector{Float64}}} L factor: 3ร—3 UnitLowerTriangular{Float64, SymTridiagonal{Float64, Vector{Float64}}}: 1.0 โ‹… โ‹… 0.333333 1.0 โ‹… 0.0 0.545455 1.0 D factor: 3ร—3 Diagonal{Float64, Vector{Float64}}: 3.0 โ‹… โ‹… โ‹… 3.66667 โ‹… โ‹… โ‹… 3.90909 ``` """ struct LDLt{T,S<:AbstractMatrix{T}} <: Factorization{T} data::S function LDLt{T,S}(data) where {T,S<:AbstractMatrix{T}} require_one_based_indexing(data) new{T,S}(data) end end LDLt(data::AbstractMatrix{T}) where {T} = LDLt{T,typeof(data)}(data) LDLt{T}(data::AbstractMatrix) where {T} = LDLt(convert(AbstractMatrix{T}, data)::AbstractMatrix{T}) size(S::LDLt) = size(S.data) size(S::LDLt, i::Integer) = size(S.data, i) LDLt{T,S}(F::LDLt{T,S}) where {T,S<:AbstractMatrix{T}} = F LDLt{T,S}(F::LDLt) where {T,S<:AbstractMatrix{T}} = LDLt{T,S}(convert(S, F.data)::S) LDLt{T}(F::LDLt{T}) where {T} = F LDLt{T}(F::LDLt) where {T} = LDLt(convert(AbstractMatrix{T}, F.data)::AbstractMatrix{T}) Factorization{T}(F::LDLt{T}) where {T} = F Factorization{T}(F::LDLt) where {T} = LDLt{T}(F) function getproperty(F::LDLt{<:Any, <:SymTridiagonal}, d::Symbol) Fdata = getfield(F, :data) if d === :d return Fdata.dv elseif d === :D return Diagonal(Fdata.dv) elseif d === :L return UnitLowerTriangular(Fdata) elseif d === :Lt return UnitUpperTriangular(Fdata) else return getfield(F, d) end end adjoint(F::LDLt{<:Real,<:SymTridiagonal}) = F adjoint(F::LDLt) = LDLt(copy(adjoint(F.data))) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LDLt) summary(io, F); println(io) println(io, "L factor:") show(io, mime, F.L) println(io, "\nD factor:") show(io, mime, F.D) end # SymTridiagonal """ ldlt!(S::SymTridiagonal) -> LDLt Same as [`ldlt`](@ref), but saves space by overwriting the input `S`, instead of creating a copy. # Examples ```jldoctest julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 3.0 1.0 โ‹… 1.0 4.0 2.0 โ‹… 2.0 5.0 julia> ldltS = ldlt!(S); julia> ldltS === S false julia> S 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 3.0 0.333333 โ‹… 0.333333 3.66667 0.545455 โ‹… 0.545455 3.90909 ``` """ function ldlt!(S::SymTridiagonal{T,V}) where {T,V} n = size(S,1) d = S.dv e = S.ev @inbounds for i in 1:n-1 iszero(d[i]) && throw(ZeroPivotException(i)) e[i] /= d[i] d[i+1] -= e[i]^2*d[i] end return LDLt{T,SymTridiagonal{T,V}}(S) end """ ldlt(S::SymTridiagonal) -> LDLt Compute an `LDLt` (i.e., ``LDL^T``) factorization of the real symmetric tridiagonal matrix `S` such that `S = L*Diagonal(d)*L'` where `L` is a unit lower triangular matrix and `d` is a vector. The main use of an `LDLt` factorization `F = ldlt(S)` is to solve the linear system of equations `Sx = b` with `F\\b`. See also [`bunchkaufman`](@ref) for a similar, but pivoted, factorization of arbitrary symmetric or Hermitian matrices. # Examples ```jldoctest julia> S = SymTridiagonal([3., 4., 5.], [1., 2.]) 3ร—3 SymTridiagonal{Float64, Vector{Float64}}: 3.0 1.0 โ‹… 1.0 4.0 2.0 โ‹… 2.0 5.0 julia> ldltS = ldlt(S); julia> b = [6., 7., 8.]; julia> ldltS \\ b 3-element Vector{Float64}: 1.7906976744186047 0.627906976744186 1.3488372093023255 julia> S \\ b 3-element Vector{Float64}: 1.7906976744186047 0.627906976744186 1.3488372093023255 ``` """ function ldlt(M::SymTridiagonal{T}; shift::Number=false) where T S = typeof((zero(T)+shift)/one(T)) Mโ‚› = SymTridiagonal{S}(copymutable_oftype(M.dv, S), copymutable_oftype(M.ev, S)) if !iszero(shift) Mโ‚›.dv .+= shift end return ldlt!(Mโ‚›) end factorize(S::SymTridiagonal) = ldlt(S) function ldiv!(S::LDLt{<:Any,<:SymTridiagonal}, B::AbstractVecOrMat) require_one_based_indexing(B) n, nrhs = size(B, 1), size(B, 2) if size(S,1) != n throw(DimensionMismatch("Matrix has dimensions $(size(S)) but right hand side has first dimension $n")) end d = S.data.dv l = S.data.ev @inbounds begin for i = 2:n li1 = l[i-1] @simd for j = 1:nrhs B[i,j] -= li1*B[i-1,j] end end dn = d[n] @simd for j = 1:nrhs B[n,j] /= dn end for i = n-1:-1:1 di = d[i] li = l[i] @simd for j = 1:nrhs B[i,j] /= di B[i,j] -= li*B[i+1,j] end end end return B end rdiv!(B::AbstractVecOrMat, S::LDLt{<:Any,<:SymTridiagonal}) = transpose(ldiv!(S, transpose(B))) function logabsdet(F::LDLt{<:Any,<:SymTridiagonal}) it = (F.data[i,i] for i in 1:size(F, 1)) return sum(logโˆ˜abs, it), prod(sign, it) end # Conversion methods function SymTridiagonal(F::LDLt{<:Any, <:SymTridiagonal}) e = copy(F.data.ev) d = copy(F.data.dv) e .*= d[1:end-1] d[2:end] += e .* F.data.ev SymTridiagonal(d, e) end AbstractMatrix(F::LDLt) = SymTridiagonal(F) AbstractArray(F::LDLt) = AbstractMatrix(F) Matrix(F::LDLt) = Array(AbstractArray(F)) Array(F::LDLt) = Matrix(F) v/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/schur.jl,=# This file is a part of Julia. License is MIT: https://julialang.org/license # Schur decomposition """ Schur <: Factorization Matrix factorization type of the Schur factorization of a matrix `A`. This is the return type of [`schur(_)`](@ref), the corresponding matrix factorization function. If `F::Schur` is the factorization object, the (quasi) triangular Schur factor can be obtained via either `F.Schur` or `F.T` and the orthogonal/unitary Schur vectors via `F.vectors` or `F.Z` such that `A = F.vectors * F.Schur * F.vectors'`. The eigenvalues of `A` can be obtained with `F.values`. Iterating the decomposition produces the components `F.T`, `F.Z`, and `F.values`. # Examples ```jldoctest julia> A = [5. 7.; -2. -4.] 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> F = schur(A) Schur{Float64, Matrix{Float64}, Vector{Float64}} T factor: 2ร—2 Matrix{Float64}: 3.0 9.0 0.0 -2.0 Z factor: 2ร—2 Matrix{Float64}: 0.961524 0.274721 -0.274721 0.961524 eigenvalues: 2-element Vector{Float64}: 3.0 -2.0 julia> F.vectors * F.Schur * F.vectors' 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> t, z, vals = F; # destructuring via iteration julia> t == F.T && z == F.Z && vals == F.values true ``` """ struct Schur{Ty,S<:AbstractMatrix,C<:AbstractVector} <: Factorization{Ty} T::S Z::S values::C Schur{Ty,S,C}(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::AbstractVector) where {Ty,S,C} = new(T, Z, values) end Schur(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::AbstractVector) where {Ty} = Schur{Ty, typeof(T), typeof(values)}(T, Z, values) # backwards-compatible constructors (remove with Julia 2.0) @deprecate(Schur{Ty,S}(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::AbstractVector) where {Ty,S}, Schur{Ty,S,typeof(values)}(T, Z, values)) # iteration for destructuring into components Base.iterate(S::Schur) = (S.T, Val(:Z)) Base.iterate(S::Schur, ::Val{:Z}) = (S.Z, Val(:values)) Base.iterate(S::Schur, ::Val{:values}) = (S.values, Val(:done)) Base.iterate(S::Schur, ::Val{:done}) = nothing """ schur!(A) -> F::Schur Same as [`schur`](@ref) but uses the input argument `A` as workspace. # Examples ```jldoctest julia> A = [5. 7.; -2. -4.] 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> F = schur!(A) Schur{Float64, Matrix{Float64}, Vector{Float64}} T factor: 2ร—2 Matrix{Float64}: 3.0 9.0 0.0 -2.0 Z factor: 2ร—2 Matrix{Float64}: 0.961524 0.274721 -0.274721 0.961524 eigenvalues: 2-element Vector{Float64}: 3.0 -2.0 julia> A 2ร—2 Matrix{Float64}: 3.0 9.0 0.0 -2.0 ``` """ schur!(A::StridedMatrix{<:BlasFloat}) = Schur(LinearAlgebra.LAPACK.gees!('V', A)...) schur!(A::UpperHessenberg{T}) where {T<:BlasFloat} = Schur(LinearAlgebra.LAPACK.hseqr!(parent(A))...) """ schur(A) -> F::Schur Computes the Schur factorization of the matrix `A`. The (quasi) triangular Schur factor can be obtained from the `Schur` object `F` with either `F.Schur` or `F.T` and the orthogonal/unitary Schur vectors can be obtained with `F.vectors` or `F.Z` such that `A = F.vectors * F.Schur * F.vectors'`. The eigenvalues of `A` can be obtained with `F.values`. For real `A`, the Schur factorization is "quasitriangular", which means that it is upper-triangular except with 2ร—2 diagonal blocks for any conjugate pair of complex eigenvalues; this allows the factorization to be purely real even when there are complex eigenvalues. To obtain the (complex) purely upper-triangular Schur factorization from a real quasitriangular factorization, you can use `Schur{Complex}(schur(A))`. Iterating the decomposition produces the components `F.T`, `F.Z`, and `F.values`. # Examples ```jldoctest julia> A = [5. 7.; -2. -4.] 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> F = schur(A) Schur{Float64, Matrix{Float64}, Vector{Float64}} T factor: 2ร—2 Matrix{Float64}: 3.0 9.0 0.0 -2.0 Z factor: 2ร—2 Matrix{Float64}: 0.961524 0.274721 -0.274721 0.961524 eigenvalues: 2-element Vector{Float64}: 3.0 -2.0 julia> F.vectors * F.Schur * F.vectors' 2ร—2 Matrix{Float64}: 5.0 7.0 -2.0 -4.0 julia> t, z, vals = F; # destructuring via iteration julia> t == F.T && z == F.Z && vals == F.values true ``` """ schur(A::AbstractMatrix{T}) where {T} = schur!(copy_similar(A, eigtype(T))) schur(A::UpperHessenberg{T}) where {T} = schur!(copy_similar(A, eigtype(T))) function schur(A::RealHermSymComplexHerm) F = eigen(A; sortby=nothing) return Schur(typeof(F.vectors)(Diagonal(F.values)), F.vectors, F.values) end function schur(A::Union{UnitUpperTriangular{T},UpperTriangular{T}}) where {T} t = eigtype(T) Z = copy_similar(A, t) return Schur(Z, Matrix{t}(I, size(A)), convert(Vector{t}, diag(A))) end function schur(A::Union{UnitLowerTriangular{T},LowerTriangular{T}}) where {T} t = eigtype(T) # double flip the matrix A Z = copy_similar(A, t) reverse!(reshape(Z, :)) # construct "reverse" identity n = size(A, 1) J = zeros(t, n, n) for i in axes(J, 2) J[n+1-i, i] = oneunit(t) end return Schur(Z, J, convert(Vector{t}, diag(A))) end function schur(A::Bidiagonal{T}) where {T} t = eigtype(T) if A.uplo == 'U' return Schur(Matrix{t}(A), Matrix{t}(I, size(A)), Vector{t}(A.dv)) else # A.uplo == 'L' # construct "reverse" identity n = size(A, 1) J = zeros(t, n, n) for i in axes(J, 2) J[n+1-i, i] = oneunit(t) end dv = reverse!(Vector{t}(A.dv)) ev = reverse!(Vector{t}(A.ev)) return Schur(Matrix{t}(Bidiagonal(dv, ev, 'U')), J, dv) end end function getproperty(F::Schur, d::Symbol) if d === :Schur return getfield(F, :T) elseif d === :vectors return getfield(F, :Z) else getfield(F, d) end end Base.propertynames(F::Schur) = (:Schur, :vectors, fieldnames(typeof(F))...) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::Schur) summary(io, F); println(io) println(io, "T factor:") show(io, mime, F.T) println(io, "\nZ factor:") show(io, mime, F.Z) println(io, "\neigenvalues:") show(io, mime, F.values) end # convert a (standard-form) quasi-triangular real Schur factorization into a # triangular complex Schur factorization. # # Based on the "triangularize" function from GenericSchur.jl, # released under the MIT "Expat" license by @RalphAS function Schur{CT}(S::Schur{<:Real}) where {CT<:Complex} Tr = S.T T = CT.(Tr) Z = CT.(S.Z) n = size(T,1) for j=n:-1:2 if !iszero(Tr[j,j-1]) # We want a unitary similarity transform from # โ”Œ โ” โ”Œ โ” # โ”‚a bโ”‚ โ”‚wโ‚ xโ”‚ # โ”‚c aโ”‚ into โ”‚0 wโ‚‚โ”‚ where bc < 0 (a,b,c real) # โ”” โ”˜ โ”” โ”˜ # If we write it as # โ”Œ โ” # โ”‚u v'โ”‚ # โ”‚-v u'โ”‚ # โ”” โ”˜ # and make the Ansatz that u is real (so v is imaginary), # we arrive at a Givens rotation: # ฮธ = atan(sqrt(-Tr[j,j-1]/Tr[j-1,j])) # s,c = sin(ฮธ), cos(ฮธ) s = sqrt(abs(Tr[j,j-1])) c = sqrt(abs(Tr[j-1,j])) r = hypot(s,c) G = Givens(j-1,j,complex(c/r),im*(-s/r)) lmul!(G,T) rmul!(T,G') rmul!(Z,G') end end return Schur(triu!(T),Z,diag(T)) end Schur{Complex}(S::Schur{<:Complex}) = S Schur{T}(S::Schur{T}) where {T} = S Schur{T}(S::Schur) where {T} = Schur(T.(S.T), T.(S.Z), T <: Real && !(eltype(S.values) <: Real) ? complex(T).(S.values) : T.(S.values)) """ ordschur!(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur Same as [`ordschur`](@ref) but overwrites the factorization `F`. """ function ordschur!(schur::Schur, select::Union{Vector{Bool},BitVector}) _, _, vals = _ordschur!(schur.T, schur.Z, select) schur.values[:] = vals return schur end _ordschur(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = _ordschur!(copy(T), copy(Z), select) _ordschur!(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = LinearAlgebra.LAPACK.trsen!(convert(Vector{BlasInt}, select), T, Z)[1:3] """ ordschur(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur Reorders the Schur factorization `F` of a matrix `A = Z*T*Z'` according to the logical array `select` returning the reordered factorization `F` object. The selected eigenvalues appear in the leading diagonal of `F.Schur` and the corresponding leading columns of `F.vectors` form an orthogonal/unitary basis of the corresponding right invariant subspace. In the real case, a complex conjugate pair of eigenvalues must be either both included or both excluded via `select`. """ ordschur(schur::Schur, select::Union{Vector{Bool},BitVector}) = Schur(_ordschur(schur.T, schur.Z, select)...) """ GeneralizedSchur <: Factorization Matrix factorization type of the generalized Schur factorization of two matrices `A` and `B`. This is the return type of [`schur(_, _)`](@ref), the corresponding matrix factorization function. If `F::GeneralizedSchur` is the factorization object, the (quasi) triangular Schur factors can be obtained via `F.S` and `F.T`, the left unitary/orthogonal Schur vectors via `F.left` or `F.Q`, and the right unitary/orthogonal Schur vectors can be obtained with `F.right` or `F.Z` such that `A=F.left*F.S*F.right'` and `B=F.left*F.T*F.right'`. The generalized eigenvalues of `A` and `B` can be obtained with `F.ฮฑ./F.ฮฒ`. Iterating the decomposition produces the components `F.S`, `F.T`, `F.Q`, `F.Z`, `F.ฮฑ`, and `F.ฮฒ`. """ struct GeneralizedSchur{Ty,M<:AbstractMatrix,A<:AbstractVector,B<:AbstractVector{Ty}} <: Factorization{Ty} S::M T::M ฮฑ::A ฮฒ::B Q::M Z::M function GeneralizedSchur{Ty,M,A,B}(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, alpha::AbstractVector, beta::AbstractVector{Ty}, Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where {Ty,M,A,B} new{Ty,M,A,B}(S, T, alpha, beta, Q, Z) end end function GeneralizedSchur(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, alpha::AbstractVector, beta::AbstractVector{Ty}, Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where Ty GeneralizedSchur{Ty, typeof(S), typeof(alpha), typeof(beta)}(S, T, alpha, beta, Q, Z) end # backwards-compatible constructors (remove with Julia 2.0) @deprecate(GeneralizedSchur{Ty,M}(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, alpha::AbstractVector, beta::AbstractVector{Ty}, Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where {Ty,M}, GeneralizedSchur{Ty,M,typeof(alpha),typeof(beta)}(S, T, alpha, beta, Q, Z)) # iteration for destructuring into components Base.iterate(S::GeneralizedSchur) = (S.S, Val(:T)) Base.iterate(S::GeneralizedSchur, ::Val{:T}) = (S.T, Val(:Q)) Base.iterate(S::GeneralizedSchur, ::Val{:Q}) = (S.Q, Val(:Z)) Base.iterate(S::GeneralizedSchur, ::Val{:Z}) = (S.Z, Val(:ฮฑ)) Base.iterate(S::GeneralizedSchur, ::Val{:ฮฑ}) = (S.ฮฑ, Val(:ฮฒ)) Base.iterate(S::GeneralizedSchur, ::Val{:ฮฒ}) = (S.ฮฒ, Val(:done)) Base.iterate(S::GeneralizedSchur, ::Val{:done}) = nothing """ schur!(A::StridedMatrix, B::StridedMatrix) -> F::GeneralizedSchur Same as [`schur`](@ref) but uses the input matrices `A` and `B` as workspace. """ function schur!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} if LAPACK.version() < v"3.6.0" GeneralizedSchur(LinearAlgebra.LAPACK.gges!('V', 'V', A, B)...) else GeneralizedSchur(LinearAlgebra.LAPACK.gges3!('V', 'V', A, B)...) end end """ schur(A, B) -> F::GeneralizedSchur Computes the Generalized Schur (or QZ) factorization of the matrices `A` and `B`. The (quasi) triangular Schur factors can be obtained from the `Schur` object `F` with `F.S` and `F.T`, the left unitary/orthogonal Schur vectors can be obtained with `F.left` or `F.Q` and the right unitary/orthogonal Schur vectors can be obtained with `F.right` or `F.Z` such that `A=F.left*F.S*F.right'` and `B=F.left*F.T*F.right'`. The generalized eigenvalues of `A` and `B` can be obtained with `F.ฮฑ./F.ฮฒ`. Iterating the decomposition produces the components `F.S`, `F.T`, `F.Q`, `F.Z`, `F.ฮฑ`, and `F.ฮฒ`. """ function schur(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB} S = promote_type(eigtype(TA), TB) return schur!(copy_similar(A, S), copy_similar(B, S)) end """ ordschur!(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur Same as `ordschur` but overwrites the factorization `F`. """ function ordschur!(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) _, _, ฮฑ, ฮฒ, _, _ = _ordschur!(gschur.S, gschur.T, gschur.Q, gschur.Z, select) gschur.ฮฑ[:] = ฮฑ gschur.ฮฒ[:] = ฮฒ return gschur end _ordschur(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = _ordschur!(copy(S), copy(T), copy(Q), copy(Z), select) _ordschur!(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} = LinearAlgebra.LAPACK.tgsen!(convert(Vector{BlasInt}, select), S, T, Q, Z) """ ordschur(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur Reorders the Generalized Schur factorization `F` of a matrix pair `(A, B) = (Q*S*Z', Q*T*Z')` according to the logical array `select` and returns a GeneralizedSchur object `F`. The selected eigenvalues appear in the leading diagonal of both `F.S` and `F.T`, and the left and right orthogonal/unitary Schur vectors are also reordered such that `(A, B) = F.Q*(F.S, F.T)*F.Z'` still holds and the generalized eigenvalues of `A` and `B` can still be obtained with `F.ฮฑ./F.ฮฒ`. """ ordschur(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) = GeneralizedSchur(_ordschur(gschur.S, gschur.T, gschur.Q, gschur.Z, select)...) function getproperty(F::GeneralizedSchur, d::Symbol) if d === :values return getfield(F, :ฮฑ) ./ getfield(F, :ฮฒ) elseif d === :alpha return getfield(F, :ฮฑ) elseif d === :beta return getfield(F, :ฮฒ) elseif d === :left return getfield(F, :Q) elseif d === :right return getfield(F, :Z) else getfield(F, d) end end Base.propertynames(F::GeneralizedSchur) = (:values, :left, :right, fieldnames(typeof(F))...) function show(io::IO, mime::MIME{Symbol("text/plain")}, F::GeneralizedSchur) summary(io, F); println(io) println(io, "S factor:") show(io, mime, F.S) println(io, "\nT factor:") show(io, mime, F.T) println(io, "\nQ factor:") show(io, mime, F.Q) println(io, "\nZ factor:") show(io, mime, F.Z) println(io, "\nฮฑ:") show(io, mime, F.ฮฑ) println(io, "\nฮฒ:") show(io, mime, F.ฮฒ) end # Conversion AbstractMatrix(F::Schur) = (F.Z * F.T) * F.Z' AbstractArray(F::Schur) = AbstractMatrix(F) Matrix(F::Schur) = Array(AbstractArray(F)) Array(F::Schur) = Matrix(F) copy(F::Schur) = Schur(copy(F.T), copy(F.Z), copy(F.values)) copy(F::GeneralizedSchur) = GeneralizedSchur(copy(F.S), copy(F.T), copy(F.ฮฑ), copy(F.ฮฒ), copy(F.Q), copy(F.Z)) „/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/structuredbroadcast.jlง4# This file is a part of Julia. License is MIT: https://julialang.org/license ## Broadcast styles import Base.Broadcast using Base.Broadcast: DefaultArrayStyle, Broadcasted, tail struct StructuredMatrixStyle{T} <: Broadcast.AbstractArrayStyle{2} end StructuredMatrixStyle{T}(::Val{2}) where {T} = StructuredMatrixStyle{T}() StructuredMatrixStyle{T}(::Val{N}) where {T,N} = Broadcast.DefaultArrayStyle{N}() const StructuredMatrix = Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal,LowerTriangular,UnitLowerTriangular,UpperTriangular,UnitUpperTriangular} for ST in Base.uniontypes(StructuredMatrix) @eval Broadcast.BroadcastStyle(::Type{<:$ST}) = $(StructuredMatrixStyle{ST}()) end # Promotion of broadcasts between structured matrices. This is slightly unusual # as we define them symmetrically. This allows us to have a fallback to DefaultArrayStyle{2}(). # Diagonal can cavort with all the other structured matrix types. # Bidiagonal doesn't know if it's upper or lower, so it becomes Tridiagonal Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Diagonal}) = StructuredMatrixStyle{Diagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{Bidiagonal}) = StructuredMatrixStyle{Bidiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Diagonal}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{Diagonal}) = StructuredMatrixStyle{Bidiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Bidiagonal}, ::StructuredMatrixStyle{<:Union{Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{SymTridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{Tridiagonal}, ::StructuredMatrixStyle{<:Union{Diagonal,Bidiagonal,SymTridiagonal,Tridiagonal}}) = StructuredMatrixStyle{Tridiagonal}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{LowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{UpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitLowerTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{LowerTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{Matrix}() Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = StructuredMatrixStyle{Matrix}() # Make sure that `StructuredMatrixStyle{Matrix}` doesn't ever end up falling # through and give back `DefaultArrayStyle{2}` Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle) = T Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{Matrix}) = T Broadcast.BroadcastStyle(T::StructuredMatrixStyle{Matrix}, ::StructuredMatrixStyle{Matrix}) = T # All other combinations fall back to the default style Broadcast.BroadcastStyle(::StructuredMatrixStyle, ::StructuredMatrixStyle) = DefaultArrayStyle{2}() # And a definition akin to similar using the structured type: structured_broadcast_alloc(bc, ::Type{Diagonal}, ::Type{ElType}, n) where {ElType} = Diagonal(Array{ElType}(undef, n)) # Bidiagonal is tricky as we need to know if it's upper or lower. The promotion # system will return Tridiagonal when there's more than one Bidiagonal, but when # there's only one, we need to make figure out upper or lower merge_uplos(::Nothing, ::Nothing) = nothing merge_uplos(a, ::Nothing) = a merge_uplos(::Nothing, b) = b merge_uplos(a, b) = a == b ? a : 'T' find_uplo(a::Bidiagonal) = a.uplo find_uplo(a) = nothing find_uplo(bc::Broadcasted) = mapfoldl(find_uplo, merge_uplos, Broadcast.cat_nested(bc), init=nothing) function structured_broadcast_alloc(bc, ::Type{Bidiagonal}, ::Type{ElType}, n) where {ElType} uplo = n > 0 ? find_uplo(bc) : 'U' n1 = max(n - 1, 0) if uplo == 'T' return Tridiagonal(Array{ElType}(undef, n1), Array{ElType}(undef, n), Array{ElType}(undef, n1)) end return Bidiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n1), uplo) end structured_broadcast_alloc(bc, ::Type{SymTridiagonal}, ::Type{ElType}, n) where {ElType} = SymTridiagonal(Array{ElType}(undef, n),Array{ElType}(undef, n-1)) structured_broadcast_alloc(bc, ::Type{Tridiagonal}, ::Type{ElType}, n) where {ElType} = Tridiagonal(Array{ElType}(undef, n-1),Array{ElType}(undef, n),Array{ElType}(undef, n-1)) structured_broadcast_alloc(bc, ::Type{LowerTriangular}, ::Type{ElType}, n) where {ElType} = LowerTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{UpperTriangular}, ::Type{ElType}, n) where {ElType} = UpperTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{UnitLowerTriangular}, ::Type{ElType}, n) where {ElType} = UnitLowerTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = UnitUpperTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{Matrix}, ::Type{ElType}, n) where {ElType} = Matrix(Array{ElType}(undef, n, n)) # A _very_ limited list of structure-preserving functions known at compile-time. This list is # derived from the formerly-implemented `broadcast` methods in 0.6. Note that this must # preserve both zeros and ones (for Unit***erTriangular) and symmetry (for SymTridiagonal) const TypeFuncs = Union{typeof(round),typeof(trunc),typeof(floor),typeof(ceil)} isstructurepreserving(bc::Broadcasted) = isstructurepreserving(bc.f, bc.args...) isstructurepreserving(::Union{typeof(abs),typeof(big)}, ::StructuredMatrix) = true isstructurepreserving(::TypeFuncs, ::StructuredMatrix) = true isstructurepreserving(::TypeFuncs, ::Ref{<:Type}, ::StructuredMatrix) = true function isstructurepreserving(::typeof(Base.literal_pow), ::Ref{typeof(^)}, ::StructuredMatrix, ::Ref{Val{N}}) where N return N isa Integer && N > 0 end isstructurepreserving(f, args...) = false """ iszerodefined(T::Type) Return a `Bool` indicating whether `iszero` is well-defined for objects of type `T`. By default, this function returns `false` unless `T <: Number`. Note that this function may return `true` even if `zero(::T)` is not defined as long as `iszero(::T)` has a method that does not requires `zero(::T)`. This function is used to determine if mapping the elements of an array with a specific structure of nonzero elements preserve this structure. For instance, it is used to determine whether the output of `tuple.(Diagonal([1, 2]))` is `Diagonal([(1,), (2,)])` or `[(1,) (0,); (0,) (2,)]`. For this, we need to determine whether `(0,)` is considered to be zero. `iszero((0,))` falls back to `(0,) == zero((0,))` which fails as `zero(::Tuple{Int})` is not defined. However, `iszerodefined(::Tuple{Int})` is `false` hence we falls back to the comparison `(0,) == 0` which returns `false` and decides that the correct output is `[(1,) (0,); (0,) (2,)]`. """ iszerodefined(::Type) = false iszerodefined(::Type{<:Number}) = true iszerodefined(::Type{<:AbstractArray{T}}) where T = iszerodefined(T) fzeropreserving(bc) = (v = fzero(bc); !ismissing(v) && (iszerodefined(typeof(v)) ? iszero(v) : v == 0)) # Like sparse matrices, we assume that the zero-preservation property of a broadcasted # expression is stable. We can test the zero-preservability by applying the function # in cases where all other arguments are known scalars against a zero from the structured # matrix. If any non-structured matrix argument is not a known scalar, we give up. fzero(x::Number) = x fzero(::Type{T}) where T = T fzero(r::Ref) = r[] fzero(t::Tuple{Any}) = t[1] fzero(S::StructuredMatrix) = zero(eltype(S)) fzero(x) = missing function fzero(bc::Broadcast.Broadcasted) args = map(fzero, bc.args) return any(ismissing, args) ? missing : bc.f(args...) end function Base.similar(bc::Broadcasted{StructuredMatrixStyle{T}}, ::Type{ElType}) where {T,ElType} inds = axes(bc) if isstructurepreserving(bc) || (fzeropreserving(bc) && !(T <: Union{SymTridiagonal,UnitLowerTriangular,UnitUpperTriangular})) return structured_broadcast_alloc(bc, T, ElType, length(inds[1])) end return similar(convert(Broadcasted{DefaultArrayStyle{ndims(bc)}}, bc), ElType) end isvalidstructbc(dest, bc::Broadcasted{T}) where {T<:StructuredMatrixStyle} = Broadcast.combine_styles(dest, bc) === Broadcast.combine_styles(dest) && (isstructurepreserving(bc) || fzeropreserving(bc)) isvalidstructbc(dest::Bidiagonal, bc::Broadcasted{StructuredMatrixStyle{Bidiagonal}}) = (size(dest, 1) < 2 || find_uplo(bc) == dest.uplo) && (isstructurepreserving(bc) || fzeropreserving(bc)) function copyto!(dest::Diagonal, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] dest.diag[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end return dest end function copyto!(dest::Bidiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] dest.dv[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end if dest.uplo == 'U' for i = 1:size(dest, 1)-1 dest.ev[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) end else for i = 1:size(dest, 1)-1 dest.ev[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) end end return dest end function copyto!(dest::SymTridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] dest.dv[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end for i = 1:size(dest, 1)-1 v = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) v == (@inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i))) || throw(ArgumentError("broadcasted assignment breaks symmetry between locations ($i, $(i+1)) and ($(i+1), $i)")) dest.ev[i] = v end return dest end function copyto!(dest::Tridiagonal, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for i in axs[1] dest.d[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i)) end for i = 1:size(dest, 1)-1 dest.du[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i, i+1)) dest.dl[i] = @inbounds Broadcast._broadcast_getindex(bc, CartesianIndex(i+1, i)) end return dest end function copyto!(dest::LowerTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] for i in j:axs[1][end] @inbounds dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) end end return dest end function copyto!(dest::UpperTriangular, bc::Broadcasted{<:StructuredMatrixStyle}) isvalidstructbc(dest, bc) || return copyto!(dest, convert(Broadcasted{Nothing}, bc)) axs = axes(dest) axes(bc) == axs || Broadcast.throwdm(axes(bc), axs) for j in axs[2] for i in 1:j @inbounds dest.data[i,j] = Broadcast._broadcast_getindex(bc, CartesianIndex(i, j)) end end return dest end # We can also implement `map` and its promotion in terms of broadcast with a stricter dimension check function map(f, A::StructuredMatrix, Bs::StructuredMatrix...) sz = size(A) all(map(B->size(B)==sz, Bs)) || throw(DimensionMismatch("dimensions must match")) return f.(A, Bs...) end {/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LinearAlgebra/src/deprecated.jl{# This file is a part of Julia. License is MIT: https://julialang.org/license # To be deprecated in 2.0 rank(A::AbstractMatrix, tol::Real) = rank(A,rtol=tol) nullspace(A::AbstractVector, tol::Real) = nullspace(reshape(A, length(A), 1), rtol= tol) nullspace(A::AbstractMatrix, tol::Real) = nullspace(A, rtol=tol) pinv(A::AbstractMatrix{T}, tol::Real) where T = pinv(A, rtol=tol) t/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Markdown.jl## This file is a part of Julia. License is MIT: https://julialang.org/license """ Tools for working with the Markdown file format. Mainly for documentation. """ module Markdown import Base: show, ==, with_output_color, mapany using Base64: stringmime # Margin for printing in terminal. const margin = 2 include("parse/config.jl") include("parse/util.jl") include("parse/parse.jl") include("Common/Common.jl") include("GitHub/GitHub.jl") include("IPython/IPython.jl") include("Julia/Julia.jl") include("render/plain.jl") include("render/html.jl") include("render/latex.jl") include("render/rst.jl") include("render/terminal/render.jl") export @md_str, @doc_str parse(markdown::AbstractString; flavor = julia) = parse(IOBuffer(markdown), flavor = flavor) parse_file(file::AbstractString; flavor = julia) = parse(read(file, String), flavor = flavor) function mdexpr(s, flavor = :julia) md = parse(s, flavor = Symbol(flavor)) esc(toexpr(md)) end function docexpr(source::LineNumberNode, mod::Module, s, flavor = :julia) :($doc_str($(mdexpr(s, flavor)), $(QuoteNode(source)), $mod)) end macro md_str(s, t...) mdexpr(s, t...) end function doc_str(md, source::LineNumberNode, mod::Module) md.meta[:path] = isa(source.file, Symbol) ? String(source.file) : "" md.meta[:module] = mod md end doc_str(md::AbstractString, source::LineNumberNode, mod::Module) = doc_str(parse(md), source, mod) macro doc_str(s::AbstractString, t...) docexpr(__source__, __module__, s, t...) end import Base.Docs: catdoc catdoc(md::MD...) = MD(md...) end x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/parse/config.jlํ# This file is a part of Julia. License is MIT: https://julialang.org/license const InnerConfig = Dict{Char, Vector{Function}} mutable struct Config breaking::Vector{Function} regular::Vector{Function} inner::InnerConfig end Config() = Config(Function[], Function[], InnerConfig()) const META = IdDict{Function, Dict{Symbol, Any}}() meta(f) = get!(Dict{Symbol,Any}, META, f) breaking!(f) = meta(f)[:breaking] = true breaking(f) = get(meta(f), :breaking, false)::Bool triggers!(f, ts) = meta(f)[:triggers] = Set{Char}(ts) triggers(f) = get!(Set{Char}, meta(f), :triggers)::Set{Char} # Macros isexpr(x::Expr, ts...) = x.head in ts isexpr(x::T, ts...) where {T} = T in ts macro breaking(ex) isexpr(ex, :->) || error("invalid @breaking form, use ->") b, def = ex.args if b quote f = $(esc(def)) breaking!(f) f end else esc(def) end end macro trigger(ex) isexpr(ex, :->) || error("invalid @triggers form, use ->") ts, def = ex.args quote f = $(esc(def)) triggers!(f, $ts) f end end # Construction function config(parsers::Function...) c = Config() for parser in parsers ts = triggers(parser) if breaking(parser) push!(c.breaking, parser) elseif !isempty(ts) for t in ts push!(get!(Vector{Function}, c.inner, t), parser) end else push!(c.regular, parser) end end return c end # Flavour definitions const flavors = Dict{Symbol, Config}() macro flavor(name, features) quote const $(esc(name)) = config($(mapany(esc,features.args)...)) flavors[$(Expr(:quote, name))] = $(esc(name)) end end v/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/parse/util.jlด# This file is a part of Julia. License is MIT: https://julialang.org/license macro dotimes(n, body) quote for i = 1:$(esc(n)) $(esc(body)) end end end const whitespace = " \t\r" """ Skip any leading whitespace. Returns io. """ function skipwhitespace(io::IO; newlines = true) while !eof(io) && (peek(io, Char) in whitespace || (newlines && peek(io) == UInt8('\n'))) read(io, Char) end return io end """ Skip any leading blank lines. Returns the number skipped. """ function skipblank(io::IO) start = position(io) i = 0 for c in readeach(io, Char) c == '\n' && (start = position(io); i+=1; continue) c == '\r' && (start = position(io); i+=1; continue) c in whitespace || break end seek(io, start) return i end """ Return true if the line contains only (and, unless allowempty, at least one of) the characters given. """ function linecontains(io::IO, chars; allow_whitespace = true, eat = true, allowempty = false) start = position(io) l = readline(io) length(l) == 0 && return allowempty result = allowempty for c in l c in whitespace && (allow_whitespace ? continue : (result = false; break)) c in chars && (result = true; continue) result = false; break end !(result && eat) && seek(io, start) return result end blankline(io::IO; eat = true) = linecontains(io, "", allow_whitespace = true, allowempty = true, eat = eat) """ Test if the stream starts with the given string. `eat` specifies whether to advance on success (true by default). `padding` specifies whether leading whitespace should be ignored. """ function startswith(stream::IO, s::AbstractString; eat = true, padding = false, newlines = true) start = position(stream) padding && skipwhitespace(stream, newlines = newlines) result = true for char in s !eof(stream) && read(stream, Char) == char || (result = false; break) end !(result && eat) && seek(stream, start) return result end function startswith(stream::IO, c::AbstractChar; eat = true) if !eof(stream) && peek(stream) == UInt8(c) eat && read(stream, Char) return true else return false end end function startswith(stream::IO, ss::Vector{<:AbstractString}; kws...) any(s->startswith(stream, s; kws...), ss) end function startswith(stream::IO, r::Regex; eat = true, padding = false) @assert Base.startswith(r.pattern, "^") start = position(stream) padding && skipwhitespace(stream) line = readline(stream) seek(stream, start) m = match(r, line) m === nothing && return "" eat && @dotimes length(m.match) read(stream, Char) return m.match end """ Executes the block of code, and if the return value is `nothing`, returns the stream to its initial position. """ function withstream(f, stream) pos = position(stream) result = f() (result โ‰ก nothing || result โ‰ก false) && seek(stream, pos) return result end """ Consume the standard allowed markdown indent of three spaces. Returns false if there are more than three present. """ function eatindent(io::IO, n = 3) withstream(io) do m = 0 while startswith(io, ' ') m += 1 end return m <= n end end """ Read the stream until startswith(stream, delim) The delimiter is consumed but not included. Returns nothing and resets the stream if delim is not found. """ function readuntil(stream::IO, delimiter; newlines = false, match = nothing) withstream(stream) do buffer = IOBuffer() count = 0 while !eof(stream) if startswith(stream, delimiter) if count == 0 return String(take!(buffer)) else count -= 1 write(buffer, delimiter) continue end end char = read(stream, Char) char == match && (count += 1) !newlines && char == '\n' && break write(buffer, char) end end end # TODO: refactor this. If we're going to assume # the delimiter is a single character + a minimum # repeat we may as well just pass that into the # function. """ Parse a symmetrical delimiter which wraps words. i.e. `*word word*` but not `*word * word`. `repeat` specifies whether the delimiter can be repeated. Escaped delimiters are not yet supported. """ function parse_inline_wrapper(stream::IO, delimiter::AbstractString; rep = false) delimiter, nmin = string(delimiter[1]), length(delimiter) withstream(stream) do if position(stream) >= 1 # check the previous byte isn't a delimiter skip(stream, -1) (read(stream, Char) in delimiter) && return nothing end n = nmin startswith(stream, delimiter^n) || return nothing while startswith(stream, delimiter); n += 1; end !rep && n > nmin && return nothing !eof(stream) && peek(stream, Char) in whitespace && return nothing buffer = IOBuffer() for char in readeach(stream, Char) write(buffer, char) if !(char in whitespace || char == '\n' || char in delimiter) && startswith(stream, delimiter^n) trailing = 0 while startswith(stream, delimiter); trailing += 1; end trailing == 0 && return String(take!(buffer)) write(buffer, delimiter ^ (n + trailing)) end end end end function showrest(io::IO) start = position(io) show(read(io, String)) println() seek(io, start) end w/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/parse/parse.jl‚ # This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct MD content::Vector{Any} meta::Dict{Symbol, Any} MD(content::AbstractVector, meta::Dict = Dict()) = new(content, meta) end MD(xs...) = MD(vcat(xs...)) function MD(cfg::Config, xs...) md = MD(xs...) md.meta[:config] = cfg return md end config(md::MD) = md.meta[:config]::Config # Forward some array methods Base.push!(md::MD, x) = push!(md.content, x) Base.getindex(md::MD, args...) = md.content[args...] Base.setindex!(md::MD, args...) = setindex!(md.content, args...) Base.lastindex(md::MD) = lastindex(md.content) Base.firstindex(md::MD) = firstindex(md.content) Base.length(md::MD) = length(md.content) Base.isempty(md::MD) = isempty(md.content) Base.copy(md::MD) = MD(copy(md.content), copy(md.meta)) ==(a::MD, b::MD) = (html(a) == html(b)) # Parser functions: # md โ€“ should be modified appropriately # return โ€“ basically, true if parse was successful # false uses the next parser in the queue, true # goes back to the beginning # # Inner parsers: # return โ€“ element to use or nothing # Inner parsing function parseinline(stream::IO, md::MD, parsers::Vector{Function}) for parser in parsers inner = parser(stream, md) inner โ‰ก nothing || return inner end end function parseinline(stream::IO, md::MD, config::Config) content = [] buffer = IOBuffer() while !eof(stream) char = peek(stream, Char) if haskey(config.inner, char) && (inner = parseinline(stream, md, config.inner[char])) !== nothing c = String(take!(buffer)) !isempty(c) && push!(content, c) buffer = IOBuffer() push!(content, inner) else write(buffer, read(stream, Char)) end end c = String(take!(buffer)) !isempty(c) && push!(content, c) return content end parseinline(s::AbstractString, md::MD, c::Config) = parseinline(IOBuffer(s), md, c) parseinline(s, md::MD) = parseinline(s, md, config(md)) # Block parsing function parse(stream::IO, block::MD, config::Config; breaking = false) skipblank(stream) eof(stream) && return false for parser in (breaking ? config.breaking : [config.breaking; config.regular]) parser(stream, block) && return true end return false end parse(stream::IO, block::MD; breaking = false) = parse(stream, block, config(block), breaking = breaking) function parse(stream::IO; flavor = julia) isa(flavor, Symbol) && (flavor = flavors[flavor]) markdown = MD(flavor) while parse(stream, markdown, flavor) end return markdown end y/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Common/Common.jl # This file is a part of Julia. License is MIT: https://julialang.org/license include("block.jl") include("inline.jl") @flavor common [list, indentcode, blockquote, admonition, footnote, hashheader, horizontalrule, paragraph, linebreak, escapes, inline_code, asterisk_bold, underscore_bold, asterisk_italic, underscore_italic, image, footnote_link, link, autolink] x/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Common/block.jl๒*# This file is a part of Julia. License is MIT: https://julialang.org/license # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # Paragraphs # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct Paragraph content end Paragraph() = Paragraph([]) function paragraph(stream::IO, md::MD) buffer = IOBuffer() p = Paragraph() push!(md, p) skipwhitespace(stream) prev_char = '\n' for char in readeach(stream, Char) if char == '\n' || char == '\r' char == '\r' && !eof(stream) && peek(stream, Char) == '\n' && read(stream, Char) if prev_char == '\\' write(buffer, '\n') elseif blankline(stream) || parse(stream, md, breaking = true) break else write(buffer, ' ') end else write(buffer, char) end prev_char = char end p.content = parseinline(seek(buffer, 0), md) return true end # โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # Headers # โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct Header{level} text end Header(s, level::Int) = Header{level}(s) Header(s) = Header(s, 1) @breaking true -> function hashheader(stream::IO, md::MD) withstream(stream) do eatindent(stream) || return false level = 0 while startswith(stream, '#') level += 1 end (level < 1 || level > 6) && return false c = ' ' # Allow empty headers, but require a space !eof(stream) && (c = read(stream, Char); !(c in " \n")) && return false if c != '\n' # Empty header h = strip(readline(stream)) h = (match(r"(.*?)( +#+)?$", h)::AbstractMatch).captures[1] buffer = IOBuffer() print(buffer, h) push!(md.content, Header(parseinline(seek(buffer, 0), md), level)) else push!(md.content, Header("", level)) end return true end end function setextheader(stream::IO, md::MD) withstream(stream) do eatindent(stream) || return false header = strip(readline(stream)) isempty(header) && return false eatindent(stream) || return false underline = strip(readline(stream)) length(underline) < 3 && return false u = underline[1] u in "-=" || return false all(c -> c == u, underline) || return false level = (u == '=') ? 1 : 2 push!(md.content, Header(parseinline(header, md), level)) return true end end # โ€“โ€“โ€“โ€“ # Code # โ€“โ€“โ€“โ€“ mutable struct Code language::String code::String end Code(code) = Code("", code) function indentcode(stream::IO, block::MD) withstream(stream) do buffer = IOBuffer() while !eof(stream) if startswith(stream, " ") || startswith(stream, "\t") write(buffer, readline(stream, keep=true)) elseif blankline(stream) write(buffer, '\n') else break end end code = String(take!(buffer)) !isempty(code) && (push!(block, Code(rstrip(code))); return true) return false end end # -------- # Footnote # -------- mutable struct Footnote id::String text end function footnote(stream::IO, block::MD) withstream(stream) do regex = r"^\[\^(\w+)\]:" str = startswith(stream, regex) if isempty(str) return false else ref = (match(regex, str)::AbstractMatch).captures[1] buffer = IOBuffer() write(buffer, readline(stream, keep=true)) while !eof(stream) if startswith(stream, " ") || startswith(stream, "\t") write(buffer, readline(stream, keep=true)) elseif blankline(stream) write(buffer, '\n') else break end end content = parse(seekstart(buffer)).content push!(block, Footnote(ref, content)) return true end end end # โ€“โ€“โ€“โ€“โ€“โ€“ # Quotes # โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct BlockQuote content end BlockQuote() = BlockQuote([]) # TODO: Laziness @breaking true -> function blockquote(stream::IO, block::MD) withstream(stream) do buffer = IOBuffer() empty = true while eatindent(stream) && startswith(stream, '>') startswith(stream, " ") write(buffer, readline(stream, keep=true)) empty = false end empty && return false md = String(take!(buffer)) push!(block, BlockQuote(parse(md, flavor = config(block)).content)) return true end end # ----------- # Admonitions # ----------- mutable struct Admonition category::String title::String content::Vector end @breaking true -> function admonition(stream::IO, block::MD) withstream(stream) do # Admonition syntax: # # !!! category "optional explicit title within double quotes" # Any number of other indented markdown elements. # # This is the second paragraph. # startswith(stream, "!!! ") || return false # Extract the category of admonition and its title: category, title = let untitled = r"^([a-z]+)$", # !!! titled = r"^([a-z]+) \"(.*)\"$", # !!! "" line = strip(readline(stream)) if occursin(untitled, line) m = match(untitled, line)::AbstractMatch # When no title is provided we use CATEGORY_NAME, capitalising it. m.captures[1], uppercasefirst(m.captures[1]) elseif occursin(titled, line) m = match(titled, line)::AbstractMatch # To have a blank TITLE provide an explicit empty string as TITLE. m.captures[1], m.captures[2] else # Admonition header is invalid so we give up parsing here and move # on to the next parser. return false end end # Consume the following indented (4 spaces or tab) block. buffer = IOBuffer() while !eof(stream) if startswith(stream, " ") || startswith(stream, "\t") write(buffer, readline(stream, keep=true)) elseif blankline(stream) write(buffer, '\n') else break end end # Parse the nested block as markdown and create a new Admonition block. nested = parse(String(take!(buffer)), flavor = config(block)) push!(block, Admonition(category, title, nested.content)) return true end end # โ€“โ€“โ€“โ€“โ€“ # Lists # โ€“โ€“โ€“โ€“โ€“ mutable struct List items::Vector{Any} ordered::Int # `-1` is unordered, `>= 0` is ordered. loose::Bool # TODO: Renderers should use this field end List(x::AbstractVector, b::Integer) = List(x, b, false) List(x::AbstractVector) = List(x, -1) List(b::Integer) = List(Any[], b) List(xs...) = List(vcat(xs...)) isordered(list::List) = list.ordered >= 0 const BULLETS = r"^ {0,3}(\*|\+|-)( |$)" const NUM_OR_BULLETS = r"^ {0,3}(\*|\+|-|\d+(\.|\)))( |$)" @breaking true -> function list(stream::IO, block::MD) withstream(stream) do bullet = startswith(stream, NUM_OR_BULLETS; eat = false) indent = isempty(bullet) ? (return false) : length(bullet) # Calculate the starting number and regex to use for bullet matching. initial, regex = if occursin(BULLETS, bullet) # An unordered list. Use `-1` to flag the list as unordered. -1, BULLETS elseif occursin(r"^ {0,3}\d+(\.|\))( |$)", bullet) # An ordered list. Either with `1. ` or `1) ` style numbering. r = occursin(".", bullet) ? r"^ {0,3}(\d+)\.( |$)" : r"^ {0,3}(\d+)\)( |$)" Base.parse(Int, (match(r, bullet)::AbstractMatch).captures[1]), r else # Failed to match any bullets. This branch shouldn't actually be needed # since the `NUM_OR_BULLETS` regex should cover this, but we include it # simply for thoroughness. return false end # Initialise the empty list object: either ordered or unordered. list = List(initial) buffer = IOBuffer() # For capturing nested text for recursive parsing. newline = false # For checking if we have two consecutive newlines: end of list. count = 0 # Count of list items. Used to check if we need to push remaining # content in `buffer` after leaving the `while` loop. while !eof(stream) if startswith(stream, "\n") if newline # Double newline ends the current list. pushitem!(list, buffer) break else newline = true println(buffer) end else if startswith(stream, " "^indent) newline && (list.loose = true) # Indented text that is part of the current list item. print(buffer, readline(stream, keep=true)) else matched = startswith(stream, regex) if isempty(matched) # Unindented text meaning we have left the current list. pushitem!(list, buffer) break else # Start of a new list item. newline && (list.loose = true) count += 1 count > 1 && pushitem!(list, buffer) print(buffer, readline(stream, keep=true)) end end newline = false end end count == length(list.items) || pushitem!(list, buffer) push!(block, list) return true end end pushitem!(list, buffer) = push!(list.items, parse(String(take!(buffer))).content) # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # HorizontalRule # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct HorizontalRule end function horizontalrule(stream::IO, block::MD) withstream(stream) do n, rule = 0, ' ' for char in readeach(stream, Char) char == '\n' && break isspace(char) && continue if n==0 || char==rule rule = char n += 1 else return false end end is_hr = (n โ‰ฅ 3 && rule in "*-") is_hr && push!(block, HorizontalRule()) return is_hr end end y���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Common/inline.jl ������# This file is a part of Julia. License is MIT: https://julialang.org/license # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # Emphasis # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct Italic text end @trigger '*' -> function asterisk_italic(stream::IO, md::MD) result = parse_inline_wrapper(stream, "*") return result === nothing ? nothing : Italic(parseinline(result, md)) end @trigger '_' -> function underscore_italic(stream::IO, md::MD) result = parse_inline_wrapper(stream, "_") return result === nothing ? nothing : Italic(parseinline(result, md)) end mutable struct Bold text end @trigger '*' -> function asterisk_bold(stream::IO, md::MD) result = parse_inline_wrapper(stream, "**") return result === nothing ? nothing : Bold(parseinline(result, md)) end @trigger '_' -> function underscore_bold(stream::IO, md::MD) result = parse_inline_wrapper(stream, "__") return result === nothing ? nothing : Bold(parseinline(result, md)) end # โ€“โ€“โ€“โ€“ # Code # โ€“โ€“โ€“โ€“ @trigger '`' -> function inline_code(stream::IO, md::MD) withstream(stream) do ticks = startswith(stream, r"^(`+)") result = readuntil(stream, ticks) if result === nothing nothing else result = strip(result) # An odd number of backticks wrapping the text will produce a `Code` node, while # an even number will result in a `LaTeX` node. This allows for arbitrary # backtick combinations to be embedded inside the resulting node, i.e. # # `a`, ``a``, `` `a` ``, ``` ``a`` ```, ``` `a` ```, etc. # ^ ^ ^ ^ ^ # C L L C C with C=Code and L=LaTeX. # isodd(length(ticks)) ? Code(result) : LaTeX(result) end end end # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # Images & Links # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct Image url::String alt::String end @trigger '!' -> function image(stream::IO, md::MD) withstream(stream) do startswith(stream, "![") || return alt = readuntil(stream, ']', match = '[') alt โ‰ก nothing && return skipwhitespace(stream) startswith(stream, '(') || return url = readuntil(stream, ')', match = '(') url โ‰ก nothing && return return Image(url, alt) end end mutable struct Link text url::String end @trigger '[' -> function link(stream::IO, md::MD) withstream(stream) do startswith(stream, '[') || return text = readuntil(stream, ']', match = '[') text โ‰ก nothing && return skipwhitespace(stream) startswith(stream, '(') || return url = readuntil(stream, ')', match = '(') url โ‰ก nothing && return return Link(parseinline(text, md), url) end end @trigger '[' -> function footnote_link(stream::IO, md::MD) withstream(stream) do regex = r"^\[\^(\w+)\]" str = startswith(stream, regex) if isempty(str) return else ref = (match(regex, str)::AbstractMatch).captures[1] return Footnote(ref, nothing) end end end @trigger '<' -> function autolink(stream::IO, md::MD) withstream(stream) do startswith(stream, '<') || return url = readuntil(stream, '>') url โ‰ก nothing && return _is_link(url) && return Link(url, url) _is_mailto(url) && return Link(url, url) return end end # This list is taken from the commonmark spec # http://spec.commonmark.org/0.19/#absolute-uri const _allowable_schemes = Set(split("coap doi javascript aaa aaas about acap cap cid crid data dav dict dns file ftp geo go gopher h323 http https iax icap im imap info ipp iris iris.beep iris.xpc iris.xpcs iris.lwz ldap mailto mid msrp msrps mtqp mupdate news nfs ni nih nntp opaquelocktoken pop pres rtsp service session shttp sieve sip sips sms snmp,soap.beep soap.beeps tag tel telnet tftp thismessage tn3270 tip tv urn vemmi ws wss xcon xcon-userid xmlrpc.beep xmlrpc.beeps xmpp z39.50r z39.50s adiumxtra afp afs aim apt,attachment aw beshare bitcoin bolo callto chrome,chrome-extension com-eventbrite-attendee content cvs,dlna-playsingle dlna-playcontainer dtn dvb ed2k facetime feed finger fish gg git gizmoproject gtalk hcp icon ipn irc irc6 ircs itms jar jms keyparc lastfm ldaps magnet maps market,message mms ms-help msnim mumble mvn notes oid palm paparazzi platform proxy psyc query res resource rmi rsync rtmp secondlife sftp sgn skype smb soldat spotify ssh steam svn teamspeak things udp unreal ut2004 ventrilo view-source webcal wtai wyciwyg xfire xri ymsgr")) function _is_link(s::AbstractString) '<' in s && return false m = match(r"^(.*)://(\S+?)(:\S*)?$", s) m โ‰ก nothing && return false scheme = lowercase(m.captures[1]) return scheme in _allowable_schemes end # non-normative regex from the HTML5 spec const _email_regex = r"^mailto\:[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" function _is_mailto(s::AbstractString) return occursin(_email_regex, s) end # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ # Punctuation # โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ mutable struct LineBreak end @trigger '\\' -> function linebreak(stream::IO, md::MD) if startswith(stream, "\\\n") return LineBreak() end end @trigger '-' -> function en_dash(stream::IO, md::MD) if startswith(stream, "--") return "โ€“" end end const escape_chars = "\\`*_#+-.!{}[]()\$" @trigger '\\' -> function escapes(stream::IO, md::MD) withstream(stream) do if startswith(stream, "\\") && !eof(stream) && (c = read(stream, Char)) in escape_chars return string(c) end end end y���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/GitHub/GitHub.jl"������# This file is a part of Julia. License is MIT: https://julialang.org/license include("table.jl") @breaking true -> function fencedcode(stream::IO, block::MD) withstream(stream) do startswith(stream, "~~~", padding = true) || startswith(stream, "```", padding = true) || return false skip(stream, -1) ch = read(stream, Char) trailing = strip(readline(stream)) flavor = lstrip(trailing, ch) n = 3 + length(trailing) - length(flavor) # inline code block ch in flavor && return false buffer = IOBuffer() while !eof(stream) line_start = position(stream) if startswith(stream, string(ch) ^ n) if !startswith(stream, string(ch)) if flavor == "math" push!(block, LaTeX(String(take!(buffer)) |> chomp)) else push!(block, Code(flavor, String(take!(buffer)) |> chomp)) end return true else seek(stream, line_start) end end write(buffer, readline(stream, keep=true)) end return false end end function github_paragraph(stream::IO, md::MD) skipwhitespace(stream) buffer = IOBuffer() p = Paragraph() push!(md, p) for char in readeach(stream, Char) if char == '\n' eof(stream) && break if blankline(stream) || parse(stream, md, breaking = true) break else write(buffer, '\n') end else write(buffer, char) end end p.content = parseinline(seek(buffer, 0), md) return true end @flavor github [list, indentcode, blockquote, admonition, footnote, fencedcode, hashheader, github_table, github_paragraph, linebreak, escapes, en_dash, inline_code, asterisk_bold, underscore_bold, asterisk_italic, underscore_italic, image, footnote_link, link, autolink] x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/GitHub/table.jl(������# This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct Table rows::Vector{Vector{Any}} align::Vector{Symbol} end function parserow(stream::IO) withstream(stream) do line = readline(stream) row = split(line, r"(?<!\\)\|") length(row) == 1 && return isempty(row[1]) && popfirst!(row) map!(x -> strip(replace(x, "\\|" => "|")), row, row) isempty(row[end]) && pop!(row) return row end end function rowlength!(row, len) while length(row) < len push!(row, "") end while length(row) > len pop!(row) end return row end const default_align = :r function parsealign(row) align = Symbol[] for s in row (length(s) โ‰ฅ 3 && s โІ Set("-:")) || return push!(align, s[1] == ':' ? (s[end] == ':' ? :c : :l) : s[end] == ':' ? :r : default_align) end return align end function github_table(stream::IO, md::MD) withstream(stream) do skipblank(stream) rows = [] cols = 0 align = nothing while (row = parserow(stream)) !== nothing if length(rows) == 0 cols = length(row) end if align === nothing && length(rows) == 1 # Must have a --- row align = parsealign(row) (align === nothing || length(align) != cols) && return false else push!(rows, map(x -> parseinline(x, md), rowlength!(row, cols))) end end length(rows) <= 1 && return false push!(md, Table(rows, align)) return true end end function html(io::IO, md::Table) withtag(io, :table) do for (i, row) in enumerate(md.rows) withtag(io, :tr) do for (j, c) in enumerate(md.rows[i]) alignment = md.align[j] alignment = alignment === :l ? "left" : alignment === :r ? "right" : "center" withtag(io, i == 1 ? :th : :td, ("align", alignment)) do htmlinline(io, c) end end end end end end mapmap(f, xss) = map(xs->map(f, xs), xss) colwidths(rows; len = length, min = 0) = reduce((x,y) -> max.(x,y), [min; convert(Vector{Vector{Int}}, mapmap(len, rows))]) padding(width, twidth, a) = a === :l ? (0, twidth - width) : a === :r ? (twidth - width, 0) : a === :c ? (floor(Int, (twidth-width)/2), ceil(Int, (twidth-width)/2)) : error("Invalid alignment $a") function padcells!(rows, align; len = length, min = 0) widths = colwidths(rows, len = len, min = min) for i = 1:length(rows), j = axes(rows[1],1) cell = rows[i][j] lpad, rpad = padding(len(cell), widths[j], align[j]) rows[i][j] = " "^lpad * cell * " "^rpad end return rows end _dash(width, align) = align === :l ? ":" * "-"^width * " " : align === :r ? " " * "-"^width * ":" : align === :c ? ":" * "-"^width * ":" : throw(ArgumentError("Invalid alignment $align")) function plain(io::IO, md::Table) cells = mapmap(md.rows) do each replace(plaininline(each), "|" => "\\|") end padcells!(cells, md.align, len = length, min = 3) for i = axes(cells,1) print(io, "| ") join(io, cells[i], " | ") println(io, " |") if i == 1 print(io, "|") join(io, [_dash(length(cells[i][j]), md.align[j]) for j = axes(cells[1],1)], "|") println(io, "|") end end end function rst(io::IO, md::Table) cells = mapmap(rstinline, md.rows) padcells!(cells, md.align, len = length, min = 3) single = ["-"^length(c) for c in cells[1]] double = ["="^length(c) for c in cells[1]] function print_row(row, row_sep, col_sep) print(io, col_sep, row_sep) join(io, row, string(row_sep, col_sep, row_sep)) println(io, row_sep, col_sep) end print_row(single, '-', '+') for i = 1:length(cells) print_row(cells[i], ' ', '|') i โ‰ก 1 ? print_row(double, '=', '+') : print_row(single, '-', '+') end end function term(io::IO, md::Table, columns) margin_str = " "^margin cells = mapmap(x -> terminline_string(io, x), md.rows) padcells!(cells, md.align, len = ansi_length) for i = 1:length(cells) print(io, margin_str) join(io, cells[i], " ") if i == 1 println(io) print(io, margin_str) join(io, ["โ€“"^ansi_length(cells[i][j]) for j = 1:length(cells[1])], " ") end i < length(cells) && println(io) end end function latex(io::IO, md::Table) wrapblock(io, "tabular") do align = md.align println(io, "{$(join(align, " | "))}") for (i, row) in enumerate(md.rows) for (j, cell) in enumerate(row) j != 1 && print(io, " & ") latexinline(io, cell) end println(io, " \\\\") if i == 1 println(io, "\\hline") end end end end {���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/IPython/IPython.jlแ������# This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct LaTeX formula::String end @trigger '$' -> function tex(stream::IO, md::MD) result = parse_inline_wrapper(stream, "\$", rep = true) return result === nothing ? nothing : LaTeX(result) end function blocktex(stream::IO, md::MD) withstream(stream) do ex = tex(stream, md) if ex โ‰ก nothing return false else push!(md, ex) return true end end end show(io::IO, tex::LaTeX) = print(io, '$', tex.formula, '$') latex(io::IO, tex::LaTeX) = println(io, "\$\$", tex.formula, "\$\$") latexinline(io::IO, tex::LaTeX) = print(io, '$', tex.formula, '$') w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Julia/Julia.jl†������# This file is a part of Julia. License is MIT: https://julialang.org/license # This file contains markdown extensions designed to make documenting # Julia easy peasy. # # We start by borrowing GitHub's `fencedcode` extension โ€“ more to follow. include("interp.jl") @flavor julia [blocktex, blockinterp, hashheader, list, indentcode, fencedcode, blockquote, admonition, footnote, github_table, horizontalrule, setextheader, paragraph, linebreak, escapes, tex, interp, en_dash, inline_code, asterisk_bold, underscore_bold, asterisk_italic, underscore_italic, image, footnote_link, link, autolink] x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/Julia/interp.jl ������# This file is a part of Julia. License is MIT: https://julialang.org/license function _parse(stream::IO; greedy::Bool = true, raise::Bool = true) pos = position(stream) ex, ฮ” = Meta.parse(read(stream, String), 1, greedy = greedy, raise = raise) seek(stream, pos + ฮ” - 1) return ex end function interpinner(stream::IO, greedy = false) startswith(stream, '$') || return (eof(stream) || peek(stream, Char) in whitespace) && return try return _parse(stream, greedy = greedy) catch e if isa(e, Meta.ParseError) return nothing else rethrow() end end end @trigger '$' -> function interp(stream::IO, md::MD) withstream(stream) do ex = interpinner(stream) return ex end end function blockinterp(stream::IO, md::MD) withstream(stream) do ex = interpinner(stream) if ex โ‰ก nothing return false else push!(md, ex) return true end end end toexpr(x) = x toexpr(xs::Union{Vector{Any},Vector{Vector{Any}}}) = Expr(:call, GlobalRef(Base,:getindex), Any, mapany(toexpr, xs)...) for T in Any[MD, Paragraph, Header, Link, Bold, Italic] @eval function toexpr(md::$T) Expr(:call, typeof(md), $(map(x->:(toexpr(md.$x)), fieldnames(Base.unwrap_unionall(T)))...)) end end function toexpr(md::Table) Expr(:call, Table, toexpr(md.rows), md.align) end function toexpr(md::List) Expr(:call, List, toexpr(md.items), md.ordered, md.loose) end x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/plain.jl๏������# This file is a part of Julia. License is MIT: https://julialang.org/license plain(x) = sprint(plain, x) function plain(io::IO, content::Vector) isempty(content) && return for md in content[1:end-1] plain(io, md) println(io) end plain(io, content[end]) end plain(io::IO, md::MD) = plain(io, md.content) function plain(io::IO, header::Header{l}) where l print(io, "#"^l*" ") plaininline(io, header.text) println(io) end function plain(io::IO, code::Code) # If the code includes a fenced block this will break parsing, # so it must be enclosed by a longer ````-sequence. n = mapreduce(m -> length(m.match), max, eachmatch(r"^`+"m, code.code); init=2) + 1 println(io, "`" ^ n, code.language) println(io, code.code) println(io, "`" ^ n) end function plain(io::IO, p::Paragraph) plaininline(io, p.content) println(io) end function plain(io::IO, list::List) for (i, item) in enumerate(list.items) print(io, isordered(list) ? "$(i + list.ordered - 1). " : " * ") lines = split(rstrip(sprint(plain, item)), "\n") for (n, line) in enumerate(lines) print(io, (n == 1 || isempty(line)) ? "" : " ", line) n < length(lines) && println(io) end println(io) end end function plain(io::IO, q::BlockQuote) s = sprint(plain, q.content) for line in split(rstrip(s), "\n") println(io, isempty(line) ? ">" : "> ", line) end println(io) end function plain(io::IO, f::Footnote) print(io, "[^", f.id, "]:") s = sprint(plain, f.text) lines = split(rstrip(s), "\n") # Single line footnotes are printed on the same line as their label # rather than taking up an additional line. if length(lines) == 1 println(io, " ", lines[1]) else println(io) for line in lines println(io, isempty(line) ? "" : " ", line) end println(io) end end function plain(io::IO, md::Admonition) s = sprint(plain, md.content) title = md.title == uppercasefirst(md.category) ? "" : " \"$(md.title)\"" println(io, "!!! ", md.category, title) for line in split(rstrip(s), "\n") println(io, isempty(line) ? "" : " ", line) end println(io) end function plain(io::IO, md::HorizontalRule) println(io, "-" ^ 3) end function plain(io::IO, l::LaTeX) println(io, '$', '$') println(io, l.formula) println(io, '$', '$') end function plain(io::IO, md) show(io, MIME"text/plain"(), md) println(io) end # Inline elements plaininline(x) = sprint(plaininline, x) function plaininline(io::IO, md...) for el in md plaininline(io, el) end end plaininline(io::IO, md::Vector) = !isempty(md) && plaininline(io, md...) plaininline(io::IO, f::Footnote) = print(io, "[^", f.id, "]") plaininline(io::IO, link::Link) = plaininline(io, "[", link.text, "](", link.url, ")") plaininline(io::IO, md::Image) = plaininline(io, "![", md.alt, "](", md.url, ")") plaininline(io::IO, s::AbstractString) = print(io, s) plaininline(io::IO, md::Bold) = plaininline(io, "**", md.text, "**") plaininline(io::IO, md::Italic) = plaininline(io, "*", md.text, "*") function plaininline(io::IO, md::Code) if occursin("`", md.code) n = maximum(length(m.match) for m in eachmatch(r"(`+)", md.code)) s = "`"^((iseven(n) ? 1 : 2) + n) print(io, s, Base.startswith(md.code, "`") ? " " : "") print(io, md.code, endswith(md.code, "`") ? " " : "", s) else print(io, "`", md.code, "`") end end plaininline(io::IO, br::LineBreak) = println(io) plaininline(io::IO, x) = show(io, MIME"text/plain"(), x) # show Base.show(io::IO, md::MD) = plain(io, md) Base.show(io::IO, ::MIME"text/markdown", md::MD) = plain(io, md) w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/html.jl|������# This file is a part of Julia. License is MIT: https://julialang.org/license include("rich.jl") # Utils function withtag(f, io::IO, tag, attrs...) print(io, "<$tag") for (attr, value) in attrs print(io, " ") htmlesc(io, attr) print(io, "=\"") htmlesc(io, value) print(io, "\"") end f === nothing && return print(io, " />") print(io, ">") f() print(io, "</$tag>") end tag(io::IO, tag, attrs...) = withtag(nothing, io, tag, attrs...) const _htmlescape_chars = Dict('<'=>"<", '>'=>">", '"'=>""", '&'=>"&", # ' '=>" ", ) for ch in "'`!\$%()=+{}[]" _htmlescape_chars[ch] = "&#$(Int(ch));" end function htmlesc(io::IO, s::AbstractString) # s1 = replace(s, r"&(?!(\w+|\#\d+);)" => "&") for ch in s print(io, get(_htmlescape_chars, ch, ch)) end end function htmlesc(io::IO, s::Symbol) htmlesc(io, string(s)) end function htmlesc(io::IO, xs::Union{AbstractString,Symbol}...) for s in xs htmlesc(io, s) end end function htmlesc(s::Union{AbstractString,Symbol}) sprint(htmlesc, s) end # Block elements function html(io::IO, content::Vector) for md in content html(io, md) println(io) end end html(io::IO, md::MD) = html(io, md.content) function html(io::IO, header::Header{l}) where l withtag(io, "h$l") do htmlinline(io, header.text) end end function html(io::IO, code::Code) withtag(io, :pre) do maybe_lang = !isempty(code.language) ? Any[:class=>"language-$(code.language)"] : [] withtag(io, :code, maybe_lang...) do htmlesc(io, code.code) # TODO should print newline if this is longer than one line ? end end end function html(io::IO, md::Paragraph) withtag(io, :p) do htmlinline(io, md.content) end end function html(io::IO, md::BlockQuote) withtag(io, :blockquote) do println(io) html(io, md.content) end end function html(io::IO, f::Footnote) withtag(io, :div, :class => "footnote", :id => "footnote-$(f.id)") do withtag(io, :p, :class => "footnote-title") do print(io, f.id) end html(io, f.text) end end function html(io::IO, md::Admonition) withtag(io, :div, :class => "admonition $(md.category)") do withtag(io, :p, :class => "admonition-title") do print(io, md.title) end html(io, md.content) end end function html(io::IO, md::List) maybe_attr = md.ordered > 1 ? Any[:start => string(md.ordered)] : [] withtag(io, isordered(md) ? :ol : :ul, maybe_attr...) do for item in md.items println(io) withtag(io, :li) do html(io, item) end end println(io) end end function html(io::IO, md::HorizontalRule) tag(io, :hr) end html(io::IO, x) = tohtml(io, x) # Inline elements function htmlinline(io::IO, content::Vector) for x in content htmlinline(io, x) end end function htmlinline(io::IO, code::Code) withtag(io, :code) do htmlesc(io, code.code) end end function htmlinline(io::IO, md::Union{Symbol,AbstractString}) htmlesc(io, md) end function htmlinline(io::IO, md::Bold) withtag(io, :strong) do htmlinline(io, md.text) end end function htmlinline(io::IO, md::Italic) withtag(io, :em) do htmlinline(io, md.text) end end function htmlinline(io::IO, md::Image) tag(io, :img, :src=>md.url, :alt=>md.alt) end function htmlinline(io::IO, f::Footnote) withtag(io, :a, :href => "#footnote-$(f.id)", :class => "footnote") do print(io, "[", f.id, "]") end end function htmlinline(io::IO, link::Link) withtag(io, :a, :href=>link.url) do htmlinline(io, link.text) end end function htmlinline(io::IO, br::LineBreak) tag(io, :br) end htmlinline(io::IO, x) = tohtml(io, x) # API export html html(md) = sprint(html, md) function show(io::IO, ::MIME"text/html", md::MD) withtag(io, :div, :class=>"markdown") do html(io, md) end end w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/rich.jl������# This file is a part of Julia. License is MIT: https://julialang.org/license function tohtml(io::IO, m::MIME"text/html", x) show(io, m, x) end function tohtml(io::IO, m::MIME"text/plain", x) htmlesc(io, sprint(show, m, x)) end function tohtml(io::IO, m::MIME"image/png", img) print(io, """<img src="data:image/png;base64,""") print(io, stringmime(m, img)) print(io, "\" />") end function tohtml(io::IO, m::MIME"image/svg+xml", img) show(io, m, img) end # AbstractDisplay infrastructure function bestmime(val) for mime in ("text/html", "image/svg+xml", "image/png", "text/plain") showable(mime, val) && return MIME(Symbol(mime)) end error("Cannot render $val to Markdown.") end tohtml(io::IO, x) = tohtml(io, bestmime(x), x) x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/latex.jl๊������# This file is a part of Julia. License is MIT: https://julialang.org/license export latex function wrapblock(f, io, env) println(io, "\\begin{", env, "}") f() println(io, "\\end{", env, "}") end function wrapinline(f, io, cmd) print(io, "\\", cmd, "{") f() print(io, "}") end # Block elements latex(io::IO, md::MD) = latex(io, md.content) function latex(io::IO, content::Vector) for c in content latex(io, c) end end function latex(io::IO, header::Header{l}) where l tag = l < 4 ? "sub"^(l-1) * "section" : "sub"^(l-4) * "paragraph" wrapinline(io, tag) do latexinline(io, header.text) end println(io) end function latex(io::IO, code::Code) occursin("\\end{verbatim}", code.code) && error("Cannot include \"\\end{verbatim}\" in a latex code block") wrapblock(io, "verbatim") do println(io, code.code) end end function latexinline(io::IO, code::Code) wrapinline(io, "texttt") do print(io, latexesc(code.code)) end end function latex(io::IO, md::Paragraph) for mdc in md.content latexinline(io, mdc) end println(io) println(io) end function latex(io::IO, md::BlockQuote) wrapblock(io, "quote") do latex(io, md.content) end end function latex(io::IO, f::Footnote) print(io, "\\footnotetext[", f.id, "]{") latex(io, f.text) println(io, "}") end function latex(io::IO, md::Admonition) wrapblock(io, "quote") do wrapinline(io, "textbf") do print(io, md.category) end println(io, "\n\n", md.title, "\n") latex(io, md.content) end end function latex(io::IO, md::List) # `\begin{itemize}` is used here for both ordered and unordered lists since providing # custom starting numbers for enumerated lists is simpler to do by manually assigning # each number to `\item` ourselves rather than using `\setcounter{enumi}{<start>}`. # # For an ordered list starting at 5 the following will be generated: # # \begin{itemize} # \item[5. ] ... # \item[6. ] ... # ... # \end{itemize} # pad = ndigits(md.ordered + length(md.items)) + 2 fmt = n -> (isordered(md) ? "[$(rpad("$(n + md.ordered - 1).", pad))]" : "") wrapblock(io, "itemize") do for (n, item) in enumerate(md.items) print(io, "\\item$(fmt(n)) ") latex(io, item) n < length(md.items) && println(io) end end end function latex(io::IO, md::HorizontalRule) println(io, "\\rule{\\textwidth}{1pt}") end # Inline elements function latexinline(io::IO, md::Vector) for c in md latexinline(io, c) end end function latexinline(io::IO, md::AbstractString) latexesc(io, md) end function latexinline(io::IO, md::Bold) wrapinline(io, "textbf") do latexinline(io, md.text) end end function latexinline(io::IO, md::Italic) wrapinline(io, "emph") do latexinline(io, md.text) end end function latexinline(io::IO, md::Image) wrapblock(io, "figure") do println(io, "\\centering") wrapinline(io, "includegraphics") do print(io, md.url) end println(io) wrapinline(io, "caption") do latexinline(io, md.alt) end println(io) end end latexinline(io::IO, f::Footnote) = print(io, "\\footnotemark[", f.id, "]") function latexinline(io::IO, md::Link) wrapinline(io, "href") do print(io, md.url) end print(io, "{") latexinline(io, md.text) print(io, "}") end const _latexescape_chars = Dict{Char, AbstractString}( '~'=>"{\\textasciitilde}", '^'=>"\\^{}", '\\'=>"{\\textbackslash}") for ch in "&%\$#_{}" _latexescape_chars[ch] = "\\$ch" end function latexesc(io, s::AbstractString) for ch in s print(io, get(_latexescape_chars, ch, ch)) end end latex(md) = sprint(latex, md) latexinline(md) = sprint(latexinline, md) latexesc(s) = sprint(latexesc, s) show(io::IO, ::MIME"text/latex", md::MD) = latex(io, md) v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/rst.jl๕������# This file is a part of Julia. License is MIT: https://julialang.org/license rst(x) = sprint(rst, x) function rst(io::IO, content::Vector) isempty(content) && return for md in content[1:end-1] rst(io, md) println(io) end rst(io, content[end]) end rst(io::IO, md::MD) = rst(io, md.content) function rst(io::IO, header::Header{l}) where l s = rstinline(header.text) println(io, s) println(io, string("*=-~:.^"[l])^length(s)) println(io) end function rst(io::IO, code::Code) if code.language == "jldoctest" println(io, ".. doctest::\n") elseif code.language != "rst" println(io, ".. code-block:: julia\n") end for l in lines(code.code) println(io, " ", l) end end function rst(io::IO, p::Paragraph) rstinline(io, p.content) println(io) end function rst(io::IO, list::List) for (i, item) in enumerate(list.items) bullet = isordered(list) ? "$(i + list.ordered - 1). " : "* " print(io, bullet) lines = split(rstrip(sprint(rst, item)), '\n') for (n, line) in enumerate(lines) print(io, (n == 1 || isempty(line)) ? "" : " "^length(bullet), line) n < length(lines) && println(io) end println(io) end end function rst(io::IO, q::BlockQuote) s = sprint(rst, q.content) for line in split(rstrip(s), "\n") println(io, " ", line) end println(io) end function rst(io::IO, f::Footnote) print(io, ".. [", f.id, "]") s = sprint(rst, f.text) lines = split(rstrip(s), "\n") # Single line footnotes are printed on the same line as their label # rather than taking up an additional line. if length(lines) == 1 println(io, " ", lines[1]) else println(io) for line in lines println(io, isempty(line) ? "" : " ", rstrip(line)) end println(io) end end function rst(io::IO, md::Admonition) s = sprint(rst, md.content) title = md.title == uppercasefirst(md.category) ? "" : md.title println(io, ".. ", md.category, "::", isempty(title) ? "" : " $title") for line in split(rstrip(s), "\n") println(io, isempty(line) ? "" : " ", line) end println(io) end function rst(io::IO, md::HorizontalRule) println(io, "โ€“" ^ 5) end function rst(io::IO, l::LaTeX) println(io, ".. math::\n") for line in lines(l.formula) println(io, " ", line) end end rst(io::IO, md) = show(io, "text/rst", md) # Inline elements rstinline(x) = sprint(rstinline, x) function rstinline(io::IO, md...) wasCode = false for el in md wasCode && isa(el, AbstractString) && !Base.startswith(el, " ") && print(io, "\\ ") wasCode = (isa(el, Code) || isa(el, LaTeX) || isa(el, Link)) && (wasCode = true) rstinline(io, el) end end rstinline(io::IO, md::Vector) = !isempty(md) && rstinline(io, md...) # rstinline(io::IO, md::Image) = rstinline(io, ".. image:: ", md.url) function rstinline(io::IO, md::Link) if occursin(r":(func|obj|ref|exc|class|const|data):`\.*", md.url) rstinline(io, md.url) else rstinline(io, "`", md.text, " <", md.url, ">`_") end end rstinline(io::IO, f::Footnote) = print(io, "[", f.id, "]_") rstescape(s) = replace(s, "\\" => "\\\\") rstinline(io::IO, s::AbstractString) = print(io, rstescape(s)) rstinline(io::IO, md::Bold) = rstinline(io, "**", md.text, "**") rstinline(io::IO, md::Italic) = rstinline(io, "*", md.text, "*") rstinline(io::IO, md::Code) = print(io, "``", md.code, "``") rstinline(io::IO, br::LineBreak) = println(io) rstinline(io::IO, l::LaTeX) = print(io, ":math:`", l.formula, "`") rstinline(io::IO, x) = show(io, MIME"text/rst"(), x) # show Base.show(io::IO, ::MIME"text/rst", md::MD) = rst(io, md) ‚���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/terminal/render.jlK������# This file is a part of Julia. License is MIT: https://julialang.org/license include("formatting.jl") cols(io) = displaysize(io)[2] function term(io::IO, content::Vector, cols) isempty(content) && return for md in content[1:end-1] term(io, md, cols) print(io, '\n', '\n') end term(io, content[end], cols) end term(io::IO, md::MD, columns = cols(io)) = term(io, md.content, columns) function term(io::IO, md::Paragraph, columns) print(io, ' '^margin) print_wrapped(io, width = columns-2margin, pre = ' '^margin) do io terminline(io, md.content) end end function term(io::IO, md::BlockQuote, columns) s = sprint(term, md.content, columns - 10; context=io) lines = split(rstrip(s), '\n') print(io, ' '^margin, 'โ”‚', lines[1]) for i = 2:length(lines) print(io, '\n', ' '^margin, 'โ”‚', lines[i]) end end function term(io::IO, md::Admonition, columns) col = :default # If the types below are modified, the page manual/documentation.md must be updated accordingly. if md.category == "danger" col = Base.error_color() elseif md.category == "warning" col = Base.warn_color() elseif md.category in ("info", "note") col = Base.info_color() elseif md.category == "tip" col = :green end printstyled(io, ' '^margin, "โ”‚ "; color=col, bold=true) printstyled(io, isempty(md.title) ? md.category : md.title; color=col, bold=true) printstyled(io, '\n', ' '^margin, 'โ”‚', '\n'; color=col, bold=true) s = sprint(term, md.content, columns - 10; context=io) lines = split(rstrip(s), '\n') for i in eachindex(lines) printstyled(io, ' '^margin, 'โ”‚'; color=col, bold=true) print(io, lines[i]) i < lastindex(lines) && println(io) end end function term(io::IO, f::Footnote, columns) print(io, ' '^margin, "โ”‚ ") printstyled(io, "[^$(f.id)]", bold=true) println(io, '\n', ' '^margin, 'โ”‚') s = sprint(term, f.text, columns - 10; context=io) lines = split(rstrip(s), '\n') for i in eachindex(lines) print(io, ' '^margin, 'โ”‚', lines[i]) i < lastindex(lines) && println(io) end end function term(io::IO, md::List, columns) for (i, point) in enumerate(md.items) print(io, ' '^2margin, isordered(md) ? "$(i + md.ordered - 1). " : "โ€ข ") print_wrapped(io, width = columns-(4margin+2), pre = ' '^(2margin+3), i = 2margin+2) do io term(io, point, columns - 10) end i < lastindex(md.items) && print(io, '\n', '\n') end end function _term_header(io::IO, md, char, columns) text = terminline_string(io, md.text) with_output_color(:bold, io) do io pre = ' '^margin print(io, pre) line_no, lastline_width = print_wrapped(io, text, width=columns - 4margin; pre) line_width = min(lastline_width, columns) if line_no > 1 line_width = max(line_width, div(columns, 3)+length(pre)) end header_width = max(0, line_width-length(pre)) char != ' ' && header_width > 0 && print(io, '\n', ' '^(margin), char^header_width) end end const _header_underlines = collect("โ‰ก=โ€“-โ‹… ") # TODO settle on another option with unicode e.g. "โ‰ก=โ‰ƒโ€“โˆผโ‹…" ? function term(io::IO, md::Header{l}, columns) where l underline = _header_underlines[l] _term_header(io, md, underline, columns) end function term(io::IO, md::Code, columns) with_output_color(:cyan, io) do io L = lines(md.code) for i in eachindex(L) print(io, ' '^margin, L[i]) i < lastindex(L) && println(io) end end end function term(io::IO, tex::LaTeX, columns) printstyled(io, ' '^margin, tex.formula, color=:magenta) end term(io::IO, br::LineBreak, columns) = nothing # line breaks already printed between subsequent elements function term(io::IO, br::HorizontalRule, columns) print(io, ' '^margin, 'โ”€'^(columns - 2margin)) end term(io::IO, x, _) = show(io, MIME"text/plain"(), x) # Inline Content terminline_string(io::IO, md) = sprint(terminline, md; context=io) terminline(io::IO, content...) = terminline(io, collect(content)) function terminline(io::IO, content::Vector) for md in content terminline(io, md) end end function terminline(io::IO, md::AbstractString) print(io, replace(md, r"[\s\t\n]+" => ' ')) end function terminline(io::IO, md::Bold) with_output_color(terminline, :bold, io, md.text) end function terminline(io::IO, md::Italic) with_output_color(terminline, :underline, io, md.text) end function terminline(io::IO, md::LineBreak) println(io) end function terminline(io::IO, md::Image) terminline(io, "(Image: $(md.alt))") end terminline(io::IO, f::Footnote) = with_output_color(terminline, :bold, io, "[^$(f.id)]") function terminline(io::IO, md::Link) url = !Base.startswith(md.url, "@ref") ? " ($(md.url))" : "" text = terminline_string(io, md.text) terminline(io, text, url) end function terminline(io::IO, code::Code) printstyled(io, code.code, color=:cyan) end function terminline(io::IO, tex::LaTeX) printstyled(io, tex.formula, color=:magenta) end terminline(io::IO, x) = show(io, MIME"text/plain"(), x) # Show in terminal Base.show(io::IO, ::MIME"text/plain", md::MD) = (term(io, md); nothing) †���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Markdown/src/render/terminal/formatting.jl ������# This file is a part of Julia. License is MIT: https://julialang.org/license # Wrapping function ansi_length(s) replace(s, r"\e\[[0-9]+m" => "") |> textwidth end words(s) = split(s, " ") lines(s) = split(s, "\n") function wrapped_line(io::IO, s::AbstractString, width, i) ws = words(s) lines = String[] for word in ws word_length = ansi_length(word) word_length == 0 && continue if isempty(lines) || i + word_length + 1 > width i = word_length if length(lines) > 0 last_line = lines[end] maybe_underline = findlast(Base.text_colors[:underline], last_line) if !isnothing(maybe_underline) # disable underline style at end of line if not already disabled. maybe_disable_underline = max( last(something(findlast(Base.disable_text_style[:underline], last_line), -1)), last(something(findlast(Base.text_colors[:normal], last_line), -1)), ) if maybe_disable_underline < 0 || maybe_disable_underline < last(maybe_underline) lines[end] = last_line * Base.disable_text_style[:underline] word = Base.text_colors[:underline] * word end end end push!(lines, word) else i += word_length + 1 lines[end] *= " " * word # this could be more efficient end end return i, lines end function wrapped_lines(io::IO, s::AbstractString; width = 80, i = 0) ls = String[] for ss in lines(s) i, line = wrapped_line(io, ss, width, i) append!(ls, line) end return ls end wrapped_lines(io::IO, f::Function, args...; width = 80, i = 0) = wrapped_lines(io, sprint(f, args...; context=io), width = width, i = 0) function print_wrapped(io::IO, s...; width = 80, pre = "", i = 0) lines = wrapped_lines(io, s..., width = width, i = i) isempty(lines) && return 0, 0 print(io, lines[1]) for line in lines[2:end] print(io, '\n', pre, line) end length(lines), length(pre) + ansi_length(lines[end]) end print_wrapped(f::Function, io::IO, args...; kws...) = print_wrapped(io, f, args...; kws...) p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Printf/src/Printf.jlƒ������# This file is a part of Julia. License is MIT: https://julialang.org/license module Printf using Base.Ryu export @printf, @sprintf # format specifier categories const Ints = Union{Val{'d'}, Val{'i'}, Val{'u'}, Val{'x'}, Val{'X'}, Val{'o'}} const Floats = Union{Val{'e'}, Val{'E'}, Val{'f'}, Val{'F'}, Val{'g'}, Val{'G'}, Val{'a'}, Val{'A'}} const Chars = Union{Val{'c'}, Val{'C'}} const Strings = Union{Val{'s'}, Val{'S'}} const Pointer = Val{'p'} const HexBases = Union{Val{'x'}, Val{'X'}, Val{'a'}, Val{'A'}} const PositionCounter = Val{'n'} const MAX_FRACTIONAL_PART_WIDTH = 17 # max significant decimals + 1: `ceil(Int, log10(1 / eps(Float64))) + 1` const MAX_INTEGER_PART_WIDTH = 309 # max exponent: `ceil(Int, log10(prevfloat(typemax(Float64))))` const MAX_FMT_CHARS_WIDTH = 5 # hash | sign +/- | decimal dot | exponent e/E | exponent sign """ Typed representation of a format specifier. `T` is a `Val{'_'}`, where `_` is a valid format specifier character. Fields are the various modifiers allowed for various format specifiers. """ struct Spec{T} # T => %type => Val{'type'} leftalign::Bool plus::Bool space::Bool zero::Bool hash::Bool width::Int precision::Int dynamic_width::Bool dynamic_precision::Bool end # recreate the format specifier string from a typed Spec Base.string(f::Spec{T}; modifier::String="") where {T} = string("%", f.leftalign ? "-" : "", f.plus ? "+" : "", f.space ? " " : "", f.zero ? "0" : "", f.hash ? "#" : "", f.dynamic_width ? "*" : (f.width > 0 ? f.width : ""), f.dynamic_precision ? ".*" : (f.precision == 0 ? ".0" : (f.precision > 0 ? ".$(f.precision)" : "")), modifier, char(T)) Base.show(io::IO, f::Spec) = print(io, string(f)) floatfmt(s::Spec{T}) where {T} = Spec{Val{'f'}}(s.leftalign, s.plus, s.space, s.zero, s.hash, s.width, 0, s.dynamic_width, s.dynamic_precision) ptrfmt(s::Spec{T}, x) where {T} = Spec{Val{'x'}}(s.leftalign, s.plus, s.space, s.zero, true, s.width, sizeof(x) == 8 ? 16 : 8, s.dynamic_width, s.dynamic_precision) """ Printf.Format(format_str) Create a C printf-compatible format object that can be used for formatting values. The input `format_str` can include any valid format specifier character and modifiers. A `Format` object can be passed to `Printf.format(f::Format, args...)` to produce a formatted string, or `Printf.format(io::IO, f::Format, args...)` to print the formatted string directly to `io`. For convenience, the `Printf.format"..."` string macro form can be used for building a `Printf.Format` object at macro-expansion-time. !!! compat "Julia 1.6" `Printf.Format` requires Julia 1.6 or later. """ struct Format{S, T} str::S # original full format string as CodeUnits # keep track of non-format specifier strings to print # length(substringranges) == length(formats) + 1 # so when printing, we start with printing # str[substringranges[1]], then formats[1] + args[1] # then str[substringranges[2]], then formats[2] + args[2] # and so on, then at the end, str[substringranges[end]] substringranges::Vector{UnitRange{Int}} formats::T # Tuple of Specs numarguments::Int # required for dynamic format specifiers end # what number base should be used for a given format specifier? base(T) = T <: HexBases ? 16 : T <: Val{'o'} ? 8 : 10 char(::Type{Val{c}}) where {c} = c struct InvalidFormatStringError <: Exception message::String format::String start_color::Int end_color::Int end function Base.showerror(io::IO, err::InvalidFormatStringError) io_has_color = get(io, :color, false)::Bool println(io, "InvalidFormatStringError: ", err.message) print(io, " \"", @view(err.format[begin:prevind(err.format, err.start_color)])) invalid_text = @view err.format[err.start_color:err.end_color] printstyled(io, invalid_text, color=:red) # +1 is okay, since all format characters are single bytes println(io, @view(err.format[err.end_color+1:end]), "\"") arrow_error = '-'^(length(invalid_text)-1) arrow = " " * ' '^err.start_color * arrow_error * "^\n" if io_has_color printstyled(io, arrow, color=:red) else print(io, arrow) end end # parse format string function Format(f::AbstractString) bytes = codeunits(f) len = length(bytes) pos = 1 numarguments = 0 b = 0x00 local last_percent_pos # skip ahead to first format specifier while pos <= len b = bytes[pos] pos += 1 if b == UInt8('%') last_percent_pos = pos-1 pos > len && throw(InvalidFormatStringError("Format specifier is incomplete", f, last_percent_pos, last_percent_pos)) if bytes[pos] == UInt8('%') # escaped '%' b = bytes[pos] pos += 1 else break end end end strs = [1:pos - 1 - (b == UInt8('%'))] fmts = [] while pos <= len b = bytes[pos] pos += 1 # positioned at start of first format str % # parse flags leftalign = plus = space = zero = hash = false while true if b == UInt8('-') leftalign = true elseif b == UInt8('+') plus = true elseif b == UInt8(' ') space = true elseif b == UInt8('0') zero = true elseif b == UInt8('#') hash = true else break end pos > len && throw(InvalidFormatStringError("Format specifier is incomplete", f, last_percent_pos, pos-1)) b = bytes[pos] pos += 1 end if leftalign zero = false end # parse width width = 0 dynamic_width = false if b == UInt8('*') dynamic_width = true numarguments += 1 b = bytes[pos] pos += 1 else while b - UInt8('0') < 0x0a width = 10 * width + (b - UInt8('0')) b = bytes[pos] pos += 1 pos > len && break end end # parse precision precision = 0 parsedprecdigits = false dynamic_precision = false if b == UInt8('.') pos > len && throw(InvalidFormatStringError("Precision specifier is missing precision", f, last_percent_pos, pos-1)) parsedprecdigits = true b = bytes[pos] pos += 1 if pos <= len if b == UInt8('*') dynamic_precision = true numarguments += 1 b = bytes[pos] pos += 1 else precision = 0 while b - UInt8('0') < 0x0a precision = 10precision + (b - UInt8('0')) b = bytes[pos] pos += 1 pos > len && break end end end end # parse length modifier (ignored) if b == UInt8('h') || b == UInt8('l') prev = b pos > len && throw(InvalidFormatStringError("Length modifier is missing type specifier", f, last_percent_pos, pos-1)) b = bytes[pos] pos += 1 if b == prev pos > len && throw(InvalidFormatStringError("Length modifier is missing type specifier", f, last_percent_pos, pos-1)) b = bytes[pos] pos += 1 end elseif b in b"Ljqtz" # q was a synonym for ll above, see `man 3 printf`. Not to be used. pos > len && throw(InvalidFormatStringError("Length modifier is missing type specifier", f, last_percent_pos, pos-1)) b = bytes[pos] pos += 1 end # parse type !(b in b"diouxXDOUeEfFgGaAcCsSpn") && throw(InvalidFormatStringError("'$(Char(b))' is not a valid type specifier", f, last_percent_pos, pos-1)) type = Val{Char(b)} if type <: Ints && precision > 0 # note - we should also set zero to false if dynamic precison > 0 # this is taken care of in fmt() for Ints zero = false elseif (type <: Strings || type <: Chars) && !parsedprecdigits precision = -1 elseif type <: Union{Val{'a'}, Val{'A'}} && !parsedprecdigits precision = -1 elseif type <: Floats && !parsedprecdigits precision = 6 end numarguments += 1 push!(fmts, Spec{type}(leftalign, plus, space, zero, hash, width, precision, dynamic_width, dynamic_precision)) start = pos while pos <= len b = bytes[pos] pos += 1 if b == UInt8('%') last_percent_pos = pos-1 pos > len && throw(InvalidFormatStringError("Format specifier is incomplete", f, last_percent_pos, last_percent_pos)) if bytes[pos] == UInt8('%') # escaped '%' b = bytes[pos] pos += 1 else break end end end push!(strs, start:pos - 1 - (b == UInt8('%'))) end return Format(bytes, strs, Tuple(fmts), numarguments) end macro format_str(str) Format(str) end const hex = b"0123456789abcdef" const HEX = b"0123456789ABCDEF" # write out a single arg according to format options # char @inline function writechar(buf, pos, c) u = bswap(reinterpret(UInt32, c)) while true buf[pos] = u % UInt8 pos += 1 (u >>= 8) == 0 && break end return pos end @inline function rmdynamic(spec::Spec{T}, args, argp) where {T} zero, width, precision = spec.zero, spec.width, spec.precision if spec.dynamic_width width = args[argp] argp += 1 end if spec.dynamic_precision precision = args[argp] if zero && T <: Ints && precision > 0 zero = false end argp += 1 end (Spec{T}(spec.leftalign, spec.plus, spec.space, zero, spec.hash, width, precision, false, false), argp) end @inline function fmt(buf, pos, args, argp, spec::Spec{T}) where {T} spec, argp = rmdynamic(spec, args, argp) (fmt(buf, pos, args[argp], spec), argp+1) end @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Chars} leftalign, width = spec.leftalign, spec.width c = Char(first(arg)) w = textwidth(c) if !leftalign && width > w for _ = 1:(width - w) buf[pos] = UInt8(' ') pos += 1 end end pos = writechar(buf, pos, c) if leftalign && width > w for _ = 1:(width - w) buf[pos] = UInt8(' ') pos += 1 end end return pos end # strings @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Strings} leftalign, hash, width, prec = spec.leftalign, spec.hash, spec.width, spec.precision str = string(arg) slen = textwidth(str)::Int + (hash ? arg isa AbstractString ? 2 : 1 : 0) op = p = prec == -1 ? slen : min(slen, prec) if !leftalign && width > p for _ = 1:(width - p) buf[pos] = UInt8(' ') pos += 1 end end if hash if arg isa Symbol buf[pos] = UInt8(':') pos += 1 p -= 1 elseif arg isa AbstractString buf[pos] = UInt8('"') pos += 1 p -= 1 end end for c in str p -= textwidth(c) p < 0 && break pos = writechar(buf, pos, c) end if hash && arg isa AbstractString && p > 0 buf[pos] = UInt8('"') pos += 1 end if leftalign && width > op for _ = 1:(width - op) buf[pos] = UInt8(' ') pos += 1 end end return pos end # integers toint(x) = x toint(x::Rational) = Integer(x) fmt(buf, pos, arg::AbstractFloat, spec::Spec{T}) where {T <: Ints} = fmt(buf, pos, arg, floatfmt(spec)) @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Ints} leftalign, plus, space, zero, hash, width, prec = spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision bs = base(T) arg2 = toint(arg) n = i = ndigits(arg2, base=bs, pad=1) neg = arg2 < 0 x = arg2 isa Base.BitSigned ? unsigned(abs(arg2)) : abs(arg2) arglen = n + (neg || (plus | space)) + (T == Val{'o'} && hash ? 1 : 0) + (T == Val{'x'} && hash ? 2 : 0) + (T == Val{'X'} && hash ? 2 : 0) arglen2 = arglen < width && prec > 0 ? arglen + min(max(0, prec - n), width - arglen) : arglen if !leftalign && !zero && arglen2 < width # pad left w/ spaces for _ = 1:(width - arglen2) buf[pos] = UInt8(' ') pos += 1 end end if neg buf[pos] = UInt8('-'); pos += 1 elseif plus # plus overrides space buf[pos] = UInt8('+'); pos += 1 elseif space buf[pos] = UInt8(' '); pos += 1 end if T == Val{'o'} && hash buf[pos] = UInt8('0') pos += 1 elseif T == Val{'x'} && hash buf[pos] = UInt8('0') buf[pos + 1] = UInt8('x') pos += 2 elseif T == Val{'X'} && hash buf[pos] = UInt8('0') buf[pos + 1] = UInt8('X') pos += 2 end if zero && arglen2 < width for _ = 1:(width - arglen2) buf[pos] = UInt8('0') pos += 1 end elseif n < prec for _ = 1:(prec - n) buf[pos] = UInt8('0') pos += 1 end elseif arglen < arglen2 for _ = 1:(arglen2 - arglen) buf[pos] = UInt8('0') pos += 1 end end while i > 0 @inbounds buf[pos + i - 1] = bs == 16 ? (T == Val{'x'} ? hex[(x & 0x0f) + 1] : HEX[(x & 0x0f) + 1]) : (48 + (bs == 8 ? (x & 0x07) : rem(x, 10))) if bs == 8 x >>= 3 elseif bs == 16 x >>= 4 else x = oftype(x, div(x, 10)) end i -= 1 end pos += n if leftalign && arglen2 < width # pad right for _ = 1:(width - arglen2) buf[pos] = UInt8(' ') pos += 1 end end return pos end # floats """ Printf.tofloat(x) Convert an argument to a Base float type for printf formatting. By default, arguments are converted to `Float64` via `Float64(x)`. Custom numeric types that have a conversion to a Base float type that wish to hook into printf formatting can extend this method like: ```julia Printf.tofloat(x::MyCustomType) = convert_my_custom_type_to_float(x) ``` For arbitrary precision numerics, you might extend the method like: ```julia Printf.tofloat(x::MyArbitraryPrecisionType) = BigFloat(x) ``` !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ tofloat(x) = Float64(x) tofloat(x::Base.IEEEFloat) = x tofloat(x::BigFloat) = x _snprintf(ptr, siz, str, arg) = @ccall "libmpfr".mpfr_snprintf(ptr::Ptr{UInt8}, siz::Csize_t, str::Ptr{UInt8}; arg::Ref{BigFloat})::Cint # Arbitrary constant for a maximum number of bytes we want to output for a BigFloat. # 8KiB seems like a reasonable default. Larger BigFloat representations should probably # use a custom printing routine. Printing values with results larger than this ourselves # seems like a dangerous thing to do. const __BIG_FLOAT_MAX__ = 8192 @inline function fmt(buf, pos, arg, spec::Spec{T}) where {T <: Floats} leftalign, plus, space, zero, hash, width, prec = spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision x = tofloat(arg) if x isa BigFloat if isfinite(x) GC.@preserve buf begin siz = length(buf) - pos + 1 str = string(spec; modifier="R") required_length = _snprintf(pointer(buf, pos), siz, str, x) if required_length > siz required_length > __BIG_FLOAT_MAX__ && throw(ArgumentError("The given BigFloat requires $required_length bytes to be printed, which is more than the maximum of $__BIG_FLOAT_MAX__ bytes supported.")) resize!(buf, required_length + 1) required_length = _snprintf(pointer(buf, pos), required_length + 1, str, x) end required_length > 0 || throw(ArgumentError("The given BigFloat would produce less than the maximum allowed number of bytes $__BIG_FLOAT_MAX__, but still couldn't be printed fully for an unknown reason.")) return pos + required_length end end x = Float64(x) end if T == Val{'e'} || T == Val{'E'} newpos = Ryu.writeexp(buf, pos, x, prec, plus, space, hash, char(T), UInt8('.')) elseif T == Val{'f'} || T == Val{'F'} newpos = Ryu.writefixed(buf, pos, x, prec, plus, space, hash, UInt8('.')) elseif T == Val{'g'} || T == Val{'G'} if isinf(x) || isnan(x) newpos = Ryu.writeshortest(buf, pos, x, plus, space) else # C11-compliant general format prec = prec == 0 ? 1 : prec # format the value in scientific notation and parse the exponent part exp = let p = Ryu.writeexp(buf, pos, x, prec) b1, b2, b3, b4 = buf[p-4], buf[p-3], buf[p-2], buf[p-1] Z = UInt8('0') if b1 == UInt8('e') # two-digit exponent sign = b2 == UInt8('+') ? 1 : -1 exp = 10 * (b3 - Z) + (b4 - Z) else # three-digit exponent sign = b1 == UInt8('+') ? 1 : -1 exp = 100 * (b2 - Z) + 10 * (b3 - Z) + (b4 - Z) end flipsign(exp, sign) end if -4 โ‰ค exp < prec newpos = Ryu.writefixed(buf, pos, x, prec - (exp + 1), plus, space, hash, UInt8('.'), !hash) else newpos = Ryu.writeexp(buf, pos, x, prec - 1, plus, space, hash, T == Val{'g'} ? UInt8('e') : UInt8('E'), UInt8('.'), !hash) end end elseif T == Val{'a'} || T == Val{'A'} x, neg = x < 0 || x === -Base.zero(x) ? (-x, true) : (x, false) newpos = pos if neg buf[newpos] = UInt8('-') newpos += 1 elseif plus buf[newpos] = UInt8('+') newpos += 1 elseif space buf[newpos] = UInt8(' ') newpos += 1 end if isnan(x) buf[newpos] = UInt8('N') buf[newpos + 1] = UInt8('a') buf[newpos + 2] = UInt8('N') newpos += 3 elseif !isfinite(x) buf[newpos] = UInt8('I') buf[newpos + 1] = UInt8('n') buf[newpos + 2] = UInt8('f') newpos += 3 else buf[newpos] = UInt8('0') newpos += 1 buf[newpos] = T <: Val{'a'} ? UInt8('x') : UInt8('X') newpos += 1 if arg == 0 buf[newpos] = UInt8('0') newpos += 1 if prec > 0 buf[newpos] = UInt8('.') newpos += 1 while prec > 0 buf[newpos] = UInt8('0') newpos += 1 prec -= 1 end end buf[newpos] = T <: Val{'a'} ? UInt8('p') : UInt8('P') buf[newpos + 1] = UInt8('+') buf[newpos + 2] = UInt8('0') newpos += 3 else if prec > -1 s, p = frexp(x) sigbits = 4 * min(prec, 13) s = 0.25 * round(ldexp(s, 1 + sigbits)) # ensure last 2 exponent bits either 01 or 10 u = (reinterpret(UInt64, s) & 0x003f_ffff_ffff_ffff) >> (52 - sigbits) i = n = (sizeof(u) << 1) - (leading_zeros(u) >> 2) else s, p = frexp(x) s *= 2.0 u = (reinterpret(UInt64, s) & 0x001f_ffff_ffff_ffff) t = (trailing_zeros(u) >> 2) u >>= (t << 2) i = n = 14 - t end frac = u > 9 || hash || prec > 0 while i > 1 buf[newpos + i] = T == Val{'a'} ? hex[(u & 0x0f) + 1] : HEX[(u & 0x0f) + 1] u >>= 4 i -= 1 prec -= 1 end if frac buf[newpos + 1] = UInt8('.') end buf[newpos] = T == Val{'a'} ? hex[(u & 0x0f) + 1] : HEX[(u & 0x0f) + 1] newpos += n + frac while prec > 0 buf[newpos] = UInt8('0') newpos += 1 prec -= 1 end buf[newpos] = T <: Val{'a'} ? UInt8('p') : UInt8('P') newpos += 1 p -= 1 buf[newpos] = p < 0 ? UInt8('-') : UInt8('+') p = p < 0 ? -p : p newpos += 1 n = i = ndigits(p, base=10, pad=1) while i > 0 buf[newpos + i - 1] = 48 + rem(p, 10) p = oftype(p, div(p, 10)) i -= 1 end newpos += n end end end if newpos - pos < width # need to pad if leftalign # easy case, just pad spaces after number for _ = 1:(width - (newpos - pos)) buf[newpos] = UInt8(' ') newpos += 1 end else # right aligned n = width - (newpos - pos) if zero ex = (arg < 0 || (plus | space)) + (T <: Union{Val{'a'}, Val{'A'}} ? 2 : 0) so = pos + ex len = (newpos - pos) - ex copyto!(buf, so + n, buf, so, len) for i = so:(so + n - 1) buf[i] = UInt8('0') end newpos += n else copyto!(buf, pos + n, buf, pos, newpos - pos) for i = pos:(pos + n - 1) buf[i] = UInt8(' ') end newpos += n end end end return newpos end # pointers fmt(buf, pos, arg, spec::Spec{Pointer}) = fmt(buf, pos, UInt64(arg), ptrfmt(spec, arg)) # position counters function fmt(buf, pos, arg::Ref{<:Integer}, ::Spec{PositionCounter}) arg[] = pos - 1 pos end # old Printf compat function fix_dec end function ini_dec end # generic fallback function fmtfallback(buf, pos, arg, spec::Spec{T}) where {T} leftalign, plus, space, zero, hash, width, prec = spec.leftalign, spec.plus, spec.space, spec.zero, spec.hash, spec.width, spec.precision buf2 = Base.StringVector( MAX_INTEGER_PART_WIDTH + MAX_FRACTIONAL_PART_WIDTH + MAX_FMT_CHARS_WIDTH ) ise = T <: Union{Val{'e'}, Val{'E'}} isg = T <: Union{Val{'g'}, Val{'G'}} isf = T <: Val{'f'} if isg prec = prec == 0 ? 1 : prec arg = round(arg, sigdigits=prec) end n, pt, neg = isf ? fix_dec(arg, prec, buf2) : ini_dec(arg, min(prec + ise, length(buf2) - 1), buf2) if isg && !hash while buf2[n] == UInt8('0') n -= 1 end end expform = ise || (isg && !(-4 < pt <= prec)) n2 = n + (expform ? 4 : 0) + (prec > 0 || hash) + (neg || (plus | space)) + (isf && pt >= n ? prec + 1 : 0) if !leftalign && !zero && n2 < width # pad left w/ spaces for _ = 1:(width - n2) buf[pos] = UInt8(' ') pos += 1 end end if neg buf[pos] = UInt8('-'); pos += 1 elseif plus # plus overrides space buf[pos] = UInt8('+'); pos += 1 elseif space buf[pos] = UInt8(' '); pos += 1 end if zero && n2 < width for _ = 1:(width - n2) buf[pos] = UInt8('0') pos += 1 end end if expform buf[pos] = buf2[1] pos += 1 if n > 1 || hash buf[pos] = UInt8('.') pos += 1 for i = 2:n buf[pos] = buf2[i] pos += 1 end end buf[pos] = T <: Val{'e'} || T <: Val{'g'} ? UInt8('e') : UInt8('E') pos += 1 exp = pt - 1 buf[pos] = exp < 0 ? UInt8('-') : UInt8('+') pos += 1 exp = abs(exp) if exp < 10 buf[pos] = UInt8('0') buf[pos + 1] = 48 + exp pos += 2 else buf[pos] = 48 + div(exp, 10) buf[pos + 1] = 48 + rem(exp, 10) pos += 2 end elseif pt <= 0 buf[pos] = UInt8('0') buf[pos + 1] = UInt8('.') pos += 2 while pt < 0 buf[pos] = UInt8('0') pos += 1 pt += 1 end for i = 1:n buf[pos] = buf2[i] pos += 1 end elseif pt >= n for i = 1:n buf[pos] = buf2[i] pos += 1 end while pt > n buf[pos] = UInt8('0') pos += 1 n += 1 end if hash || (isf && prec > 0) buf[pos] = UInt8('.') pos += 1 while prec > 0 buf[pos] = UInt8('0') pos += 1 prec -= 1 end end else for i = 1:pt buf[pos] = buf2[i] pos += 1 end buf[pos] = UInt8('.') pos += 1 for i = pt+1:n buf[pos] = buf2[i] pos += 1 end end if leftalign && n2 < width # pad right for _ = 1:(width - n2) buf[pos] = UInt8(' ') pos += 1 end end return pos end const UNROLL_UPTO = 16 # if you have your own buffer + pos, write formatted args directly to it @inline function format(buf::Vector{UInt8}, pos::Integer, f::Format, args...) # write out first substring escapechar = false for i in f.substringranges[1] b = f.str[i] if !escapechar buf[pos] = b pos += 1 escapechar = b === UInt8('%') else escapechar = false end end # for each format, write out arg and next substring # unroll up to 16 formats N = length(f.formats) argp = 1 Base.@nexprs 16 i -> begin if N >= i pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar buf[pos] = b pos += 1 escapechar = b === UInt8('%') else escapechar = false end end end end if N > 16 for i = 17:length(f.formats) pos, argp = fmt(buf, pos, args, argp, f.formats[i]) for j in f.substringranges[i + 1] b = f.str[j] if !escapechar buf[pos] = b pos += 1 escapechar = b === UInt8('%') else escapechar = false end end end end return pos end @inline function plength(f::Spec{T}, args, argp) where {T} f, argp = rmdynamic(f, args, argp) (plength(f, args[argp]), argp+1) end function plength(f::Spec{T}, x) where {T <: Chars} c = Char(first(x)) w = textwidth(c) return max(f.width, w) + (ncodeunits(c) - w) end plength(f::Spec{Pointer}, x) = max(f.width, 2 * sizeof(x) + 2) function plength(f::Spec{T}, x) where {T <: Strings} str = string(x) sw = textwidth(str) p = f.precision == -1 ? (sw + (f.hash ? (x isa Symbol ? 1 : 2) : 0)) : f.precision return max(f.width, p) + (sizeof(str) - sw) end function plength(f::Spec{T}, x) where {T <: Ints} x2 = toint(x) return max( f.width, f.precision + ndigits(x2, base=base(T), pad=1) + MAX_FMT_CHARS_WIDTH ) end plength(f::Spec{T}, x::AbstractFloat) where {T <: Ints} = max(f.width, f.hash + MAX_INTEGER_PART_WIDTH + 0 + MAX_FMT_CHARS_WIDTH) plength(f::Spec{T}, x) where {T <: Floats} = max(f.width, f.hash + MAX_INTEGER_PART_WIDTH + f.precision + MAX_FMT_CHARS_WIDTH) plength(::Spec{PositionCounter}, x) = 0 @inline function computelen(substringranges, formats, args) len = sum(length, substringranges) N = length(formats) # unroll up to 16 formats argp = 1 Base.@nexprs 16 i -> begin if N >= i l, argp = plength(formats[i], args, argp) len += l end end if N > 16 for i = 17:length(formats) l, argp = plength(formats[i], args, argp) len += l end end return len end @noinline argmismatch(a, b) = throw(ArgumentError("Number of format specifiers and number of provided args differ: $a != $b")) """ Printf.format(f::Printf.Format, args...) => String Printf.format(io::IO, f::Printf.Format, args...) Apply a printf format object `f` to provided `args` and return the formatted string (1st method), or print directly to an `io` object (2nd method). See [`@printf`](@ref) for more details on C `printf` support. """ function format end function format(io::IO, f::Format, args...) # => Nothing f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) write(io, resize!(buf, pos - 1)) return end function format(f::Format, args...) # => String f.numarguments == length(args) || argmismatch(f.numarguments, length(args)) buf = Base.StringVector(computelen(f.substringranges, f.formats, args)) pos = format(buf, 1, f, args...) return String(resize!(buf, pos - 1)) end """ @printf([io::IO], "%Fmt", args...) Print `args` using C `printf` style format specification string. Optionally, an `IO` may be passed as the first argument to redirect output. # Examples ```jldoctest julia> @printf "Hello %s" "world" Hello world julia> @printf "Scientific notation %e" 1.234 Scientific notation 1.234000e+00 julia> @printf "Scientific notation three digits %.3e" 1.23456 Scientific notation three digits 1.235e+00 julia> @printf "Decimal two digits %.2f" 1.23456 Decimal two digits 1.23 julia> @printf "Padded to length 5 %5i" 123 Padded to length 5 123 julia> @printf "Padded with zeros to length 6 %06i" 123 Padded with zeros to length 6 000123 julia> @printf "Use shorter of decimal or scientific %g %g" 1.23 12300000.0 Use shorter of decimal or scientific 1.23 1.23e+07 julia> @printf "Use dynamic width and precision %*.*f" 10 2 0.12345 Use dynamic width and precision 0.12 ``` For a systematic specification of the format, see [here](https://en.cppreference.com/w/c/io/fprintf). See also [`@sprintf`](@ref) to get the result as a `String` instead of it being printed. # Caveats `Inf` and `NaN` are printed consistently as `Inf` and `NaN` for flags `%a`, `%A`, `%e`, `%E`, `%f`, `%F`, `%g`, and `%G`. Furthermore, if a floating point number is equally close to the numeric values of two possible output strings, the output string further away from zero is chosen. # Examples ```jldoctest julia> @printf("%f %F %f %F", Inf, Inf, NaN, NaN) Inf Inf NaN NaN julia> @printf "%.0f %.1f %f" 0.5 0.025 -0.0078125 0 0.0 -0.007812 ``` !!! compat "Julia 1.8" Starting in Julia 1.8, `%s` (string) and `%c` (character) widths are computed using [`textwidth`](@ref), which e.g. ignores zero-width characters (such as combining characters for diacritical marks) and treats certain "wide" characters (e.g. emoji) as width `2`. !!! compat "Julia 1.10" Dynamic width specifiers like `%*s` and `%0*.*f` require Julia 1.10. """ macro printf(io_or_fmt, args...) if io_or_fmt isa String fmt = Format(io_or_fmt) return esc(:($Printf.format(stdout, $fmt, $(args...)))) else io = io_or_fmt isempty(args) && throw(ArgumentError("No format string provided to `@printf` - use like `@printf [io] <format string> [<args...>].")) fmt_str = first(args) fmt_str isa String || throw(ArgumentError("First argument to `@printf` after `io` must be a format string")) fmt = Format(fmt_str) return esc(:($Printf.format($io, $fmt, $(Base.tail(args)...)))) end end """ @sprintf("%Fmt", args...) Return [`@printf`](@ref) formatted output as string. # Examples ```jldoctest julia> @sprintf "this is a %s %15.1f" "test" 34.567 "this is a test 34.6" ``` """ macro sprintf(fmt, args...) fmt isa String || throw(ArgumentError("First argument to `@sprintf` must be a format string.")) f = Format(fmt) return esc(:($Printf.format($f, $(args...)))) end end # module p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/Random.jl84������# This file is a part of Julia. License is MIT: https://julialang.org/license """ Random Support for generating random numbers. Provides [`rand`](@ref), [`randn`](@ref), [`AbstractRNG`](@ref), [`MersenneTwister`](@ref), and [`RandomDevice`](@ref). """ module Random include("DSFMT.jl") using .DSFMT using Base.GMP.MPZ using Base.GMP: Limb import SHA using Base: BitInteger, BitInteger_types, BitUnsigned, require_one_based_indexing import Base: copymutable, copy, copy!, ==, hash, convert, rand, randn, show export rand!, randn!, randexp, randexp!, bitrand, randstring, randsubseq, randsubseq!, shuffle, shuffle!, randperm, randperm!, randcycle, randcycle!, AbstractRNG, MersenneTwister, RandomDevice, TaskLocalRNG, Xoshiro ## general definitions """ AbstractRNG Supertype for random number generators such as [`MersenneTwister`](@ref) and [`RandomDevice`](@ref). """ abstract type AbstractRNG end Base.broadcastable(x::AbstractRNG) = Ref(x) gentype(::Type{X}) where {X} = eltype(X) gentype(x) = gentype(typeof(x)) ### integers # we define types which encode the generation of a specific number of bits # the "raw" version means that the unused bits are not zeroed abstract type UniformBits{T<:BitInteger} end struct UInt10{T} <: UniformBits{T} end struct UInt10Raw{T} <: UniformBits{T} end struct UInt23{T} <: UniformBits{T} end struct UInt23Raw{T} <: UniformBits{T} end struct UInt52{T} <: UniformBits{T} end struct UInt52Raw{T} <: UniformBits{T} end struct UInt104{T} <: UniformBits{T} end struct UInt104Raw{T} <: UniformBits{T} end struct UInt2x52{T} <: UniformBits{T} end struct UInt2x52Raw{T} <: UniformBits{T} end uint_sup(::Type{<:Union{UInt10,UInt10Raw}}) = UInt16 uint_sup(::Type{<:Union{UInt23,UInt23Raw}}) = UInt32 uint_sup(::Type{<:Union{UInt52,UInt52Raw}}) = UInt64 uint_sup(::Type{<:Union{UInt104,UInt104Raw}}) = UInt128 uint_sup(::Type{<:Union{UInt2x52,UInt2x52Raw}}) = UInt128 for UI = (:UInt10, :UInt10Raw, :UInt23, :UInt23Raw, :UInt52, :UInt52Raw, :UInt104, :UInt104Raw, :UInt2x52, :UInt2x52Raw) @eval begin $UI(::Type{T}=uint_sup($UI)) where {T} = $UI{T}() # useful for defining rand generically: uint_default(::$UI) = $UI{uint_sup($UI)}() end end gentype(::Type{<:UniformBits{T}}) where {T} = T ### floats abstract type FloatInterval{T<:AbstractFloat} end struct CloseOpen01{T<:AbstractFloat} <: FloatInterval{T} end # interval [0,1) struct CloseOpen12{T<:AbstractFloat} <: FloatInterval{T} end # interval [1,2) const FloatInterval_64 = FloatInterval{Float64} const CloseOpen01_64 = CloseOpen01{Float64} const CloseOpen12_64 = CloseOpen12{Float64} CloseOpen01(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen01{T}() CloseOpen12(::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen12{T}() gentype(::Type{<:FloatInterval{T}}) where {T<:AbstractFloat} = T const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} ### Sampler abstract type Sampler{E} end gentype(::Type{<:Sampler{E}}) where {E} = E # temporarily for BaseBenchmarks RangeGenerator(x) = Sampler(default_rng(), x) # In some cases, when only 1 random value is to be generated, # the optimal sampler can be different than if multiple values # have to be generated. Hence a `Repetition` parameter is used # to choose the best one depending on the need. const Repetition = Union{Val{1},Val{Inf}} # these default fall-back for all RNGs would be nice, # but generate difficult-to-solve ambiguities # Sampler(::AbstractRNG, X, ::Val{Inf}) = Sampler(X) # Sampler(::AbstractRNG, ::Type{X}, ::Val{Inf}) where {X} = Sampler(X) """ Sampler(rng, x, repetition = Val(Inf)) Return a sampler object that can be used to generate random values from `rng` for `x`. When `sp = Sampler(rng, x, repetition)`, `rand(rng, sp)` will be used to draw random values, and should be defined accordingly. `repetition` can be `Val(1)` or `Val(Inf)`, and should be used as a suggestion for deciding the amount of precomputation, if applicable. [`Random.SamplerType`](@ref) and [`Random.SamplerTrivial`](@ref) are default fallbacks for *types* and *values*, respectively. [`Random.SamplerSimple`](@ref) can be used to store pre-computed values without defining extra types for only this purpose. """ Sampler(rng::AbstractRNG, x, r::Repetition=Val(Inf)) = Sampler(typeof_rng(rng), x, r) Sampler(rng::AbstractRNG, ::Type{X}, r::Repetition=Val(Inf)) where {X} = Sampler(typeof_rng(rng), X, r) typeof_rng(rng::AbstractRNG) = typeof(rng) # this method is necessary to prevent rand(rng::AbstractRNG, X) from # recursively constructing nested Sampler types. Sampler(T::Type{<:AbstractRNG}, sp::Sampler, r::Repetition) = throw(MethodError(Sampler, (T, sp, r))) # default shortcut for the general case Sampler(::Type{RNG}, X) where {RNG<:AbstractRNG} = Sampler(RNG, X, Val(Inf)) Sampler(::Type{RNG}, ::Type{X}) where {RNG<:AbstractRNG,X} = Sampler(RNG, X, Val(Inf)) #### pre-defined useful Sampler types """ SamplerType{T}() A sampler for types, containing no other information. The default fallback for `Sampler` when called with types. """ struct SamplerType{T} <: Sampler{T} end Sampler(::Type{<:AbstractRNG}, ::Type{T}, ::Repetition) where {T} = SamplerType{T}() Base.getindex(::SamplerType{T}) where {T} = T struct SamplerTrivial{T,E} <: Sampler{E} self::T end """ SamplerTrivial(x) Create a sampler that just wraps the given value `x`. This is the default fall-back for values. The `eltype` of this sampler is equal to `eltype(x)`. The recommended use case is sampling from values without precomputed data. """ SamplerTrivial(x::T) where {T} = SamplerTrivial{T,gentype(T)}(x) Sampler(::Type{<:AbstractRNG}, x, ::Repetition) = SamplerTrivial(x) Base.getindex(sp::SamplerTrivial) = sp.self # simple sampler carrying data (which can be anything) struct SamplerSimple{T,S,E} <: Sampler{E} self::T data::S end """ SamplerSimple(x, data) Create a sampler that wraps the given value `x` and the `data`. The `eltype` of this sampler is equal to `eltype(x)`. The recommended use case is sampling from values with precomputed data. """ SamplerSimple(x::T, data::S) where {T,S} = SamplerSimple{T,S,gentype(T)}(x, data) Base.getindex(sp::SamplerSimple) = sp.self # simple sampler carrying a (type) tag T and data struct SamplerTag{T,S,E} <: Sampler{E} data::S SamplerTag{T}(s::S) where {T,S} = new{T,S,gentype(T)}(s) end #### helper samplers # TODO: make constraining constructors to enforce that those # types are <: Sampler{T} ##### Adapter to generate a random value in [0, n] struct LessThan{T<:Integer,S} <: Sampler{T} sup::T s::S # the scalar specification/sampler to feed to rand end function rand(rng::AbstractRNG, sp::LessThan) while true x = rand(rng, sp.s) x <= sp.sup && return x end end struct Masked{T<:Integer,S} <: Sampler{T} mask::T s::S end rand(rng::AbstractRNG, sp::Masked) = rand(rng, sp.s) & sp.mask ##### Uniform struct UniformT{T} <: Sampler{T} end uniform(::Type{T}) where {T} = UniformT{T}() rand(rng::AbstractRNG, ::UniformT{T}) where {T} = rand(rng, T) ### machinery for generation with Sampler # This describes how to generate random scalars or arrays, by generating a Sampler # and calling rand on it (which should be defined in "generation.jl"). # NOTE: this section could be moved into a separate file when more containers are supported. #### scalars rand(rng::AbstractRNG, X) = rand(rng, Sampler(rng, X, Val(1))) # this is needed to disambiguate rand(rng::AbstractRNG, X::Dims) = rand(rng, Sampler(rng, X, Val(1))) rand(rng::AbstractRNG=default_rng(), ::Type{X}=Float64) where {X} = rand(rng, Sampler(rng, X, Val(1)))::X rand(X) = rand(default_rng(), X) rand(::Type{X}) where {X} = rand(default_rng(), X) #### arrays rand!(A::AbstractArray{T}, X) where {T} = rand!(default_rng(), A, X) rand!(A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(default_rng(), A, X) rand!(rng::AbstractRNG, A::AbstractArray{T}, X) where {T} = rand!(rng, A, Sampler(rng, X)) rand!(rng::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} = rand!(rng, A, Sampler(rng, X)) function rand!(rng::AbstractRNG, A::AbstractArray{T}, sp::Sampler) where T for i in eachindex(A) @inbounds A[i] = rand(rng, sp) end A end rand(r::AbstractRNG, dims::Integer...) = rand(r, Float64, Dims(dims)) rand( dims::Integer...) = rand(Float64, Dims(dims)) rand(r::AbstractRNG, X, dims::Dims) = rand!(r, Array{gentype(X)}(undef, dims), X) rand( X, dims::Dims) = rand(default_rng(), X, dims) rand(r::AbstractRNG, X, d::Integer, dims::Integer...) = rand(r, X, Dims((d, dims...))) rand( X, d::Integer, dims::Integer...) = rand(X, Dims((d, dims...))) # note: the above methods would trigger an ambiguity warning if d was not separated out: # rand(r, ()) would match both this method and rand(r, dims::Dims) # moreover, a call like rand(r, NotImplementedType()) would be an infinite loop rand(r::AbstractRNG, ::Type{X}, dims::Dims) where {X} = rand!(r, Array{X}(undef, dims), X) rand( ::Type{X}, dims::Dims) where {X} = rand(default_rng(), X, dims) rand(r::AbstractRNG, ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(r, X, Dims((d, dims...))) rand( ::Type{X}, d::Integer, dims::Integer...) where {X} = rand(X, Dims((d, dims...))) # SamplerUnion(X, Y, ...}) == Union{SamplerType{X}, SamplerType{Y}, ...} SamplerUnion(U...) = Union{Any[SamplerType{T} for T in U]...} const SamplerBoolBitInteger = SamplerUnion(Bool, BitInteger_types...) include("Xoshiro.jl") include("RNGs.jl") include("generation.jl") include("normal.jl") include("misc.jl") include("XoshiroSimd.jl") ## rand & rand! & seed! docstrings """ rand([rng=default_rng()], [S], [dims...]) Pick a random element or array of random elements from the set of values specified by `S`; `S` can be * an indexable collection (for example `1:9` or `('x', "y", :z)`), * an `AbstractDict` or `AbstractSet` object, * a string (considered as a collection of characters), or * a type: the set of values to pick from is then equivalent to `typemin(S):typemax(S)` for integers (this is not applicable to [`BigInt`](@ref)), to ``[0, 1)`` for floating point numbers and to ``[0, 1)+i[0, 1)`` for complex floating point numbers; `S` defaults to [`Float64`](@ref). When only one argument is passed besides the optional `rng` and is a `Tuple`, it is interpreted as a collection of values (`S`) and not as `dims`. See also [`randn`](@ref) for normally distributed numbers, and [`rand!`](@ref) and [`randn!`](@ref) for the in-place equivalents. !!! compat "Julia 1.1" Support for `S` as a tuple requires at least Julia 1.1. # Examples ```julia-repl julia> rand(Int, 2) 2-element Array{Int64,1}: 1339893410598768192 1575814717733606317 julia> using Random julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) 1=>2 julia> rand((2, 3)) 3 julia> rand(Float64, (2, 3)) 2ร—3 Array{Float64,2}: 0.999717 0.0143835 0.540787 0.696556 0.783855 0.938235 ``` !!! note The complexity of `rand(rng, s::Union{AbstractDict,AbstractSet})` is linear in the length of `s`, unless an optimized method with constant complexity is available, which is the case for `Dict`, `Set` and dense `BitSet`s. For more than a few calls, use `rand(rng, collect(s))` instead, or either `rand(rng, Dict(s))` or `rand(rng, Set(s))` as appropriate. """ rand """ rand!([rng=default_rng()], A, [S=eltype(A)]) Populate the array `A` with random values. If `S` is specified (`S` can be a type or a collection, cf. [`rand`](@ref) for details), the values are picked randomly from `S`. This is equivalent to `copyto!(A, rand(rng, S, size(A)))` but without allocating a new array. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> rand!(rng, zeros(5)) 5-element Vector{Float64}: 0.5908446386657102 0.7667970365022592 0.5662374165061859 0.4600853424625171 0.7940257103317943 ``` """ rand! """ seed!([rng=default_rng()], seed) -> rng seed!([rng=default_rng()]) -> rng Reseed the random number generator: `rng` will give a reproducible sequence of numbers if and only if a `seed` is provided. Some RNGs don't accept a seed, like `RandomDevice`. After the call to `seed!`, `rng` is equivalent to a newly created object initialized with the same seed. If `rng` is not specified, it defaults to seeding the state of the shared task-local generator. # Examples ```julia-repl julia> Random.seed!(1234); julia> x1 = rand(2) 2-element Vector{Float64}: 0.32597672886359486 0.5490511363155669 julia> Random.seed!(1234); julia> x2 = rand(2) 2-element Vector{Float64}: 0.32597672886359486 0.5490511363155669 julia> x1 == x2 true julia> rng = Xoshiro(1234); rand(rng, 2) == x1 true julia> Xoshiro(1) == Random.seed!(rng, 1) true julia> rand(Random.seed!(rng), Bool) # not reproducible true julia> rand(Random.seed!(rng), Bool) # not reproducible either false julia> rand(Xoshiro(), Bool) # not reproducible either true ``` """ seed!(rng::AbstractRNG, ::Nothing) = seed!(rng) end # module o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/DSFMT.jl‘D������# This file is a part of Julia. License is MIT: https://julialang.org/license module DSFMT import Base: copy, copy!, ==, hash using Base.GMP.MPZ export DSFMT_state, dsfmt_get_min_array_size, dsfmt_get_idstring, dsfmt_init_gen_rand, dsfmt_init_by_array, dsfmt_gv_init_by_array, dsfmt_fill_array_close_open!, dsfmt_fill_array_close1_open2! "Mersenne Exponent" const MEXP = 19937 "DSFMT internal state array size of N 128-bit integers." const N = floor(Int, ((MEXP - 128) / 104 + 1)) """ Julia DSFMT state representation size counted in 32-bit integers. Size: (DSFMT state array of Int128 + 1)*4 + Int32 index + Int32 padding """ const JN32 = (N+1)*4+1+1 struct DSFMT_state val::Vector{Int32} function DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) length(val) == JN32 || throw(DomainError(length(val), "Expected length: $JN32.")) new(val) end end copy!(dst::DSFMT_state, src::DSFMT_state) = (copyto!(dst.val, src.val); dst) copy(src::DSFMT_state) = DSFMT_state(copy(src.val)) ==(s1::DSFMT_state, s2::DSFMT_state) = s1.val == s2.val hash(s::DSFMT_state, h::UInt) = hash(s.val, h) ## wrapper functions function dsfmt_get_idstring() idstring = ccall((:dsfmt_get_idstring,:libdSFMT), Ptr{UInt8}, ()) return unsafe_string(idstring) end function dsfmt_get_min_array_size() min_array_size = ccall((:dsfmt_get_min_array_size,:libdSFMT), Int32, ()) end const dsfmt_min_array_size = dsfmt_get_min_array_size() function dsfmt_init_gen_rand(s::DSFMT_state, seed::UInt32) ccall((:dsfmt_init_gen_rand,:libdSFMT), Cvoid, (Ptr{Cvoid}, UInt32,), s.val, seed) end function dsfmt_init_by_array(s::DSFMT_state, seed::Vector{UInt32}) ccall((:dsfmt_init_by_array,:libdSFMT), Cvoid, (Ptr{Cvoid}, Ptr{UInt32}, Int32), s.val, seed, length(seed)) end function dsfmt_gv_init_by_array(seed::Vector{UInt32}) ccall((:dsfmt_gv_init_by_array,:libdSFMT), Cvoid, (Ptr{UInt32}, Int32), seed, length(seed)) end function dsfmt_fill_array_close1_open2!(s::DSFMT_state, A::Ptr{Float64}, n::Int) @assert Csize_t(A) % 16 == 0 # the underlying C array must be 16-byte aligned @assert dsfmt_min_array_size <= n && iseven(n) ccall((:dsfmt_fill_array_close1_open2,:libdSFMT), Cvoid, (Ptr{Cvoid}, Ptr{Float64}, Int), s.val, A, n) end function dsfmt_fill_array_close_open!(s::DSFMT_state, A::Ptr{Float64}, n::Int) @assert Csize_t(A) % 16 == 0 # the underlying C array must be 16-byte aligned @assert dsfmt_min_array_size <= n && iseven(n) ccall((:dsfmt_fill_array_close_open,:libdSFMT), Cvoid, (Ptr{Cvoid}, Ptr{Float64}, Int), s.val, A, n) end # dSFMT jump poly calculation "Represents a polynomial in GF(2)[X]" struct GF2X z::BigInt end GF2X(s::AbstractString) = GF2X(parse(BigInt, reverse(s), base = 16)) Base.string(f::GF2X) = reverse(string(f.z, base = 16)) Base.:(==)(f::GF2X, g::GF2X) = f.z == g.z Base.copy(f::GF2X) = GF2X(MPZ.set(f.z)) degree(f::GF2X) = Base.ndigits0z(f.z, 2) - 1 coeff(f::GF2X, i) = MPZ.tstbit(f.z, i) setcoeff!(f::GF2X, i) = (MPZ.setbit!(f.z, i); f) mulx!(f::GF2X, c::Int=1) = (MPZ.mul_2exp!(f.z, c), f) xor!(f::GF2X, g::GF2X) = (MPZ.xor!(f.z, g.z); f) # compute f*X mod m function mulxmod!(f::GF2X, m::GF2X, deg=degree(m))::GF2X # precondition: degree(f) < degree(m) mulx!(f) degree(f) == deg && xor!(f, m) f end # cache for X^(2i) mod m const _squares = Dict{GF2X, Vector{GF2X}}() # compute f^2 mod m function sqrmod!(f::GF2X, m::GF2X)::GF2X d = degree(m)-1 0 <= degree(f) <= d || throw(DomainError("f must satisfy 0 <= degree(f) <= degree(m)-1")) sqrs = get!(_squares, m) do x2i = GF2X(1) GF2X[copy(mulxmod!(mulxmod!(x2i, m, d+1), m, d+1)) for i=1:d] end foldl(filter(i->coeff(f, i), 0:degree(f)); init=GF2X(0)) do g, i i <= dรท2 ? # optimization for "simple" squares setcoeff!(g, 2i) : xor!(g, sqrs[i]) end end # compute X^e mod m function powxmod(e::BigInt, m::GF2X)::GF2X e < 0 && throw(DomainError("e must be >= 0")) foldl(Base.ndigits0z(e, 2)-1:-1:0; init=GF2X(1)) do f, i MPZ.tstbit(e, i) ? mulxmod!(sqrmod!(f, m), m) : sqrmod!(f, m) end end "Cached jump polynomials for `MersenneTwister`." const JumpPolys = Dict{BigInt,GF2X}() const CharPoly_ref = Ref{GF2X}() # Ref because it can not be initialized at load time function CharPoly() if !isassigned(CharPoly_ref) CharPoly_ref[] = GF2X(Poly19937) end return CharPoly_ref[] end """ calc_jump(steps::Integer) Compute the dSFMT jump polynomial for `steps` steps (by default for the Mersenne-Twister with exponent 19937). Note that `steps` should be less than the period (e.g. ``steps โ‰ช 2^19937-1``). """ function calc_jump(steps::Integer, charpoly::GF2X=CharPoly())::GF2X steps < 0 && throw(DomainError("jump steps must be >= 0 (got $steps)")) if isempty(JumpPolys) JumpPolys[big(10)^20] = GF2X(JPOLY1e20) end get!(JumpPolys, steps) do powxmod(big(steps), charpoly) end end ## dSFMT jump function dsfmt_jump(s::DSFMT_state, jp::GF2X) degree(jp) < degree(CharPoly()) || throw(DomainError("degree of jump polynomial must be < $(degree(CharPoly())), got $(degree(jp))")) val = s.val nval = length(val) index = val[nval - 1] work = zeros(Int32, JN32) rwork = reinterpret(UInt64, work) dsfmt = Vector{UInt64}(undef, nval >> 1) GC.@preserve dsfmt val begin pdsfmt = Base.unsafe_convert(Ptr{Cvoid}, dsfmt) pval = Base.unsafe_convert(Ptr{Cvoid}, val) Base.Libc.memcpy(pdsfmt, pval, (nval - 1) * sizeof(Int32)) end dsfmt[end] = UInt64(N*2) for i in 0:degree(jp) coeff(jp, i) && dsfmt_jump_add!(rwork, dsfmt) dsfmt_jump_next_state!(dsfmt) end rwork[end] = index return DSFMT_state(work) end function dsfmt_jump_add!(dest::AbstractVector{UInt64}, src::Vector{UInt64}) dp = dest[end] >> 1 sp = src[end] >> 1 diff = ((sp - dp + N) % N) i = 1 while i <= N-diff j = i*2-1 p = j + diff*2 dest[j] โŠป= src[p] dest[j+1] โŠป= src[p+1] i += 1 end while i <= N j = i*2-1 p = j + (diff - N)*2 dest[j] โŠป= src[p] dest[j+1] โŠป= src[p+1] i += 1 end dest[N*2+1] โŠป= src[N*2+1] dest[N*2+2] โŠป= src[N*2+2] return dest end function dsfmt_jump_next_state!(mts::Vector{UInt64}) POS1 = 117 SL1 = 19 SR = 12 MSK1 = 0x000ffafffffffb3f MSK2 = 0x000ffdfffc90fffd idx = (mts[end] >> 1) % N a = idx*2+1 b = ((idx + POS1) % N)*2+1 u = N*2+1 t0 = mts[a] t1 = mts[a+1] L0 = mts[u] L1 = mts[u+1] mts[u] = xor(t0 << SL1, L1 >> 32, L1 << 32, mts[b]) mts[u+1] = xor(t1 << SL1, L0 >> 32, L0 << 32, mts[b+1]) mts[a] = xor(mts[u] >> SR, mts[u] & MSK1, t0) mts[a+1] = xor(mts[u+1] >> SR, mts[u+1] & MSK2, t1) mts[end] = (mts[end] + 2) % (N*2) return mts end "Jump polynomial for 10^20 steps for dSFMT with exponent 19937" const JPOLY1e20 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" "Characteristic polynomial used by dSFMT with exponent 19937" const Poly19937 = "10455110544511444054554514541dbbb0a20820a288a00a80a82208280b73237d2ff2ab5e2fcac914510101df18802aaaa08a022820a02aaa202ae822c364b91fb08598fe4410154041f028a266586e59cf12ce316b3a56af45cb49d02c7059cb89d2d81b0d288badbb25fa4e53b280aa88a22aa82abe7fc0645b7d7a4650c1dec48f21224e3d0e6e04c062c2273ef0d8361415c724dbc8f79118d5fac429f35dc460f6007e54c3966c28808e4c9308cc46e2e0e153bd922223796d4101af867e16e6641e6831e06ebbd2beaf52b2b47076dbf3a3e36c6d14d19dbf5d4b2b44b4d3aa6e1ea102578d765f08e1cb0749220075b8aae81c6e99692a56b35ddd4cf91b1034f1398c98e2d4ac8dbed09bc73ede098514cf3ffdf45cbb59335e3ec42f5f6a5672acc4ca8daa02a2502350ac0485f8b36f27d7a1d4d4b22fc7601e22a4f7c6ba53782b335837a21a068e8fcf3fdebb28860157301519cdea328b1ef4b8d5bc512ce65ff33298c34cc98ea1558f7b6d4db594f4bcab58d9f7bcf5cc59e259216de13f77569bbcee1c8abd55de985b7129e611d156c08cafa022ad7a2206a34a61e5c4c91e75495112003ec03c182a0155d9221382d0b30f45a00d6c19740f9ecebb448de78dfc0342f14166f54afdb97d00ac1a390355ce2aa9de1b3c919d8dd188fc9451ce9c86fa560a2da9dcaa681efd21fe5b6055f8e35a833e5704936245d06e387bf9956251bf0a252628be7a3cb0edab4aaaf141e6d7a9929cc03afa76ca433016d880def9e4cf10f7274e58aa35c372b7b7fb210fe0dc1a6b8445e7774ad6001b9aa1f2a01a790ee849e7ac810e0a646e733a7121bd727a9b593e0c9f5306dff5105af5967e3cee81069100d7e91a9c266e64c9e073a6e527765308879b22ca062668f9a564da6314dcad51405f160e8259582b7c06c313c84b0acba44958c052e6be540a7688e240232bee40b990dc48ee07d560786ca34e7df1bbdd2bca38a30c548ec57e91906b8417ff0da68db9c154d8ad83b06a6fc2b2e14ca5fbe7bd50382949c9b1f6e8540d9d43b35fa76a6ded27c2f17095a4f330626c5e86e8ff88ae53e05a434356a04a1ddae43b6e2ab883719360fbece72b6090ab17414ca7006e49d183813422c808fde53a30f872254bb554653aef86855f95a9361782100de2402efd749cc8cf6a837066c1c40c0160e415d8119e53a09877f1160ef8b99b2839e9b8c09b1e461e906041344c8fc2ef0a8eda04e319da41e001e60bc64dcdfc0593dff0f4b390580e1cd5b3c16204e77df10217791f99de49fcdc9160b793fd980bff7ae0cbd570425eb439352e5032e03797461376b5fb92aa156ad64935cc201a162f10f04b6d2d20a0415ae16f299e98baa86466f6f517f05f430254884a4010ac196540b0644e3c274323d4f0206780d38175f1e41fcb65bd387be073abee61b99d43f6b9106953ae4f6906e6ac0741e26af05fa9150c4f380558668aa667e404e3784b839d631e35af015024dbfc3dce4685574cd1e58eb72c70011090a2a876b65e34cb6cf297d24cb61ccca5a9706f34ae1f66345998f850de4e48d77cdf6e00fb0d2210ec9fb53fed02a781f7dfcdb609b9f955504f450e4b7b623cb0f5ba1ea09d92cd8d14f7e827b4580855f3a7dc2e5a45817df9e26adb5934f6026f745cb0f65e71c590bd954d1abc3826379719b7c6f4a0cbe6eb22a903b98bef785bb96efe2fc96988722c91f3e59d28d8244c8bfb59ee26082d82cad937ee70f178b44082533308ca24f236d8b91ca7af5b3d865c90d61410e1ffe39be6433a12ef2932e101b4bb34befa76748e0364a96f06e7932f44297fd5f85765b662c3ade19d9a9d9479f6de20b6b753d3dcbadf63e344578b98af85b4c4c63be9d154864f5d341f210f3503a60efec38ee59069499d0049aedfaef9264a7ce9de460a01e5437254fc68dfaebaa5e0e791380806bc149aedbc1d771457770937a5d606fc5ae728993783e6c45e1f5e1b9492a32f9df46a01020792a3803af04837a3905e7cc6ae68c512cb58f4facb457476729bac1ac89a7a32dc6857edbd6624ebeffed9d4e84a2f4ded9759962635aac94ca72d039c14af6e932fc84c25de28688acab0f41ae931a0f35b9c4195483d902f95e0d3e745e08f334cf5062da9fbd18fbb9efeb275a50a8a479939aa3e376821a030f0d241a4c4f6e3298a43a7f2166db483946c5ca623087b6252e27b09805d92a834ad914b9555f9c36150bb17d7e85f6b1a10b41a5c36a1fd0fdc354ec91720b245b0abcde6b38fdd33f471aa6ba1dbb4f178c3c6d8140a1f0700e072b0f9aafe1208abc94eea750b84e74ba6781b48c398ddfd2842e5447a11767e4f467864e4447382379010669f07870339ca42ac290063b40eaba1daa63cf6cd70c5a600c018f6933dc8bec000a521344cad7320ba121dd21f4723d38a8d3ab8c1f0f6c5a6995b796af79407ae1a7ce6d78922677d9990dd2a588f8a3323d80e9a69417f5224565afa60b64604e7316229514dcb7282b4e638981a5751d114da1ac9bf31f0e2fff5213f2020f9f2f31a8fd0c614e80c1a056d4b1af3ded2f696578f56427962ca54f4a28a502a0ac59013416abd81515fb1956ccb494c05ef61cab48474b6b1609cc5a3871a5111f8bf0a76b378f0e646687829e30f5762156da66c1b1c7abc0eb84e6ff2b9f5b22d34540ab97d643e8dd2e35f6e9e4fc2c30d8af88b2caab7bd5d4a6cf967e8ef79967c1ae85bf7d410a79f4630f13fdc6507d339909b81a29d84741103371310e5b4e279758431df627553b6826fc4c98e5fb6551315b0bd811b7b0f357198210dd99ccd8fba2904114c3e0b344eba43832b3c507e8b6b586e4ab3dc7a2ec71e150c54a13eca2340328d0b3e419ab2ba862ee93fc239fb46d907628055e105318e7fa52f9a83deb0e3cb02c62b8817702ead02a315f76aa1d08860cc5214a867808e33f8e075241956f148f876f3bc66566773610c9c5935b559c0ac47d84b6bfb112f59842be58df51055cf9180264f53f7795d4c934718bc65f359e34a8d230408854685b59c3a9f4d73a229bb465d4da3165404c6786c767299f57dfa85a83492fb4f61386441c928224cd88a7f4b36f245b7aa2b5c97b545ac4db8afe9a1a87e27b57d94c2bbffdb6e88f812aa27e0908048812086c2a72289d7bf136b3a1042a44d1913d39caec24ffda22814706f080b6cbe00e9cd442ccdcb600a436c0daeacbc5482021ba8a06c1fedbb333793557d5175b9313799ff91dfa620380a9e2a10132f0818bba72072e359726e2bd1f2ec98e0face32e0f88ee2c6f7cef7c2fbceffe8d3ccdff97b7ff71d861ba8b98237ccfb00176ee02206ccc08026cee082a88a8a349a1c9016983ea10789272105032f89b3113fd9b75b35c884622ec884622ee8aee2aaa2aeae86868c868ea68ea6862e0624062ea22aa66ee66ee44ee64ce64ce64ce646464c4808081808080a0a0828282828282822222a8898888888888aaaa2aaaaaaaaaaaaa2000000000000000008" end # module q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/Xoshiro.jlƒ������# This file is a part of Julia. License is MIT: https://julialang.org/license ## Xoshiro RNG # Lots of implementation is shared with TaskLocalRNG """ Xoshiro(seed) Xoshiro() Xoshiro256++ is a fast pseudorandom number generator described by David Blackman and Sebastiano Vigna in "Scrambled Linear Pseudorandom Number Generators", ACM Trans. Math. Softw., 2021. Reference implementation is available at http://prng.di.unimi.it Apart from the high speed, Xoshiro has a small memory footprint, making it suitable for applications where many different random states need to be held for long time. Julia's Xoshiro implementation has a bulk-generation mode; this seeds new virtual PRNGs from the parent, and uses SIMD to generate in parallel (i.e. the bulk stream consists of multiple interleaved xoshiro instances). The virtual PRNGs are discarded once the bulk request has been serviced (and should cause no heap allocations). # Examples ```jldoctest julia> using Random julia> rng = Xoshiro(1234); julia> x1 = rand(rng, 2) 2-element Vector{Float64}: 0.32597672886359486 0.5490511363155669 julia> rng = Xoshiro(1234); julia> x2 = rand(rng, 2) 2-element Vector{Float64}: 0.32597672886359486 0.5490511363155669 julia> x1 == x2 true ``` """ mutable struct Xoshiro <: AbstractRNG s0::UInt64 s1::UInt64 s2::UInt64 s3::UInt64 s4::UInt64 # internal splitmix state Xoshiro(s0::Integer, s1::Integer, s2::Integer, s3::Integer, s4::Integer) = new(s0, s1, s2, s3, s4) Xoshiro(s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64) = new(s0, s1, s2, s3, 1s0 + 3s1 + 5s2 + 7s3) Xoshiro(seed=nothing) = seed!(new(), seed) end Xoshiro(s0::Integer, s1::Integer, s2::Integer, s3::Integer) = Xoshiro(UInt64(s0), UInt64(s1), UInt64(s2), UInt64(s3)) function setstate!( x::Xoshiro, s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64, # xoshiro256 state s4::UInt64, # internal splitmix state ) x.s0 = s0 x.s1 = s1 x.s2 = s2 x.s3 = s3 x.s4 = s4 x end copy(rng::Xoshiro) = Xoshiro(rng.s0, rng.s1, rng.s2, rng.s3, rng.s4) function copy!(dst::Xoshiro, src::Xoshiro) dst.s0, dst.s1, dst.s2, dst.s3, dst.s4 = src.s0, src.s1, src.s2, src.s3, src.s4 dst end function ==(a::Xoshiro, b::Xoshiro) a.s0 == b.s0 && a.s1 == b.s1 && a.s2 == b.s2 && a.s3 == b.s3 && a.s4 == b.s4 end hash(x::Xoshiro, h::UInt) = hash((x.s0, x.s1, x.s2, x.s3, x.s4), h + 0x49a62c2dda6fa9be % UInt) rng_native_52(::Xoshiro) = UInt64 @inline function rand(rng::Xoshiro, ::SamplerType{UInt64}) s0, s1, s2, s3 = rng.s0, rng.s1, rng.s2, rng.s3 tmp = s0 + s3 res = ((tmp << 23) | (tmp >> 41)) + s0 t = s1 << 17 s2 = xor(s2, s0) s3 = xor(s3, s1) s1 = xor(s1, s2) s0 = xor(s0, s3) s2 = xor(s2, t) s3 = s3 << 45 | s3 >> 19 rng.s0, rng.s1, rng.s2, rng.s3 = s0, s1, s2, s3 res end ## Task local RNG """ TaskLocalRNG The `TaskLocalRNG` has state that is local to its task, not its thread. It is seeded upon task creation, from the state of its parent task. Therefore, task creation is an event that changes the parent's RNG state. As an upside, the `TaskLocalRNG` is pretty fast, and permits reproducible multithreaded simulations (barring race conditions), independent of scheduler decisions. As long as the number of threads is not used to make decisions on task creation, simulation results are also independent of the number of available threads / CPUs. The random stream should not depend on hardware specifics, up to endianness and possibly word size. Using or seeding the RNG of any other task than the one returned by `current_task()` is undefined behavior: it will work most of the time, and may sometimes fail silently. """ struct TaskLocalRNG <: AbstractRNG end TaskLocalRNG(::Nothing) = TaskLocalRNG() rng_native_52(::TaskLocalRNG) = UInt64 function setstate!( x::TaskLocalRNG, s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64, # xoshiro256 state s4::UInt64, # internal splitmix state ) t = current_task() t.rngState0 = s0 t.rngState1 = s1 t.rngState2 = s2 t.rngState3 = s3 t.rngState4 = s4 x end @inline function rand(::TaskLocalRNG, ::SamplerType{UInt64}) task = current_task() s0, s1, s2, s3 = task.rngState0, task.rngState1, task.rngState2, task.rngState3 tmp = s0 + s3 res = ((tmp << 23) | (tmp >> 41)) + s0 t = s1 << 17 s2 โŠป= s0 s3 โŠป= s1 s1 โŠป= s2 s0 โŠป= s3 s2 โŠป= t s3 = s3 << 45 | s3 >> 19 task.rngState0, task.rngState1, task.rngState2, task.rngState3 = s0, s1, s2, s3 res end # Shared implementation between Xoshiro and TaskLocalRNG -- seeding function seed!(rng::Union{TaskLocalRNG,Xoshiro}) # as we get good randomness from RandomDevice, we can skip hashing rd = RandomDevice() s0 = rand(rd, UInt64) s1 = rand(rd, UInt64) s2 = rand(rd, UInt64) s3 = rand(rd, UInt64) s4 = 1s0 + 3s1 + 5s2 + 7s3 setstate!(rng, s0, s1, s2, s3, s4) end function seed!(rng::Union{TaskLocalRNG,Xoshiro}, seed::Union{Vector{UInt32}, Vector{UInt64}}) c = SHA.SHA2_256_CTX() SHA.update!(c, reinterpret(UInt8, seed)) s0, s1, s2, s3 = reinterpret(UInt64, SHA.digest!(c)) s4 = 1s0 + 3s1 + 5s2 + 7s3 setstate!(rng, s0, s1, s2, s3, s4) end seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed::Integer) = seed!(rng, make_seed(seed)) @inline function rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{UInt128}) first = rand(rng, UInt64) second = rand(rng,UInt64) second + UInt128(first) << 64 end @inline rand(rng::Union{TaskLocalRNG, Xoshiro}, ::SamplerType{Int128}) = rand(rng, UInt128) % Int128 @inline function rand(rng::Union{TaskLocalRNG, Xoshiro}, T::SamplerUnion(Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64)) S = T[] # use upper bits (rand(rng, UInt64) >>> (64 - 8*sizeof(S))) % S end function copy(rng::TaskLocalRNG) t = current_task() Xoshiro(t.rngState0, t.rngState1, t.rngState2, t.rngState3, t.rngState4) end function copy!(dst::TaskLocalRNG, src::Xoshiro) t = current_task() setstate!(dst, src.s0, src.s1, src.s2, src.s3, src.s4) return dst end function copy!(dst::Xoshiro, src::TaskLocalRNG) t = current_task() setstate!(dst, t.rngState0, t.rngState1, t.rngState2, t.rngState3, t.rngState4) return dst end function ==(a::Xoshiro, b::TaskLocalRNG) t = current_task() ( a.s0 == t.rngState0 && a.s1 == t.rngState1 && a.s2 == t.rngState2 && a.s3 == t.rngState3 && a.s4 == t.rngState4 ) end ==(a::TaskLocalRNG, b::Xoshiro) = b == a function hash(x::TaskLocalRNG, h::UInt) t = current_task() hash((t.rngState0, t.rngState1, t.rngState2, t.rngState3, t.rngState4), h + 0x49a62c2dda6fa9be % UInt) end # for partial words, use upper bits from Xoshiro rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt52Raw{UInt64}}) = rand(r, UInt64) >>> 12 rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt52{UInt64}}) = rand(r, UInt64) >>> 12 rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float16}}) = Float16(Float32(rand(r, UInt16) >>> 5) * Float32(0x1.0p-11)) rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01{Float32}}) = Float32(rand(r, UInt32) >>> 8) * Float32(0x1.0p-24) rand(r::Union{TaskLocalRNG, Xoshiro}, ::SamplerTrivial{CloseOpen01_64}) = Float64(rand(r, UInt64) >>> 11) * 0x1.0p-53 n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/RNGs.jlIe������# This file is a part of Julia. License is MIT: https://julialang.org/license ## RandomDevice """ RandomDevice() Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. The entropy is obtained from the operating system. """ struct RandomDevice <: AbstractRNG; end RandomDevice(seed::Nothing) = RandomDevice() seed!(rng::RandomDevice) = rng rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = Libc.getrandom!(Ref{sp[]}())[] rand(rd::RandomDevice, ::SamplerType{Bool}) = rand(rd, UInt8) % Bool function rand!(rd::RandomDevice, A::Array{Bool}, ::SamplerType{Bool}) Libc.getrandom!(A) # we need to mask the result so that only the LSB in each byte can be non-zero GC.@preserve A begin p = Ptr{UInt8}(pointer(A)) for i = 1:length(A) unsafe_store!(p, unsafe_load(p) & 0x1) p += 1 end end return A end for T in BitInteger_types @eval rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T}) = Libc.getrandom!(A) end # RandomDevice produces natively UInt64 rng_native_52(::RandomDevice) = UInt64 ## MersenneTwister const MT_CACHE_F = 501 << 1 # number of Float64 in the cache const MT_CACHE_I = 501 << 4 # number of bytes in the UInt128 cache @assert dsfmt_get_min_array_size() <= MT_CACHE_F mutable struct MersenneTwister <: AbstractRNG seed::Vector{UInt32} state::DSFMT_state vals::Vector{Float64} ints::Vector{UInt128} idxF::Int idxI::Int # counters for show adv::Int64 # state of advance at the DSFMT_state level adv_jump::BigInt # number of skipped Float64 values via randjump adv_vals::Int64 # state of advance when vals is filled-up adv_ints::Int64 # state of advance when ints is filled-up function MersenneTwister(seed, state, vals, ints, idxF, idxI, adv, adv_jump, adv_vals, adv_ints) length(vals) == MT_CACHE_F && 0 <= idxF <= MT_CACHE_F || throw(DomainError((length(vals), idxF), "`length(vals)` and `idxF` must be consistent with $MT_CACHE_F")) length(ints) == MT_CACHE_I >> 4 && 0 <= idxI <= MT_CACHE_I || throw(DomainError((length(ints), idxI), "`length(ints)` and `idxI` must be consistent with $MT_CACHE_I")) new(seed, state, vals, ints, idxF, idxI, adv, adv_jump, adv_vals, adv_ints) end end MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = MersenneTwister(seed, state, Vector{Float64}(undef, MT_CACHE_F), Vector{UInt128}(undef, MT_CACHE_I >> 4), MT_CACHE_F, 0, 0, 0, -1, -1) """ MersenneTwister(seed) MersenneTwister() Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers. The `seed` may be a non-negative integer or a vector of `UInt32` integers. If no seed is provided, a randomly generated one is created (using entropy from the system). See the [`seed!`](@ref) function for reseeding an already existing `MersenneTwister` object. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> x1 = rand(rng, 2) 2-element Vector{Float64}: 0.5908446386657102 0.7667970365022592 julia> rng = MersenneTwister(1234); julia> x2 = rand(rng, 2) 2-element Vector{Float64}: 0.5908446386657102 0.7667970365022592 julia> x1 == x2 true ``` """ MersenneTwister(seed=nothing) = seed!(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) function copy!(dst::MersenneTwister, src::MersenneTwister) copyto!(resize!(dst.seed, length(src.seed)), src.seed) copy!(dst.state, src.state) copyto!(dst.vals, src.vals) copyto!(dst.ints, src.ints) dst.idxF = src.idxF dst.idxI = src.idxI dst.adv = src.adv dst.adv_jump = src.adv_jump dst.adv_vals = src.adv_vals dst.adv_ints = src.adv_ints dst end copy(src::MersenneTwister) = MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), copy(src.ints), src.idxF, src.idxI, src.adv, src.adv_jump, src.adv_vals, src.adv_ints) ==(r1::MersenneTwister, r2::MersenneTwister) = r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && isequal(r1.ints, r2.ints) && r1.idxF == r2.idxF && r1.idxI == r2.idxI hash(r::MersenneTwister, h::UInt) = foldr(hash, (r.seed, r.state, r.vals, r.ints, r.idxF, r.idxI); init=h) function show(io::IO, rng::MersenneTwister) # seed seed = from_seed(rng.seed) seed_str = seed <= typemax(Int) ? string(seed) : "0x" * string(seed, base=16) # DWIM if rng.adv_jump == 0 && rng.adv == 0 return print(io, MersenneTwister, "(", seed_str, ")") end print(io, MersenneTwister, "(", seed_str, ", (") # state adv = Integer[rng.adv_jump, rng.adv] if rng.adv_vals != -1 || rng.adv_ints != -1 if rng.adv_vals == -1 @assert rng.idxF == MT_CACHE_F push!(adv, 0, 0) # "(0, 0)" is nicer on the eyes than (-1, 1002) else push!(adv, rng.adv_vals, rng.idxF) end end if rng.adv_ints != -1 idxI = (length(rng.ints)*16 - rng.idxI) / 8 # 8 represents one Int64 idxI = Int(idxI) # idxI should always be an integer when using public APIs push!(adv, rng.adv_ints, idxI) end join(io, adv, ", ") print(io, "))") end ### low level API function reset_caches!(r::MersenneTwister) # zeroing the caches makes comparing two MersenneTwister RNGs easier fill!(r.vals, 0.0) fill!(r.ints, zero(UInt128)) mt_setempty!(r) mt_setempty!(r, UInt128) r.adv_vals = -1 r.adv_ints = -1 r end #### floats mt_avail(r::MersenneTwister) = MT_CACHE_F - r.idxF mt_empty(r::MersenneTwister) = r.idxF == MT_CACHE_F mt_setfull!(r::MersenneTwister) = r.idxF = 0 mt_setempty!(r::MersenneTwister) = r.idxF = MT_CACHE_F mt_pop!(r::MersenneTwister) = @inbounds return r.vals[r.idxF+=1] @noinline function gen_rand(r::MersenneTwister) r.adv_vals = r.adv GC.@preserve r fill_array!(r, pointer(r.vals), length(r.vals), CloseOpen12()) mt_setfull!(r) end reserve_1(r::MersenneTwister) = (mt_empty(r) && gen_rand(r); nothing) # `reserve` allows one to call `rand_inbounds` n times # precondition: n <= MT_CACHE_F reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) #### ints logsizeof(::Type{<:Union{Bool,Int8,UInt8}}) = 0 logsizeof(::Type{<:Union{Int16,UInt16}}) = 1 logsizeof(::Type{<:Union{Int32,UInt32}}) = 2 logsizeof(::Type{<:Union{Int64,UInt64}}) = 3 logsizeof(::Type{<:Union{Int128,UInt128}}) = 4 idxmask(::Type{<:Union{Bool,Int8,UInt8}}) = 15 idxmask(::Type{<:Union{Int16,UInt16}}) = 7 idxmask(::Type{<:Union{Int32,UInt32}}) = 3 idxmask(::Type{<:Union{Int64,UInt64}}) = 1 idxmask(::Type{<:Union{Int128,UInt128}}) = 0 mt_avail(r::MersenneTwister, ::Type{T}) where {T<:BitInteger} = r.idxI >> logsizeof(T) function mt_setfull!(r::MersenneTwister, ::Type{<:BitInteger}) r.adv_ints = r.adv ints = r.ints @assert length(ints) == 501 # dSFMT natively randomizes 52 out of 64 bits of each UInt64 words, # i.e. 12 bits are missing; # by generating 5 words == 5*52 == 260 bits, we can fully # randomize 4 UInt64 = 256 bits; IOW, at the array level, we must # randomize ceil(501*1.25) = 627 UInt128 words (with 2*52 bits each), # which we then condense into fully randomized 501 UInt128 words len = 501 + 126 # 126 == ceil(501 / 4) resize!(ints, len) p = pointer(ints) # must be *after* resize! GC.@preserve r fill_array!(r, Ptr{Float64}(p), len*2, CloseOpen12_64()) k = 501 n = 0 @inbounds while n != 500 u = ints[k+=1] ints[n+=1] โŠป= u << 48 ints[n+=1] โŠป= u << 36 ints[n+=1] โŠป= u << 24 ints[n+=1] โŠป= u << 12 end @assert k == len - 1 @inbounds ints[501] โŠป= ints[len] << 48 resize!(ints, 501) r.idxI = MT_CACHE_I end mt_setempty!(r::MersenneTwister, ::Type{<:BitInteger}) = r.idxI = 0 function reserve1(r::MersenneTwister, ::Type{T}) where T<:BitInteger r.idxI < sizeof(T) && mt_setfull!(r, T) nothing end function mt_pop!(r::MersenneTwister, ::Type{T}) where T<:BitInteger reserve1(r, T) r.idxI -= sizeof(T) i = r.idxI @inbounds x128 = r.ints[1 + i >> 4] i128 = (i >> logsizeof(T)) & idxmask(T) # 0-based "indice" in x128 (x128 >> (i128 * (sizeof(T) << 3))) % T end function mt_pop!(r::MersenneTwister, ::Type{T}) where {T<:Union{Int128,UInt128}} reserve1(r, T) idx = r.idxI >> 4 r.idxI = idx << 4 - 16 @inbounds r.ints[idx] % T end ### seeding #### make_seed() # make_seed produces values of type Vector{UInt32}, suitable for MersenneTwister seeding function make_seed() try return rand(RandomDevice(), UInt32, 4) catch ex ex isa IOError || rethrow() @warn "Entropy pool not available to seed RNG; using ad-hoc entropy sources." return make_seed(Libc.rand()) end end function make_seed(n::Integer) n < 0 && throw(DomainError(n, "`n` must be non-negative.")) seed = UInt32[] while true push!(seed, n & 0xffffffff) n >>= 32 if n == 0 return seed end end end # inverse of make_seed(::Integer) from_seed(a::Vector{UInt32})::BigInt = sum(a[i] * big(2)^(32*(i-1)) for i in 1:length(a)) #### seed!() function seed!(r::MersenneTwister, seed::Vector{UInt32}) copyto!(resize!(r.seed, length(seed)), seed) dsfmt_init_by_array(r.state, r.seed) reset_caches!(r) r.adv = 0 r.adv_jump = 0 return r end seed!(r::MersenneTwister) = seed!(r, make_seed()) seed!(r::MersenneTwister, n::Integer) = seed!(r, make_seed(n)) ### Global RNG struct _GLOBAL_RNG <: AbstractRNG global const GLOBAL_RNG = _GLOBAL_RNG.instance end # GLOBAL_RNG currently uses TaskLocalRNG typeof_rng(::_GLOBAL_RNG) = TaskLocalRNG """ default_rng() -> rng Return the default global random number generator (RNG). !!! note What the default RNG is is an implementation detail. Across different versions of Julia, you should not expect the default RNG to be always the same, nor that it will return the same stream of random numbers for a given seed. !!! compat "Julia 1.3" This function was introduced in Julia 1.3. """ @inline default_rng() = TaskLocalRNG() @inline default_rng(tid::Int) = TaskLocalRNG() copy!(dst::Xoshiro, ::_GLOBAL_RNG) = copy!(dst, default_rng()) copy!(::_GLOBAL_RNG, src::Xoshiro) = copy!(default_rng(), src) copy(::_GLOBAL_RNG) = copy(default_rng()) GLOBAL_SEED = 0 set_global_seed!(seed) = global GLOBAL_SEED = seed function seed!(::_GLOBAL_RNG, seed=rand(RandomDevice(), UInt64, 4)) global GLOBAL_SEED = seed seed!(default_rng(), seed) end seed!(rng::_GLOBAL_RNG, ::Nothing) = seed!(rng) # to resolve ambiguity seed!(seed::Union{Nothing,Integer,Vector{UInt32},Vector{UInt64}}=nothing) = seed!(GLOBAL_RNG, seed) rng_native_52(::_GLOBAL_RNG) = rng_native_52(default_rng()) rand(::_GLOBAL_RNG, sp::SamplerBoolBitInteger) = rand(default_rng(), sp) for T in (:(SamplerTrivial{UInt52Raw{UInt64}}), :(SamplerTrivial{UInt2x52Raw{UInt128}}), :(SamplerTrivial{UInt104Raw{UInt128}}), :(SamplerTrivial{CloseOpen01_64}), :(SamplerTrivial{CloseOpen12_64}), :(SamplerUnion(Int64, UInt64, Int128, UInt128)), :(SamplerUnion(Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32)), ) @eval rand(::_GLOBAL_RNG, x::$T) = rand(default_rng(), x) end rand!(::_GLOBAL_RNG, A::AbstractArray{Float64}, I::SamplerTrivial{<:FloatInterval_64}) = rand!(default_rng(), A, I) rand!(::_GLOBAL_RNG, A::Array{Float64}, I::SamplerTrivial{<:FloatInterval_64}) = rand!(default_rng(), A, I) for T in (Float16, Float32) @eval rand!(::_GLOBAL_RNG, A::Array{$T}, I::SamplerTrivial{CloseOpen12{$T}}) = rand!(default_rng(), A, I) @eval rand!(::_GLOBAL_RNG, A::Array{$T}, I::SamplerTrivial{CloseOpen01{$T}}) = rand!(default_rng(), A, I) end for T in BitInteger_types @eval rand!(::_GLOBAL_RNG, A::Array{$T}, I::SamplerType{$T}) = rand!(default_rng(), A, I) end function __init__() seed!(GLOBAL_RNG) ccall(:jl_gc_init_finalizer_rng_state, Cvoid, ()) end ### generation # MersenneTwister produces natively Float64 rng_native_52(::MersenneTwister) = Float64 #### helper functions # precondition: !mt_empty(r) rand_inbounds(r::MersenneTwister, ::CloseOpen12_64) = mt_pop!(r) rand_inbounds(r::MersenneTwister, ::CloseOpen01_64=CloseOpen01()) = rand_inbounds(r, CloseOpen12()) - 1.0 rand_inbounds(r::MersenneTwister, ::UInt52Raw{T}) where {T<:BitInteger} = reinterpret(UInt64, rand_inbounds(r, CloseOpen12())) % T function rand(r::MersenneTwister, x::SamplerTrivial{UInt52Raw{UInt64}}) reserve_1(r) rand_inbounds(r, x[]) end function rand(r::MersenneTwister, ::SamplerTrivial{UInt2x52Raw{UInt128}}) reserve(r, 2) rand_inbounds(r, UInt52Raw(UInt128)) << 64 | rand_inbounds(r, UInt52Raw(UInt128)) end function rand(r::MersenneTwister, ::SamplerTrivial{UInt104Raw{UInt128}}) reserve(r, 2) rand_inbounds(r, UInt52Raw(UInt128)) << 52 โŠป rand_inbounds(r, UInt52Raw(UInt128)) end #### floats rand(r::MersenneTwister, sp::SamplerTrivial{CloseOpen12_64}) = (reserve_1(r); rand_inbounds(r, sp[])) #### integers rand(r::MersenneTwister, T::SamplerUnion(Int64, UInt64, Int128, UInt128)) = mt_pop!(r, T[]) rand(r::MersenneTwister, T::SamplerUnion(Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32)) = rand(r, UInt52Raw()) % T[] #### arrays of floats ##### AbstractArray function rand!(r::MersenneTwister, A::AbstractArray{Float64}, I::SamplerTrivial{<:FloatInterval_64}) region = LinearIndices(A) # what follows is equivalent to this simple loop but more efficient: # for i=region # @inbounds A[i] = rand(r, I[]) # end m = Base.checked_sub(first(region), 1) n = last(region) while m < n s = mt_avail(r) if s == 0 gen_rand(r) s = mt_avail(r) end m2 = min(n, m+s) for i=m+1:m2 @inbounds A[i] = rand_inbounds(r, I[]) end m = m2 end A end ##### Array : internal functions # internal array-like type to circumvent the lack of flexibility with reinterpret struct UnsafeView{T} <: DenseArray{T,1} ptr::Ptr{T} len::Int end Base.length(a::UnsafeView) = a.len Base.getindex(a::UnsafeView, i::Int) = unsafe_load(a.ptr, i) Base.setindex!(a::UnsafeView, x, i::Int) = unsafe_store!(a.ptr, x, i) Base.pointer(a::UnsafeView) = a.ptr Base.size(a::UnsafeView) = (a.len,) Base.elsize(::Type{UnsafeView{T}}) where {T} = sizeof(T) # this is essentially equivalent to rand!(r, ::AbstractArray{Float64}, I) above, but due to # optimizations which can't be done currently when working with pointers, we have to re-order # manually the computation flow to get the performance # (see https://discourse.julialang.org/t/unsafe-store-sometimes-slower-than-arrays-setindex) function _rand_max383!(r::MersenneTwister, A::UnsafeView{Float64}, I::FloatInterval_64) n = length(A) @assert n <= dsfmt_get_min_array_size()+1 # == 383 mt_avail(r) == 0 && gen_rand(r) # from now on, at most one call to gen_rand(r) will be necessary m = min(n, mt_avail(r)) GC.@preserve r unsafe_copyto!(A.ptr, pointer(r.vals, r.idxF+1), m) if m == n r.idxF += m else # m < n gen_rand(r) GC.@preserve r unsafe_copyto!(A.ptr+m*sizeof(Float64), pointer(r.vals), n-m) r.idxF = n-m end if I isa CloseOpen01 for i=1:n A[i] -= 1.0 end end A end function fill_array!(rng::MersenneTwister, A::Ptr{Float64}, n::Int, I) rng.adv += n fill_array!(rng.state, A, n, I) end fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen01_64) = dsfmt_fill_array_close_open!(s, A, n) fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen12_64) = dsfmt_fill_array_close1_open2!(s, A, n) function rand!(r::MersenneTwister, A::UnsafeView{Float64}, I::SamplerTrivial{<:FloatInterval_64}) # depending on the alignment of A, the data written by fill_array! may have # to be left-shifted by up to 15 bytes (cf. unsafe_copyto! below) for # reproducibility purposes; # so, even for well aligned arrays, fill_array! is used to generate only # the n-2 first values (or n-3 if n is odd), and the remaining values are # generated by the scalar version of rand n = length(A) n2 = (n-2) รท 2 * 2 n2 < dsfmt_get_min_array_size() && return _rand_max383!(r, A, I[]) pA = A.ptr align = Csize_t(pA) % 16 if align > 0 pA2 = pA + 16 - align fill_array!(r, pA2, n2, I[]) # generate the data in-place, but shifted unsafe_copyto!(pA, pA2, n2) # move the data to the beginning of the array else fill_array!(r, pA, n2, I[]) end for i=n2+1:n A[i] = rand(r, I[]) end A end # fills up A reinterpreted as an array of Float64 with n64 values function _rand!(r::MersenneTwister, A::Array{T}, n64::Int, I::FloatInterval_64) where T # n64 is the length in terms of `Float64` of the target @assert sizeof(Float64)*n64 <= sizeof(T)*length(A) && isbitstype(T) GC.@preserve A rand!(r, UnsafeView{Float64}(pointer(A), n64), SamplerTrivial(I)) A end ##### Array: Float64, Float16, Float32 rand!(r::MersenneTwister, A::Array{Float64}, I::SamplerTrivial{<:FloatInterval_64}) = _rand!(r, A, length(A), I[]) mask128(u::UInt128, ::Type{Float16}) = (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 for T in (Float16, Float32) @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{CloseOpen12{$T}}) n = length(A) n128 = n * sizeof($T) รท 16 _rand!(r, A, 2*n128, CloseOpen12()) GC.@preserve A begin A128 = UnsafeView{UInt128}(pointer(A), n128) for i in 1:n128 u = A128[i] u โŠป= u << 26 # at this point, the 64 low bits of u, "k" being the k-th bit of A128[i] and "+" # the bit xor, are: # [..., 58+32,..., 53+27, 52+26, ..., 33+7, 32+6, ..., 27+1, 26, ..., 1] # the bits needing to be random are # [1:10, 17:26, 33:42, 49:58] (for Float16) # [1:23, 33:55] (for Float32) # this is obviously satisfied on the 32 low bits side, and on the high side, # the entropy comes from bits 33:52 of A128[i] and then from bits 27:32 # (which are discarded on the low side) # this is similar for the 64 high bits of u A128[i] = mask128(u, $T) end end for i in 16*n128รทsizeof($T)+1:n @inbounds A[i] = rand(r, $T) + one($T) end A end @eval function rand!(r::MersenneTwister, A::Array{$T}, ::SamplerTrivial{CloseOpen01{$T}}) rand!(r, A, CloseOpen12($T)) I32 = one(Float32) for i in eachindex(A) @inbounds A[i] = Float32(A[i])-I32 # faster than "A[i] -= one(T)" for T==Float16 end A end end #### arrays of integers function rand!(r::MersenneTwister, A::UnsafeView{UInt128}, ::SamplerType{UInt128}) n::Int=length(A) i = n while true rand!(r, UnsafeView{Float64}(A.ptr, 2i), CloseOpen12()) n < 5 && break i = 0 while n-i >= 5 u = A[i+=1] A[n] โŠป= u << 48 A[n-=1] โŠป= u << 36 A[n-=1] โŠป= u << 24 A[n-=1] โŠป= u << 12 n-=1 end end if n > 0 u = rand(r, UInt2x52Raw()) for i = 1:n A[i] โŠป= u << (12*i) end end A end for T in BitInteger_types @eval function rand!(r::MersenneTwister, A::Array{$T}, sp::SamplerType{$T}) GC.@preserve A rand!(r, UnsafeView(pointer(A), length(A)), sp) A end T == UInt128 && continue @eval function rand!(r::MersenneTwister, A::UnsafeView{$T}, ::SamplerType{$T}) n = length(A) n128 = n * sizeof($T) รท 16 rand!(r, UnsafeView{UInt128}(pointer(A), n128)) for i = 16*n128รทsizeof($T)+1:n @inbounds A[i] = rand(r, $T) end A end end #### arrays of Bool # similar to Array{UInt8}, but we need to mask the result so that only the LSB # in each byte can be non-zero function rand!(r::MersenneTwister, A1::Array{Bool}, sp::SamplerType{Bool}) n1 = length(A1) n128 = n1 รท 16 if n128 == 0 bits = rand(r, UInt52Raw()) else GC.@preserve A1 begin A = UnsafeView{UInt128}(pointer(A1), n128) rand!(r, UnsafeView{Float64}(A.ptr, 2*n128), CloseOpen12()) # without masking, non-zero bits could be observed in other # positions than the LSB of each byte mask = 0x01010101010101010101010101010101 # we need up to 15 bits of entropy in `bits` for the final loop, # which we will extract from x = A[1] % UInt64; # let y = x % UInt32; y contains 32 bits of entropy, but 4 # of them will be used for A[1] itself (the first of # each byte). To compensate, we xor with (y >> 17), # which gets the entropy from the second bit of each byte # of the upper-half of y, and sets it in the first bit # of each byte of the lower half; the first two bytes # now contain 16 usable random bits x = A[1] % UInt64 bits = x โŠป x >> 17 for i = 1:n128 # << 5 to randomize the first bit of the 8th & 16th byte # (i.e. we move bit 52 (resp. 52 + 64), which is unused, # to position 57 (resp. 57 + 64)) A[i] = (A[i] โŠป A[i] << 5) & mask end end end for i = 16*n128+1:n1 @inbounds A1[i] = bits % Bool bits >>= 1 end A1 end ### randjump # Old randjump methods are deprecated, the scalar version is in the Future module. function _randjump(r::MersenneTwister, jumppoly::DSFMT.GF2X) adv = r.adv adv_jump = r.adv_jump s = MersenneTwister(copy(r.seed), DSFMT.dsfmt_jump(r.state, jumppoly)) reset_caches!(s) s.adv = adv s.adv_jump = adv_jump s end # NON-PUBLIC function jump(r::MersenneTwister, steps::Integer) iseven(steps) || throw(DomainError(steps, "steps must be even")) # steps >= 0 checked in calc_jump (`steps >> 1 < 0` if `steps < 0`) j = _randjump(r, Random.DSFMT.calc_jump(steps >> 1)) j.adv_jump += steps j end # NON-PUBLIC jump!(r::MersenneTwister, steps::Integer) = copy!(r, jump(r, steps)) ### constructors matching show (EXPERIMENTAL) # parameters in the tuples are: # 1: .adv_jump (jump steps) # 2: .adv (number of generated floats at the DSFMT_state level since seeding, besides jumps) # 3, 4: .adv_vals, .idxF (counters to reconstruct the float cache, optional if 5-6 not shown)) # 5, 6: .adv_ints, .idxI (counters to reconstruct the integer cache, optional) Random.MersenneTwister(seed::Union{Integer,Vector{UInt32}}, advance::NTuple{6,Integer}) = advance!(MersenneTwister(seed), advance...) Random.MersenneTwister(seed::Union{Integer,Vector{UInt32}}, advance::NTuple{4,Integer}) = MersenneTwister(seed, (advance..., 0, 0)) Random.MersenneTwister(seed::Union{Integer,Vector{UInt32}}, advance::NTuple{2,Integer}) = MersenneTwister(seed, (advance..., 0, 0, 0, 0)) # advances raw state (per fill_array!) of r by n steps (Float64 values) function _advance_n!(r::MersenneTwister, n::Int64, work::Vector{Float64}) n == 0 && return n < 0 && throw(DomainError(n, "can't advance $r to the specified state")) ms = dsfmt_get_min_array_size() % Int64 @assert n >= ms lw = ms + n % ms resize!(work, lw) GC.@preserve work fill_array!(r, pointer(work), lw, CloseOpen12()) c::Int64 = lw GC.@preserve work while n > c fill_array!(r, pointer(work), ms, CloseOpen12()) c += ms end @assert n == c end function _advance_to!(r::MersenneTwister, adv::Int64, work) _advance_n!(r, adv - r.adv, work) @assert r.adv == adv end function _advance_F!(r::MersenneTwister, adv_vals, idxF, work) _advance_to!(r, adv_vals, work) gen_rand(r) @assert r.adv_vals == adv_vals r.idxF = idxF end function _advance_I!(r::MersenneTwister, adv_ints, idxI, work) _advance_to!(r, adv_ints, work) mt_setfull!(r, Int) # sets r.adv_ints @assert r.adv_ints == adv_ints r.idxI = 16*length(r.ints) - 8*idxI end function advance!(r::MersenneTwister, adv_jump, adv, adv_vals, idxF, adv_ints, idxI) adv_jump = BigInt(adv_jump) adv, adv_vals, adv_ints = Int64.((adv, adv_vals, adv_ints)) idxF, idxI = Int.((idxF, idxI)) ms = dsfmt_get_min_array_size() % Int work = sizehint!(Vector{Float64}(), 2ms) adv_jump != 0 && jump!(r, adv_jump) advF = (adv_vals, idxF) != (0, 0) advI = (adv_ints, idxI) != (0, 0) if advI && advF @assert adv_vals != adv_ints if adv_vals < adv_ints _advance_F!(r, adv_vals, idxF, work) _advance_I!(r, adv_ints, idxI, work) else _advance_I!(r, adv_ints, idxI, work) _advance_F!(r, adv_vals, idxF, work) end elseif advF _advance_F!(r, adv_vals, idxF, work) elseif advI _advance_I!(r, adv_ints, idxI, work) else @assert adv == 0 end _advance_to!(r, adv, work) r end t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/generation.jlO������# This file is a part of Julia. License is MIT: https://julialang.org/license # Uniform random generation # This file contains the creation of Sampler objects and the associated generation of # random values from them. More specifically, given the specification S of a set # of values to pick from (e.g. 1:10, or "a string"), we define # # 1) Sampler(rng, S, ::Repetition) -> sampler # 2) rand(rng, sampler) -> random value # # Note that the 1) is automated when the sampler is not intended to carry information, # i.e. the default fall-backs SamplerType and SamplerTrivial are used. ## from types: rand(::Type, [dims...]) ### random floats Sampler(::Type{RNG}, ::Type{T}, n::Repetition) where {RNG<:AbstractRNG,T<:AbstractFloat} = Sampler(RNG, CloseOpen01(T), n) # generic random generation function which can be used by RNG implementors # it is not defined as a fallback rand method as this could create ambiguities rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float16}}) = Float16(reinterpret(Float32, (rand(r, UInt10(UInt32)) << 13) | 0x3f800000) - 1) rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01{Float32}}) = reinterpret(Float32, rand(r, UInt23()) | 0x3f800000) - 1 rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen12_64}) = reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt52())) rand(r::AbstractRNG, ::SamplerTrivial{CloseOpen01_64}) = rand(r, CloseOpen12()) - 1.0 #### BigFloat const bits_in_Limb = sizeof(Limb) << 3 const Limb_high_bit = one(Limb) << (bits_in_Limb-1) struct SamplerBigFloat{I<:FloatInterval{BigFloat}} <: Sampler{BigFloat} prec::Int nlimbs::Int limbs::Vector{Limb} shift::UInt function SamplerBigFloat{I}(prec::Int) where I<:FloatInterval{BigFloat} nlimbs = (prec-1) รท bits_in_Limb + 1 limbs = Vector{Limb}(undef, nlimbs) shift = nlimbs * bits_in_Limb - prec new(prec, nlimbs, limbs, shift) end end Sampler(::Type{<:AbstractRNG}, I::FloatInterval{BigFloat}, ::Repetition) = SamplerBigFloat{typeof(I)}(precision(BigFloat)) function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat) precision(z) == sp.prec || throw(ArgumentError("incompatible BigFloat precision")) limbs = sp.limbs rand!(rng, limbs) @inbounds begin limbs[1] <<= sp.shift randbool = iszero(limbs[end] & Limb_high_bit) limbs[end] |= Limb_high_bit end z.sign = 1 GC.@preserve limbs unsafe_copyto!(z.d, pointer(limbs), sp.nlimbs) randbool end function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen12{BigFloat}) _rand!(rng, z, sp) z.exp = 1 z end function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen01{BigFloat}) randbool = _rand!(rng, z, sp) z.exp = 0 randbool && ccall((:mpfr_sub_d, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Base.MPFR.MPFRRoundingMode), z, z, 0.5, Base.MPFR.ROUNDING_MODE[]) z end # alternative, with 1 bit less of precision # TODO: make an API for requesting full or not-full precision function _rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat, ::CloseOpen01{BigFloat}, ::Nothing) _rand!(rng, z, sp, CloseOpen12(BigFloat)) ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Base.MPFR.MPFRRoundingMode), z, z, 1, Base.MPFR.ROUNDING_MODE[]) z end rand!(rng::AbstractRNG, z::BigFloat, sp::SamplerBigFloat{T} ) where {T<:FloatInterval{BigFloat}} = _rand!(rng, z, sp, T()) rand(rng::AbstractRNG, sp::SamplerBigFloat{T}) where {T<:FloatInterval{BigFloat}} = rand!(rng, BigFloat(; precision=sp.prec), sp) ### random integers #### UniformBits rand(r::AbstractRNG, ::SamplerTrivial{UInt10Raw{UInt16}}) = rand(r, UInt16) rand(r::AbstractRNG, ::SamplerTrivial{UInt23Raw{UInt32}}) = rand(r, UInt32) rand(r::AbstractRNG, ::SamplerTrivial{UInt52Raw{UInt64}}) = _rand52(r, rng_native_52(r)) _rand52(r::AbstractRNG, ::Type{Float64}) = reinterpret(UInt64, rand(r, CloseOpen12())) _rand52(r::AbstractRNG, ::Type{UInt64}) = rand(r, UInt64) rand(r::AbstractRNG, ::SamplerTrivial{UInt104Raw{UInt128}}) = rand(r, UInt52Raw(UInt128)) << 52 โŠป rand(r, UInt52Raw(UInt128)) rand(r::AbstractRNG, ::SamplerTrivial{UInt10{UInt16}}) = rand(r, UInt10Raw()) & 0x03ff rand(r::AbstractRNG, ::SamplerTrivial{UInt23{UInt32}}) = rand(r, UInt23Raw()) & 0x007fffff rand(r::AbstractRNG, ::SamplerTrivial{UInt52{UInt64}}) = rand(r, UInt52Raw()) & 0x000fffffffffffff rand(r::AbstractRNG, ::SamplerTrivial{UInt104{UInt128}}) = rand(r, UInt104Raw()) & 0x000000ffffffffffffffffffffffffff rand(r::AbstractRNG, sp::SamplerTrivial{<:UniformBits{T}}) where {T} = rand(r, uint_default(sp[])) % T #### BitInteger # rand_generic methods are intended to help RNG implementors with common operations # we don't call them simply `rand` as this can easily contribute to create # ambiguities with user-side methods (forcing the user to resort to @eval) rand_generic(r::AbstractRNG, T::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}) = rand(r, UInt52Raw()) % T[] rand_generic(r::AbstractRNG, ::Type{UInt64}) = rand(r, UInt52Raw()) << 32 โŠป rand(r, UInt52Raw()) rand_generic(r::AbstractRNG, ::Type{UInt128}) = _rand128(r, rng_native_52(r)) _rand128(r::AbstractRNG, ::Type{UInt64}) = ((rand(r, UInt64) % UInt128) << 64) โŠป rand(r, UInt64) function _rand128(r::AbstractRNG, ::Type{Float64}) xor(rand(r, UInt52Raw(UInt128)) << 96, rand(r, UInt52Raw(UInt128)) << 48, rand(r, UInt52Raw(UInt128))) end rand_generic(r::AbstractRNG, ::Type{Int128}) = rand(r, UInt128) % Int128 rand_generic(r::AbstractRNG, ::Type{Int64}) = rand(r, UInt64) % Int64 ### random complex numbers rand(r::AbstractRNG, ::SamplerType{Complex{T}}) where {T<:Real} = complex(rand(r, T), rand(r, T)) ### random characters # returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff) function rand(r::AbstractRNG, ::SamplerType{T}) where {T<:AbstractChar} c = rand(r, 0x00000000:0x0010f7ff) (c < 0xd800) ? T(c) : T(c+0x800) end ## Generate random integer within a range ### BitInteger # there are three implemented samplers for unit ranges, the second one # assumes that Float64 (i.e. 52 random bits) is the native type for the RNG: # 1) "Fast" (SamplerRangeFast), which is most efficient when the range length is close # (or equal) to a power of 2 from below. # The tradeoff is faster creation of the sampler, but more consumption of entropy bits. # 2) "Slow" (SamplerRangeInt) which tries to use as few entropy bits as possible, at the # cost of a bigger upfront price associated with the creation of the sampler. # This sampler is most appropriate for slower random generators. # 3) "Nearly Division Less" (NDL) which is generally the fastest algorithm for types of size # up to 64 bits. This is the default for these types since Julia 1.5. # The "Fast" algorithm can be faster than NDL when the length of the range is # less than and close to a power of 2. Sampler(::Type{<:AbstractRNG}, r::AbstractUnitRange{T}, ::Repetition) where {T<:Base.BitInteger64} = SamplerRangeNDL(r) Sampler(::Type{<:AbstractRNG}, r::AbstractUnitRange{T}, ::Repetition) where {T<:Union{Int128,UInt128}} = SamplerRangeFast(r) #### helper functions uint_sup(::Type{<:Base.BitInteger32}) = UInt32 uint_sup(::Type{<:Union{Int64,UInt64}}) = UInt64 uint_sup(::Type{<:Union{Int128,UInt128}}) = UInt128 #### Fast struct SamplerRangeFast{U<:BitUnsigned,T<:BitInteger} <: Sampler{T} a::T # first element of the range bw::UInt # bit width m::U # range length - 1 mask::U # mask generated values before threshold rejection end SamplerRangeFast(r::AbstractUnitRange{T}) where T<:BitInteger = SamplerRangeFast(r, uint_sup(T)) function SamplerRangeFast(r::AbstractUnitRange{T}, ::Type{U}) where {T,U} isempty(r) && throw(ArgumentError("collection must be non-empty")) m = (last(r) - first(r)) % unsigned(T) % U # % unsigned(T) to not propagate sign bit bw = (Base.top_set_bit(m)) % UInt # bit-width mask = ((1 % U) << bw) - (1 % U) SamplerRangeFast{U,T}(first(r), bw, m, mask) end function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt32,T}) where T a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask # below, we don't use UInt32, to get reproducible values, whether Int is Int64 or Int32 x = rand(rng, LessThan(m, Masked(mask, UInt52Raw(UInt32)))) (x + a % UInt32) % T end has_fast_64(rng::AbstractRNG) = rng_native_52(rng) != Float64 # for MersenneTwister, both options have very similar performance function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt64,T}) where T a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask if !has_fast_64(rng) && bw <= 52 x = rand(rng, LessThan(m, Masked(mask, UInt52Raw()))) else x = rand(rng, LessThan(m, Masked(mask, uniform(UInt64)))) end (x + a % UInt64) % T end function rand(rng::AbstractRNG, sp::SamplerRangeFast{UInt128,T}) where T a, bw, m, mask = sp.a, sp.bw, sp.m, sp.mask if has_fast_64(rng) x = bw <= 64 ? rand(rng, LessThan(m % UInt64, Masked(mask % UInt64, uniform(UInt64)))) % UInt128 : rand(rng, LessThan(m, Masked(mask, uniform(UInt128)))) else x = bw <= 52 ? rand(rng, LessThan(m % UInt64, Masked(mask % UInt64, UInt52Raw()))) % UInt128 : bw <= 104 ? rand(rng, LessThan(m, Masked(mask, UInt104Raw()))) : rand(rng, LessThan(m, Masked(mask, uniform(UInt128)))) end x % T + a end #### "Slow" / SamplerRangeInt # remainder function according to Knuth, where rem_knuth(a, 0) = a rem_knuth(a::UInt, b::UInt) = a % (b + (b == 0)) + a * (b == 0) rem_knuth(a::T, b::T) where {T<:Unsigned} = b != 0 ? a % b : a # maximum multiple of k <= sup decremented by one, # that is 0xFFFF...FFFF if k = (typemax(T) - typemin(T)) + 1 and sup == typemax(T) - 1 # with intentional underflow # see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow # sup == 0 means typemax(T) + 1 maxmultiple(k::T, sup::T=zero(T)) where {T<:Unsigned} = (div(sup - k, k + (k == 0))*k + k - one(k))::T # similar but sup must not be equal to typemax(T) unsafe_maxmultiple(k::T, sup::T) where {T<:Unsigned} = div(sup, k + (k == 0))*k - one(k) struct SamplerRangeInt{T<:Integer,U<:Unsigned} <: Sampler{T} a::T # first element of the range bw::Int # bit width k::U # range length or zero for full range u::U # rejection threshold end SamplerRangeInt(r::AbstractUnitRange{T}) where T<:BitInteger = SamplerRangeInt(r, uint_sup(T)) function SamplerRangeInt(r::AbstractUnitRange{T}, ::Type{U}) where {T,U} isempty(r) && throw(ArgumentError("collection must be non-empty")) a = first(r) m = (last(r) - first(r)) % unsigned(T) % U k = m + one(U) bw = (Base.top_set_bit(m)) % Int mult = if U === UInt32 maxmultiple(k) elseif U === UInt64 bw <= 52 ? unsafe_maxmultiple(k, one(UInt64) << 52) : maxmultiple(k) else # U === UInt128 bw <= 52 ? unsafe_maxmultiple(k, one(UInt128) << 52) : bw <= 104 ? unsafe_maxmultiple(k, one(UInt128) << 104) : maxmultiple(k) end SamplerRangeInt{T,U}(a, bw, k, mult) # overflow ok end rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt32}) where {T<:BitInteger} = (unsigned(sp.a) + rem_knuth(rand(rng, LessThan(sp.u, UInt52Raw(UInt32))), sp.k)) % T # this function uses 52 bit entropy for small ranges of length <= 2^52 function rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt64}) where T<:BitInteger x = sp.bw <= 52 ? rand(rng, LessThan(sp.u, UInt52())) : rand(rng, LessThan(sp.u, uniform(UInt64))) return ((sp.a % UInt64) + rem_knuth(x, sp.k)) % T end function rand(rng::AbstractRNG, sp::SamplerRangeInt{T,UInt128}) where T<:BitInteger x = sp.bw <= 52 ? rand(rng, LessThan(sp.u, UInt52(UInt128))) : sp.bw <= 104 ? rand(rng, LessThan(sp.u, UInt104(UInt128))) : rand(rng, LessThan(sp.u, uniform(UInt128))) return ((sp.a % UInt128) + rem_knuth(x, sp.k)) % T end #### Nearly Division Less # cf. https://arxiv.org/abs/1805.10941 (algorithm 5) struct SamplerRangeNDL{U<:Unsigned,T} <: Sampler{T} a::T # first element of the range s::U # range length or zero for full range end function SamplerRangeNDL(r::AbstractUnitRange{T}) where {T} isempty(r) && throw(ArgumentError("collection must be non-empty")) a = first(r) U = uint_sup(T) s = (last(r) - first(r)) % unsigned(T) % U + one(U) # overflow ok # mod(-s, s) could be put in the Sampler object for repeated calls, but # this would be an advantage only for very big s and number of calls SamplerRangeNDL(a, s) end function rand(rng::AbstractRNG, sp::SamplerRangeNDL{U,T}) where {U,T} s = sp.s x = widen(rand(rng, U)) m = x * s l = m % U if l < s t = mod(-s, s) # as s is unsigned, -s is equal to 2^L - s in the paper while l < t x = widen(rand(rng, U)) m = x * s l = m % U end end (s == 0 ? x : m >> (8*sizeof(U))) % T + sp.a end ### BigInt struct SamplerBigInt{SP<:Sampler{Limb}} <: Sampler{BigInt} a::BigInt # first m::BigInt # range length - 1 nlimbs::Int # number of limbs in generated BigInt's (z โˆˆ [0, m]) nlimbsmax::Int # max number of limbs for z+a highsp::SP # sampler for the highest limb of z end function SamplerBigInt(::Type{RNG}, r::AbstractUnitRange{BigInt}, N::Repetition=Val(Inf) ) where {RNG<:AbstractRNG} m = last(r) - first(r) m.size < 0 && throw(ArgumentError("collection must be non-empty")) nlimbs = Int(m.size) hm = nlimbs == 0 ? Limb(0) : GC.@preserve m unsafe_load(m.d, nlimbs) highsp = Sampler(RNG, Limb(0):hm, N) nlimbsmax = max(nlimbs, abs(last(r).size), abs(first(r).size)) return SamplerBigInt(first(r), m, nlimbs, nlimbsmax, highsp) end Sampler(::Type{RNG}, r::AbstractUnitRange{BigInt}, N::Repetition) where {RNG<:AbstractRNG} = SamplerBigInt(RNG, r, N) rand(rng::AbstractRNG, sp::SamplerBigInt) = rand!(rng, BigInt(nbits = sp.nlimbsmax*8*sizeof(Limb)), sp) function rand!(rng::AbstractRNG, x::BigInt, sp::SamplerBigInt) nlimbs = sp.nlimbs nlimbs == 0 && return MPZ.set!(x, sp.a) MPZ.realloc2!(x, sp.nlimbsmax*8*sizeof(Limb)) @assert x.alloc >= nlimbs # we randomize x โˆˆ [0, m] with rejection sampling: # 1. the first nlimbs-1 limbs of x are uniformly randomized # 2. the high limb hx of x is sampled from 0:hm where hm is the # high limb of m # We repeat 1. and 2. until x <= m hm = GC.@preserve sp unsafe_load(sp.m.d, nlimbs) GC.@preserve x begin limbs = UnsafeView(x.d, nlimbs-1) while true rand!(rng, limbs) hx = limbs[nlimbs] = rand(rng, sp.highsp) hx < hm && break # avoid calling mpn_cmp most of the time MPZ.mpn_cmp(x, sp.m, nlimbs) <= 0 && break end # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6) while nlimbs > 0 limbs[nlimbs] != 0 && break nlimbs -= 1 end x.size = nlimbs end MPZ.add!(x, sp.a) end ## random values from AbstractArray Sampler(::Type{RNG}, r::AbstractArray, n::Repetition) where {RNG<:AbstractRNG} = SamplerSimple(r, Sampler(RNG, firstindex(r):lastindex(r), n)) rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractArray,<:Sampler}) = @inbounds return sp[][rand(rng, sp.data)] ## random values from Dict function Sampler(::Type{RNG}, t::Dict, ::Repetition) where RNG<:AbstractRNG isempty(t) && throw(ArgumentError("collection must be non-empty")) # we use Val(Inf) below as rand is called repeatedly internally # even for generating only one random value from t SamplerSimple(t, Sampler(RNG, LinearIndices(t.slots), Val(Inf))) end function rand(rng::AbstractRNG, sp::SamplerSimple{<:Dict,<:Sampler}) while true i = rand(rng, sp.data) Base.isslotfilled(sp[], i) && @inbounds return (sp[].keys[i] => sp[].vals[i]) end end ## random values from Set Sampler(::Type{RNG}, t::Set{T}, n::Repetition) where {RNG<:AbstractRNG,T} = SamplerTag{Set{T}}(Sampler(RNG, t.dict, n)) rand(rng::AbstractRNG, sp::SamplerTag{<:Set,<:Sampler}) = rand(rng, sp.data).first ## random values from BitSet function Sampler(RNG::Type{<:AbstractRNG}, t::BitSet, n::Repetition) isempty(t) && throw(ArgumentError("collection must be non-empty")) SamplerSimple(t, Sampler(RNG, minimum(t):maximum(t), Val(Inf))) end function rand(rng::AbstractRNG, sp::SamplerSimple{BitSet,<:Sampler}) while true n = rand(rng, sp.data) n in sp[] && return n end end ## random values from AbstractDict/AbstractSet # we defer to _Sampler to avoid ambiguities with a call like Sampler(rng, Set(1), Val(1)) Sampler(RNG::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, n::Repetition) = _Sampler(RNG, t, n) # avoid linear complexity for repeated calls _Sampler(RNG::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, n::Val{Inf}) = Sampler(RNG, collect(t), n) # when generating only one element, avoid the call to collect _Sampler(::Type{<:AbstractRNG}, t::Union{AbstractDict,AbstractSet}, ::Val{1}) = SamplerTrivial(t) function nth(iter, n::Integer)::eltype(iter) for (i, x) in enumerate(iter) i == n && return x end end rand(rng::AbstractRNG, sp::SamplerTrivial{<:Union{AbstractDict,AbstractSet}}) = nth(sp[], rand(rng, 1:length(sp[]))) ## random characters from a string # we use collect(str), which is most of the time more efficient than specialized methods # (except maybe for very small arrays) Sampler(RNG::Type{<:AbstractRNG}, str::AbstractString, n::Val{Inf}) = Sampler(RNG, collect(str), n) # when generating only one char from a string, the specialized method below # is usually more efficient Sampler(RNG::Type{<:AbstractRNG}, str::AbstractString, ::Val{1}) = SamplerSimple(str, Sampler(RNG, 1:_lastindex(str), Val(Inf))) isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(GC.@preserve s unsafe_load(pointer(s), i)) isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) _lastindex(s::String) = sizeof(s) _lastindex(s::AbstractString) = lastindex(s) function rand(rng::AbstractRNG, sp::SamplerSimple{<:AbstractString,<:Sampler})::Char str = sp[] while true pos = rand(rng, sp.data) isvalid_unsafe(str, pos) && return str[pos] end end ## random elements from tuples ### 1 Sampler(::Type{<:AbstractRNG}, t::Tuple{A}, ::Repetition) where {A} = SamplerTrivial(t) rand(rng::AbstractRNG, sp::SamplerTrivial{Tuple{A}}) where {A} = @inbounds return sp[][1] ### 2 Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B}, n::Repetition) where {A,B} = SamplerSimple(t, Sampler(RNG, Bool, n)) rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B}}) where {A,B} = @inbounds return sp[][1 + rand(rng, sp.data)] ### 3 Sampler(RNG::Type{<:AbstractRNG}, t::Tuple{A,B,C}, n::Repetition) where {A,B,C} = SamplerSimple(t, Sampler(RNG, UInt52(), n)) function rand(rng::AbstractRNG, sp::SamplerSimple{Tuple{A,B,C}}) where {A,B,C} local r while true r = rand(rng, sp.data) r != 0x000fffffffffffff && break # _very_ likely end @inbounds return sp[][1 + r รท 0x0005555555555555] end ### n @generated function Sampler(RNG::Type{<:AbstractRNG}, t::Tuple, n::Repetition) l = fieldcount(t) if l < typemax(UInt32) && ispow2(l) :(SamplerSimple(t, Sampler(RNG, UInt32, n))) else :(SamplerSimple(t, Sampler(RNG, Base.OneTo(length(t)), n))) end end @generated function rand(rng::AbstractRNG, sp::SamplerSimple{T}) where T<:Tuple l = fieldcount(T) if l < typemax(UInt32) && ispow2(l) quote r = rand(rng, sp.data) & ($l-1) @inbounds return sp[][1 + r] end else :(@inbounds return sp[][rand(rng, sp.data)]) end end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/normal.jl๘ณ������# This file is a part of Julia. License is MIT: https://julialang.org/license # Normally distributed random numbers using Ziggurat algorithm # The Ziggurat Method for generating random variables - Marsaglia and Tsang # Paper and reference code: http://www.jstatsoft.org/v05/i08/ # randmtzig (covers also exponential variates) ## randn """ randn([rng=default_rng()], [T=Float64], [dims...]) Generate a normally-distributed random number of type `T` with mean 0 and standard deviation 1. Optionally generate an array of normally-distributed random numbers. The `Base` module currently provides an implementation for the types [`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default), and their [`Complex`](@ref) counterparts. When the type argument is complex, the values are drawn from the circularly symmetric complex normal distribution of variance 1 (corresponding to real and imaginary part having independent normal distribution with mean zero and variance `1/2`). See also [`randn!`](@ref) to act in-place. # Examples ```jldoctest julia> using Random julia> rng = MersenneTwister(1234); julia> randn(rng, ComplexF64) 0.6133070881429037 - 0.6376291670853887im julia> randn(rng, ComplexF32, (2, 3)) 2ร—3 Matrix{ComplexF32}: -0.349649-0.638457im 0.376756-0.192146im -0.396334-0.0136413im 0.611224+1.56403im 0.355204-0.365563im 0.0905552+1.31012im ``` """ @inline function randn(rng::AbstractRNG=default_rng()) #= When defining `@inline randn(rng::AbstractRNG=default_rng()) = _randn(rng, rand(rng, UInt52Raw()))` the function call to `_randn` is currently not inlined, resulting in slightly worse performance for scalar random normal numbers than repeating the code of `_randn` inside the following function. =# @inbounds begin r = rand(rng, UInt52()) # the following code is identical to the one in `_randn(rng::AbstractRNG, r::UInt64)` rabs = Int64(r>>1) # One bit for the sign idx = rabs & 0xFF x = ifelse(r % Bool, -rabs, rabs)*wi[idx+1] rabs < ki[idx+1] && return x # 99.3% of the time we return here 1st try return randn_unlikely(rng, idx, rabs, x) end end @inline function _randn(rng::AbstractRNG, r::UInt64) @inbounds begin r &= 0x000fffffffffffff rabs = Int64(r>>1) # One bit for the sign idx = rabs & 0xFF x = ifelse(r % Bool, -rabs, rabs)*wi[idx+1] rabs < ki[idx+1] && return x # 99.3% of the time we return here 1st try return randn_unlikely(rng, idx, rabs, x) end end # this unlikely branch is put in a separate function for better efficiency @noinline function randn_unlikely(rng, idx, rabs, x) @inbounds if idx == 0 while true xx = -ziggurat_nor_inv_r*log(rand(rng)) yy = -log(rand(rng)) yy+yy > xx*xx && return (rabs >> 8) % Bool ? -ziggurat_nor_r-xx : ziggurat_nor_r+xx end elseif (fi[idx] - fi[idx+1])*rand(rng) + fi[idx+1] < exp(-0.5*x*x) return x # return from the triangular area else return randn(rng) end end ### complex randn Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) ### fallback randn for float types defining rand: function randn(rng::AbstractRNG, ::Type{T}) where {T<:AbstractFloat} # Marsaglia polar variant of Boxโ€“Muller transform: while true x, y = 2rand(rng, T)-1, 2rand(rng, T)-1 0 < (s = x^2 + y^2) < 1 && return x * sqrt(-2log(s)/s) end end ## randexp """ randexp([rng=default_rng()], [T=Float64], [dims...]) Generate a random number of type `T` according to the exponential distribution with scale 1. Optionally generate an array of such random numbers. The `Base` module currently provides an implementation for the types [`Float16`](@ref), [`Float32`](@ref), and [`Float64`](@ref) (the default). # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> randexp(rng, Float32) 2.4835055f0 julia> randexp(rng, 3, 3) 3ร—3 Matrix{Float64}: 1.5167 1.30652 0.344435 0.604436 2.78029 0.418516 0.695867 0.693292 0.643644 ``` """ randexp(rng::AbstractRNG=default_rng()) = _randexp(rng, rand(rng, UInt52Raw())) function _randexp(rng::AbstractRNG, ri::UInt64) @inbounds begin ri &= 0x000fffffffffffff idx = ri & 0xFF x = ri*we[idx+1] ri < ke[idx+1] && return x # 98.9% of the time we return here 1st try return randexp_unlikely(rng, idx, x) end end @noinline function randexp_unlikely(rng, idx, x) @inbounds if idx == 0 return ziggurat_exp_r - log(rand(rng)) elseif (fe[idx] - fe[idx+1])*rand(rng) + fe[idx+1] < exp(-x) return x # return from the triangular area else return randexp(rng) end end ### fallback randexp for float types defining rand: randexp(rng::AbstractRNG, ::Type{T}) where {T<:AbstractFloat} = -log1p(-rand(rng, T)) ## arrays & other scalar methods """ randn!([rng=default_rng()], A::AbstractArray) -> A Fill the array `A` with normally-distributed (mean 0, standard deviation 1) random numbers. Also see the [`rand`](@ref) function. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> randn!(rng, zeros(5)) 5-element Vector{Float64}: 0.8673472019512456 -0.9017438158568171 -0.4944787535042339 -0.9029142938652416 0.8644013132535154 ``` """ function randn! end """ randexp!([rng=default_rng()], A::AbstractArray) -> A Fill the array `A` with random numbers following the exponential distribution (with scale 1). # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> randexp!(rng, zeros(5)) 5-element Vector{Float64}: 2.4835053723904896 1.516703605376473 0.6044364871025417 0.6958665886385867 1.3065196315496677 ``` """ function randexp! end for randfun in [:randn, :randexp] randfun! = Symbol(randfun, :!) _randfun = Symbol(:_, randfun) @eval begin # scalars $randfun(rng::AbstractRNG, T::BitFloatType) = convert(T, $randfun(rng)) $randfun(::Type{T}) where {T} = $randfun(default_rng(), T) # filling arrays function $randfun!(rng::AbstractRNG, A::AbstractArray{T}) where T for i in eachindex(A) @inbounds A[i] = $randfun(rng, T) end A end # optimization for MersenneTwister, which randomizes natively Array{Float64} function $randfun!(rng::MersenneTwister, A::Array{Float64}) if length(A) < 13 for i in eachindex(A) @inbounds A[i] = $randfun(rng, Float64) end else rand!(rng, A, CloseOpen12()) for i in eachindex(A) @inbounds A[i] = $_randfun(rng, reinterpret(UInt64, A[i])) end end A end # optimization for Xoshiro, which randomizes natively Array{UInt64} function $randfun!(rng::Union{Xoshiro, TaskLocalRNG}, A::Array{Float64}) if length(A) < 7 for i in eachindex(A) @inbounds A[i] = $randfun(rng, Float64) end else GC.@preserve A rand!(rng, UnsafeView{UInt64}(pointer(A), length(A))) for i in eachindex(A) @inbounds A[i] = $_randfun(rng, reinterpret(UInt64, A[i]) >>> 12) end end A end $randfun!(A::AbstractArray) = $randfun!(default_rng(), A) # generating arrays $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(undef, dims)) # Note that this method explicitly does not define $randfun(rng, T), # in order to prevent an infinite recursion. $randfun(rng::AbstractRNG, ::Type{T}, dim1::Integer, dims::Integer...) where {T} = $randfun!(rng, Array{T}(undef, dim1, dims...)) $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(default_rng(), T, dims) $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(default_rng(), T, dims...) $randfun(rng::AbstractRNG, dims::Dims ) = $randfun(rng, Float64, dims) $randfun(rng::AbstractRNG, dims::Integer... ) = $randfun(rng, Float64, dims...) $randfun( dims::Dims ) = $randfun(default_rng(), Float64, dims) $randfun( dims::Integer... ) = $randfun(default_rng(), Float64, dims...) end end ## Tables for normal variates const ki = UInt64[0x0007799ec012f7b2,0x0000000000000000,0x0006045f4c7de363,0x0006d1aa7d5ec0a5, 0x000728fb3f60f777,0x0007592af4e9fbc0,0x000777a5c0bf655d,0x00078ca3857d2256, 0x00079bf6b0ffe58b,0x0007a7a34ab092ad,0x0007b0d2f20dd1cb,0x0007b83d3aa9cb52, 0x0007be597614224d,0x0007c3788631abe9,0x0007c7d32bc192ee,0x0007cb9263a6e86d, 0x0007ced483edfa84,0x0007d1b07ac0fd39,0x0007d437ef2da5fc,0x0007d678b069aa6e, 0x0007d87db38c5c87,0x0007da4fc6a9ba62,0x0007dbf611b37f3b,0x0007dd7674d0f286, 0x0007ded5ce8205f6,0x0007e018307fb62b,0x0007e141081bd124,0x0007e2533d712de8, 0x0007e3514bbd7718,0x0007e43d54944b52,0x0007e5192f25ef42,0x0007e5e67481118d, 0x0007e6a6897c1ce2,0x0007e75aa6c7f64c,0x0007e803df8ee498,0x0007e8a326eb6272, 0x0007e93954717a28,0x0007e9c727f8648f,0x0007ea4d4cc85a3c,0x0007eacc5c4907a9, 0x0007eb44e0474cf6,0x0007ebb754e47419,0x0007ec242a3d8474,0x0007ec8bc5d69645, 0x0007ecee83d3d6e9,0x0007ed4cb8082f45,0x0007eda6aee0170f,0x0007edfcae2dfe68, 0x0007ee4ef5dccd3e,0x0007ee9dc08c394e,0x0007eee9441a17c7,0x0007ef31b21b4fb1, 0x0007ef773846a8a7,0x0007efba00d35a17,0x0007effa32ccf69f,0x0007f037f25e1278, 0x0007f0736112d12c,0x0007f0ac9e145c25,0x0007f0e3c65e1fcc,0x0007f118f4ed8e54, 0x0007f14c42ed0dc8,0x0007f17dc7daa0c3,0x0007f1ad99aac6a5,0x0007f1dbcce80015, 0x0007f20874cf56bf,0x0007f233a36a3b9a,0x0007f25d69a604ad,0x0007f285d7694a92, 0x0007f2acfba75e3b,0x0007f2d2e4720909,0x0007f2f79f09c344,0x0007f31b37ec883b, 0x0007f33dbae36abc,0x0007f35f330f08d5,0x0007f37faaf2fa79,0x0007f39f2c805380, 0x0007f3bdc11f4f1c,0x0007f3db71b83850,0x0007f3f846bba121,0x0007f4144829f846, 0x0007f42f7d9a8b9d,0x0007f449ee420432,0x0007f463a0f8675e,0x0007f47c9c3ea77b, 0x0007f494e643cd8e,0x0007f4ac84e9c475,0x0007f4c37dc9cd50,0x0007f4d9d638a432, 0x0007f4ef934a5b6a,0x0007f504b9d5f33d,0x0007f5194e78b352,0x0007f52d55994a96, 0x0007f540d36aba0c,0x0007f553cbef0e77,0x0007f56642f9ec8f,0x0007f5783c32f31e, 0x0007f589bb17f609,0x0007f59ac2ff1525,0x0007f5ab5718b15a,0x0007f5bb7a71427c, 0x0007f5cb2ff31009,0x0007f5da7a67cebe,0x0007f5e95c7a24e7,0x0007f5f7d8b7171e, 0x0007f605f18f5ef4,0x0007f613a958ad0a,0x0007f621024ed7e9,0x0007f62dfe94f8cb, 0x0007f63aa036777a,0x0007f646e928065a,0x0007f652db488f88,0x0007f65e786213ff, 0x0007f669c22a7d8a,0x0007f674ba446459,0x0007f67f623fc8db,0x0007f689bb9ac294, 0x0007f693c7c22481,0x0007f69d881217a6,0x0007f6a6fdd6ac36,0x0007f6b02a4c61ee, 0x0007f6b90ea0a7f4,0x0007f6c1abf254c0,0x0007f6ca03521664,0x0007f6d215c2db82, 0x0007f6d9e43a3559,0x0007f6e16fa0b329,0x0007f6e8b8d23729,0x0007f6efc09e4569, 0x0007f6f687c84cbf,0x0007f6fd0f07ea09,0x0007f703570925e2,0x0007f709606cad03, 0x0007f70f2bc8036f,0x0007f714b9a5b292,0x0007f71a0a85725d,0x0007f71f1edc4d9e, 0x0007f723f714c179,0x0007f728938ed843,0x0007f72cf4a03fa0,0x0007f7311a945a16, 0x0007f73505ac4bf8,0x0007f738b61f03bd,0x0007f73c2c193dc0,0x0007f73f67bd835c, 0x0007f74269242559,0x0007f745305b31a1,0x0007f747bd666428,0x0007f74a103f12ed, 0x0007f74c28d414f5,0x0007f74e0709a42d,0x0007f74faab939f9,0x0007f75113b16657, 0x0007f75241b5a155,0x0007f753347e16b8,0x0007f753ebb76b7c,0x0007f75467027d05, 0x0007f754a5f4199d,0x0007f754a814b207,0x0007f7546ce003ae,0x0007f753f3c4bb29, 0x0007f7533c240e92,0x0007f75245514f41,0x0007f7510e91726c,0x0007f74f971a9012, 0x0007f74dde135797,0x0007f74be2927971,0x0007f749a39e051c,0x0007f747202aba8a, 0x0007f744571b4e3c,0x0007f741473f9efe,0x0007f73def53dc43,0x0007f73a4dff9bff, 0x0007f73661d4deaf,0x0007f732294f003f,0x0007f72da2d19444,0x0007f728cca72bda, 0x0007f723a5000367,0x0007f71e29f09627,0x0007f7185970156b,0x0007f7123156c102, 0x0007f70baf5c1e2c,0x0007f704d1150a23,0x0007f6fd93f1a4e5,0x0007f6f5f53b10b6, 0x0007f6edf211023e,0x0007f6e587671ce9,0x0007f6dcb2021679,0x0007f6d36e749c64, 0x0007f6c9b91bf4c6,0x0007f6bf8e1c541b,0x0007f6b4e95ce015,0x0007f6a9c68356ff, 0x0007f69e20ef5211,0x0007f691f3b517eb,0x0007f6853997f321,0x0007f677ed03ff19, 0x0007f66a08075bdc,0x0007f65b844ab75a,0x0007f64c5b091860,0x0007f63c8506d4bc, 0x0007f62bfa8798fe,0x0007f61ab34364b0,0x0007f608a65a599a,0x0007f5f5ca4737e8, 0x0007f5e214d05b48,0x0007f5cd7af7066e,0x0007f5b7f0e4c2a1,0x0007f5a169d68fcf, 0x0007f589d80596a5,0x0007f5712c8d0174,0x0007f557574c912b,0x0007f53c46c77193, 0x0007f51fe7feb9f2,0x0007f5022646ecfb,0x0007f4e2eb17ab1d,0x0007f4c21dd4a3d1, 0x0007f49fa38ea394,0x0007f47b5ebb62eb,0x0007f4552ee27473,0x0007f42cf03d58f5, 0x0007f4027b48549f,0x0007f3d5a44119df,0x0007f3a63a8fb552,0x0007f37408155100, 0x0007f33ed05b55ec,0x0007f3064f9c183e,0x0007f2ca399c7ba1,0x0007f28a384bb940, 0x0007f245ea1b7a2b,0x0007f1fcdffe8f1b,0x0007f1ae9af758cd,0x0007f15a8917f27e, 0x0007f10001ccaaab,0x0007f09e413c418a,0x0007f034627733d7,0x0007efc15815b8d5, 0x0007ef43e2bf7f55,0x0007eeba84e31dfe,0x0007ee237294df89,0x0007ed7c7c170141, 0x0007ecc2f0d95d3a,0x0007ebf377a46782,0x0007eb09d6deb285,0x0007ea00a4f17808, 0x0007e8d0d3da63d6,0x0007e771023b0fcf,0x0007e5d46c2f08d8,0x0007e3e937669691, 0x0007e195978f1176,0x0007deb2c0e05c1c,0x0007db0362002a19,0x0007d6202c151439, 0x0007cf4b8f00a2cb,0x0007c4fd24520efd,0x0007b362fbf81816,0x00078d2d25998e24] const wi = [1.7367254121602630e-15,9.5586603514556339e-17,1.2708704834810623e-16, 1.4909740962495474e-16,1.6658733631586268e-16,1.8136120810119029e-16, 1.9429720153135588e-16,2.0589500628482093e-16,2.1646860576895422e-16, 2.2622940392218116e-16,2.3532718914045892e-16,2.4387234557428771e-16, 2.5194879829274225e-16,2.5962199772528103e-16,2.6694407473648285e-16, 2.7395729685142446e-16,2.8069646002484804e-16,2.8719058904113930e-16, 2.9346417484728883e-16,2.9953809336782113e-16,3.0543030007192440e-16, 3.1115636338921572e-16,3.1672988018581815e-16,3.2216280350549905e-16, 3.2746570407939751e-16,3.3264798116841710e-16,3.3771803417353232e-16, 3.4268340353119356e-16,3.4755088731729758e-16,3.5232663846002031e-16, 3.5701624633953494e-16,3.6162480571598339e-16,3.6615697529653540e-16, 3.7061702777236077e-16,3.7500889278747798e-16,3.7933619401549554e-16, 3.8360228129677279e-16,3.8781025861250247e-16,3.9196300853257678e-16, 3.9606321366256378e-16,4.0011337552546690e-16,4.0411583124143332e-16, 4.0807276830960448e-16,4.1198623774807442e-16,4.1585816580828064e-16, 4.1969036444740733e-16,4.2348454071520708e-16,4.2724230518899761e-16, 4.3096517957162941e-16,4.3465460355128760e-16,4.3831194100854571e-16, 4.4193848564470665e-16,4.4553546609579137e-16,4.4910405058828750e-16, 4.5264535118571397e-16,4.5616042766900381e-16,4.5965029108849407e-16, 4.6311590702081647e-16,4.6655819856008752e-16,4.6997804906941950e-16, 4.7337630471583237e-16,4.7675377680908526e-16,4.8011124396270155e-16, 4.8344945409350080e-16,4.8676912627422087e-16,4.9007095245229938e-16, 4.9335559904654139e-16,4.9662370843221783e-16,4.9987590032409088e-16, 5.0311277306593187e-16,5.0633490483427195e-16,5.0954285476338923e-16, 5.1273716399787966e-16,5.1591835667857364e-16,5.1908694086703434e-16, 5.2224340941340417e-16,5.2538824077194543e-16,5.2852189976823820e-16, 5.3164483832166176e-16,5.3475749612647295e-16,5.3786030129452348e-16, 5.4095367096239933e-16,5.4403801186554671e-16,5.4711372088173611e-16, 5.5018118554603362e-16,5.5324078453927836e-16,5.5629288815190902e-16, 5.5933785872484621e-16,5.6237605106900435e-16,5.6540781286489604e-16, 5.6843348504368141e-16,5.7145340215092040e-16,5.7446789269419609e-16, 5.7747727947569648e-16,5.8048187991076857e-16,5.8348200633338921e-16, 5.8647796628943653e-16,5.8947006281858718e-16,5.9245859472561339e-16, 5.9544385684180598e-16,5.9842614027720281e-16,6.0140573266426640e-16, 6.0438291839361250e-16,6.0735797884236057e-16,6.1033119259564394e-16, 6.1330283566179110e-16,6.1627318168165963e-16,6.1924250213258470e-16, 6.2221106652737879e-16,6.2517914260879998e-16,6.2814699653988953e-16, 6.3111489309056042e-16,6.3408309582080600e-16,6.3705186726088149e-16, 6.4002146908880247e-16,6.4299216230548961e-16,6.4596420740788321e-16, 6.4893786456033965e-16,6.5191339376461587e-16,6.5489105502874154e-16, 6.5787110853507413e-16,6.6085381480782587e-16,6.6383943488035057e-16, 6.6682823046247459e-16,6.6982046410815579e-16,6.7281639938375311e-16, 6.7581630103719006e-16,6.7882043516829803e-16,6.8182906940062540e-16, 6.8484247305500383e-16,6.8786091732516637e-16,6.9088467545571690e-16, 6.9391402292275690e-16,6.9694923761748294e-16,6.9999060003307640e-16, 7.0303839345521508e-16,7.0609290415654822e-16,7.0915442159548734e-16, 7.1222323861967788e-16,7.1529965167453030e-16,7.1838396101720629e-16, 7.2147647093647067e-16,7.2457748997883870e-16,7.2768733118146927e-16, 7.3080631231227429e-16,7.3393475611774048e-16,7.3707299057898310e-16, 7.4022134917657997e-16,7.4338017116476479e-16,7.4654980185558890e-16, 7.4973059291369793e-16,7.5292290266240584e-16,7.5612709640179217e-16, 7.5934354673958895e-16,7.6257263393567558e-16,7.6581474626104873e-16, 7.6907028037219191e-16,7.7233964170182985e-16,7.7562324486711744e-16, 7.7892151409638524e-16,7.8223488367564108e-16,7.8556379841610841e-16, 7.8890871414417552e-16,7.9227009821522709e-16,7.9564843005293662e-16, 7.9904420171571300e-16,8.0245791849212591e-16,8.0589009952726568e-16, 8.0934127848215009e-16,8.1281200422845008e-16,8.1630284158098775e-16, 8.1981437207065329e-16,8.2334719476060504e-16,8.2690192710884700e-16, 8.3047920588053737e-16,8.3407968811366288e-16,8.3770405214202216e-16, 8.4135299867980282e-16,8.4502725197240968e-16,8.4872756101861549e-16, 8.5245470086955962e-16,8.5620947401062333e-16,8.5999271183276646e-16, 8.6380527620052589e-16,8.6764806112455816e-16,8.7152199454736980e-16, 8.7542804025171749e-16,8.7936719990210427e-16,8.8334051523084080e-16, 8.8734907038131345e-16,8.9139399442240861e-16,8.9547646404950677e-16, 8.9959770648910994e-16,9.0375900262601175e-16,9.0796169037400680e-16, 9.1220716831348461e-16,9.1649689962191353e-16,9.2083241632623076e-16, 9.2521532390956933e-16,9.2964730630864167e-16,9.3413013134252651e-16, 9.3866565661866598e-16,9.4325583596767065e-16,9.4790272646517382e-16, 9.5260849610662787e-16,9.5737543220974496e-16,9.6220595062948384e-16, 9.6710260588230542e-16,9.7206810229016259e-16,9.7710530627072088e-16, 9.8221725991905411e-16,9.8740719604806711e-16,9.9267855488079765e-16, 9.9803500261836449e-16,1.0034804521436181e-15,1.0090190861637457e-15, 1.0146553831467086e-15,1.0203941464683124e-15,1.0262405372613567e-15, 1.0322001115486456e-15,1.0382788623515399e-15,1.0444832676000471e-15, 1.0508203448355195e-15,1.0572977139009890e-15,1.0639236690676801e-15, 1.0707072623632994e-15,1.0776584002668106e-15,1.0847879564403425e-15, 1.0921079038149563e-15,1.0996314701785628e-15,1.1073733224935752e-15, 1.1153497865853155e-15,1.1235791107110833e-15,1.1320817840164846e-15, 1.1408809242582780e-15,1.1500027537839792e-15,1.1594771891449189e-15, 1.1693385786910960e-15,1.1796266352955801e-15,1.1903876299282890e-15, 1.2016759392543819e-15,1.2135560818666897e-15,1.2261054417450561e-15, 1.2394179789163251e-15,1.2536093926602567e-15,1.2688244814255010e-15, 1.2852479319096109e-15,1.3031206634689985e-15,1.3227655770195326e-15, 1.3446300925011171e-15,1.3693606835128518e-15,1.3979436672775240e-15, 1.4319989869661328e-15,1.4744848603597596e-15,1.5317872741611144e-15, 1.6227698675312968e-15] const fi = [1.0000000000000000e+00,9.7710170126767082e-01,9.5987909180010600e-01, 9.4519895344229909e-01,9.3206007595922991e-01,9.1999150503934646e-01, 9.0872644005213032e-01,8.9809592189834297e-01,8.8798466075583282e-01, 8.7830965580891684e-01,8.6900868803685649e-01,8.6003362119633109e-01, 8.5134625845867751e-01,8.4291565311220373e-01,8.3471629298688299e-01, 8.2672683394622093e-01,8.1892919160370192e-01,8.1130787431265572e-01, 8.0384948317096383e-01,7.9654233042295841e-01,7.8937614356602404e-01, 7.8234183265480195e-01,7.7543130498118662e-01,7.6863731579848571e-01, 7.6195334683679483e-01,7.5537350650709567e-01,7.4889244721915638e-01, 7.4250529634015061e-01,7.3620759812686210e-01,7.2999526456147568e-01, 7.2386453346862967e-01,7.1781193263072152e-01,7.1183424887824798e-01, 7.0592850133275376e-01,7.0009191813651117e-01,6.9432191612611627e-01, 6.8861608300467136e-01,6.8297216164499430e-01,6.7738803621877308e-01, 6.7186171989708166e-01,6.6639134390874977e-01,6.6097514777666277e-01, 6.5561147057969693e-01,6.5029874311081637e-01,6.4503548082082196e-01, 6.3982027745305614e-01,6.3465179928762327e-01,6.2952877992483625e-01, 6.2445001554702606e-01,6.1941436060583399e-01,6.1442072388891344e-01, 6.0946806492577310e-01,6.0455539069746733e-01,5.9968175261912482e-01, 5.9484624376798689e-01,5.9004799633282545e-01,5.8528617926337090e-01, 5.8055999610079034e-01,5.7586868297235316e-01,5.7121150673525267e-01, 5.6658776325616389e-01,5.6199677581452390e-01,5.5743789361876550e-01, 5.5291049042583185e-01,5.4841396325526537e-01,5.4394773119002582e-01, 5.3951123425695158e-01,5.3510393238045717e-01,5.3072530440366150e-01, 5.2637484717168403e-01,5.2205207467232140e-01,5.1775651722975591e-01, 5.1348772074732651e-01,5.0924524599574761e-01,5.0502866794346790e-01, 5.0083757512614835e-01,4.9667156905248933e-01,4.9253026364386815e-01, 4.8841328470545758e-01,4.8432026942668288e-01,4.8025086590904642e-01, 4.7620473271950547e-01,4.7218153846772976e-01,4.6818096140569321e-01, 4.6420268904817391e-01,4.6024641781284248e-01,4.5631185267871610e-01, 4.5239870686184824e-01,4.4850670150720273e-01,4.4463556539573912e-01, 4.4078503466580377e-01,4.3695485254798533e-01,4.3314476911265209e-01, 4.2935454102944126e-01,4.2558393133802180e-01,4.2183270922949573e-01, 4.1810064983784795e-01,4.1438753404089090e-01,4.1069314827018799e-01, 4.0701728432947315e-01,4.0335973922111429e-01,3.9972031498019700e-01, 3.9609881851583223e-01,3.9249506145931540e-01,3.8890886001878855e-01, 3.8534003484007706e-01,3.8178841087339344e-01,3.7825381724561896e-01, 3.7473608713789086e-01,3.7123505766823922e-01,3.6775056977903225e-01, 3.6428246812900372e-01,3.6083060098964775e-01,3.5739482014578022e-01, 3.5397498080007656e-01,3.5057094148140588e-01,3.4718256395679348e-01, 3.4380971314685055e-01,3.4045225704452164e-01,3.3711006663700588e-01, 3.3378301583071823e-01,3.3047098137916342e-01,3.2717384281360129e-01, 3.2389148237639104e-01,3.2062378495690530e-01,3.1737063802991350e-01, 3.1413193159633707e-01,3.1090755812628634e-01,3.0769741250429189e-01, 3.0450139197664983e-01,3.0131939610080288e-01,2.9815132669668531e-01, 2.9499708779996164e-01,2.9185658561709499e-01,2.8872972848218270e-01, 2.8561642681550159e-01,2.8251659308370741e-01,2.7943014176163772e-01, 2.7635698929566810e-01,2.7329705406857691e-01,2.7025025636587519e-01, 2.6721651834356114e-01,2.6419576399726080e-01,2.6118791913272082e-01, 2.5819291133761890e-01,2.5521066995466168e-01,2.5224112605594190e-01, 2.4928421241852824e-01,2.4633986350126363e-01,2.4340801542275012e-01, 2.4048860594050039e-01,2.3758157443123795e-01,2.3468686187232990e-01, 2.3180441082433859e-01,2.2893416541468023e-01,2.2607607132238020e-01, 2.2323007576391746e-01,2.2039612748015194e-01,2.1757417672433113e-01, 2.1476417525117358e-01,2.1196607630703015e-01,2.0917983462112499e-01, 2.0640540639788071e-01,2.0364274931033485e-01,2.0089182249465656e-01, 1.9815258654577511e-01,1.9542500351413428e-01,1.9270903690358912e-01, 1.9000465167046496e-01,1.8731181422380025e-01,1.8463049242679927e-01, 1.8196065559952254e-01,1.7930227452284767e-01,1.7665532144373500e-01, 1.7401977008183875e-01,1.7139559563750595e-01,1.6878277480121151e-01, 1.6618128576448205e-01,1.6359110823236570e-01,1.6101222343751107e-01, 1.5844461415592431e-01,1.5588826472447920e-01,1.5334316106026283e-01, 1.5080929068184568e-01,1.4828664273257453e-01,1.4577520800599403e-01, 1.4327497897351341e-01,1.4078594981444470e-01,1.3830811644855071e-01, 1.3584147657125373e-01,1.3338602969166913e-01,1.3094177717364430e-01, 1.2850872227999952e-01,1.2608687022018586e-01,1.2367622820159654e-01, 1.2127680548479021e-01,1.1888861344290998e-01,1.1651166562561080e-01, 1.1414597782783835e-01,1.1179156816383801e-01,1.0944845714681163e-01, 1.0711666777468364e-01,1.0479622562248690e-01,1.0248715894193508e-01, 1.0018949876880981e-01,9.7903279038862284e-02,9.5628536713008819e-02, 9.3365311912690860e-02,9.1113648066373634e-02,8.8873592068275789e-02, 8.6645194450557961e-02,8.4428509570353374e-02,8.2223595813202863e-02, 8.0030515814663056e-02,7.7849336702096039e-02,7.5680130358927067e-02, 7.3522973713981268e-02,7.1377949058890375e-02,6.9245144397006769e-02, 6.7124653827788497e-02,6.5016577971242842e-02,6.2921024437758113e-02, 6.0838108349539864e-02,5.8767952920933758e-02,5.6710690106202902e-02, 5.4666461324888914e-02,5.2635418276792176e-02,5.0617723860947761e-02, 4.8613553215868521e-02,4.6623094901930368e-02,4.4646552251294443e-02, 4.2684144916474431e-02,4.0736110655940933e-02,3.8802707404526113e-02, 3.6884215688567284e-02,3.4980941461716084e-02,3.3093219458578522e-02, 3.1221417191920245e-02,2.9365939758133314e-02,2.7527235669603082e-02, 2.5705804008548896e-02,2.3902203305795882e-02,2.2117062707308864e-02, 2.0351096230044517e-02,1.8605121275724643e-02,1.6880083152543166e-02, 1.5177088307935325e-02,1.3497450601739880e-02,1.1842757857907888e-02, 1.0214971439701471e-02,8.6165827693987316e-03,7.0508754713732268e-03, 5.5224032992509968e-03,4.0379725933630305e-03,2.6090727461021627e-03, 1.2602859304985975e-03] ## Tables for exponential variates const ke = UInt64[0x000e290a13924be3,0x0000000000000000,0x0009beadebce18bf,0x000c377ac71f9e08, 0x000d4ddb99075857,0x000de893fb8ca23e,0x000e4a8e87c4328d,0x000e8dff16ae1cb9, 0x000ebf2deab58c59,0x000ee49a6e8b9638,0x000f0204efd64ee4,0x000f19bdb8ea3c1b, 0x000f2d458bbe5bd1,0x000f3da104b78236,0x000f4b86d784571f,0x000f577ad8a7784f, 0x000f61de83da32ab,0x000f6afb7843cce7,0x000f730a57372b44,0x000f7a37651b0e68, 0x000f80a5bb6eea52,0x000f867189d3cb5b,0x000f8bb1b4f8fbbd,0x000f9079062292b8, 0x000f94d70ca8d43a,0x000f98d8c7dcaa99,0x000f9c8928abe083,0x000f9ff175b734a6, 0x000fa319996bc47d,0x000fa6085f8e9d07,0x000fa8c3a62e1991,0x000fab5084e1f660, 0x000fadb36c84cccb,0x000faff041086846,0x000fb20a6ea22bb9,0x000fb404fb42cb3c, 0x000fb5e295158173,0x000fb7a59e99727a,0x000fb95038c8789d,0x000fbae44ba684eb, 0x000fbc638d822e60,0x000fbdcf89209ffa,0x000fbf29a303cfc5,0x000fc0731df1089c, 0x000fc1ad1ed6c8b1,0x000fc2d8b02b5c89,0x000fc3f6c4d92131,0x000fc5083ac9ba7d, 0x000fc60ddd1e9cd6,0x000fc7086622e825,0x000fc7f881009f0b,0x000fc8decb41ac70, 0x000fc9bbd623d7ec,0x000fca9027c5b26d,0x000fcb5c3c319c49,0x000fcc20864b4449, 0x000fccdd70a35d40,0x000fcd935e34bf80,0x000fce42ab0db8bd,0x000fceebace7ec01, 0x000fcf8eb3b0d0e7,0x000fd02c0a049b60,0x000fd0c3f59d199c,0x000fd156b7b5e27e, 0x000fd1e48d670341,0x000fd26daff73551,0x000fd2f2552684be,0x000fd372af7233c1, 0x000fd3eeee528f62,0x000fd4673e73543a,0x000fd4dbc9e72ff7,0x000fd54cb856dc2c, 0x000fd5ba2f2c4119,0x000fd62451ba02c2,0x000fd68b415fcff4,0x000fd6ef1dabc160, 0x000fd75004790eb6,0x000fd7ae120c583f,0x000fd809612dbd09,0x000fd8620b40effa, 0x000fd8b8285b78fd,0x000fd90bcf594b1d,0x000fd95d15efd425,0x000fd9ac10bfa70c, 0x000fd9f8d364df06,0x000fda437086566b,0x000fda8bf9e3c9fe,0x000fdad28062fed5, 0x000fdb17141bff2c,0x000fdb59c4648085,0x000fdb9a9fda83cc,0x000fdbd9b46e3ed4, 0x000fdc170f6b5d04,0x000fdc52bd81a3fb,0x000fdc8ccacd07ba,0x000fdcc542dd3902, 0x000fdcfc30bcb793,0x000fdd319ef77143,0x000fdd6597a0f60b,0x000fdd98245a48a2, 0x000fddc94e575271,0x000fddf91e64014f,0x000fde279ce914ca,0x000fde54d1f0a06a, 0x000fde80c52a47cf,0x000fdeab7def394e,0x000fded50345eb35,0x000fdefd5be59fa0, 0x000fdf248e39b26f,0x000fdf4aa064b4af,0x000fdf6f98435894,0x000fdf937b6f30ba, 0x000fdfb64f414571,0x000fdfd818d48262,0x000fdff8dd07fed8,0x000fe018a08122c4, 0x000fe03767adaa59,0x000fe05536c58a13,0x000fe07211ccb4c5,0x000fe08dfc94c532, 0x000fe0a8fabe8ca1,0x000fe0c30fbb87a5,0x000fe0dc3ecf3a5a,0x000fe0f48b107521, 0x000fe10bf76a82ef,0x000fe122869e41ff,0x000fe1383b4327e1,0x000fe14d17c83187, 0x000fe1611e74c023,0x000fe1745169635a,0x000fe186b2a09176,0x000fe19843ef4e07, 0x000fe1a90705bf63,0x000fe1b8fd6fb37c,0x000fe1c828951443,0x000fe1d689ba4bfd, 0x000fe1e4220099a4,0x000fe1f0f26655a0,0x000fe1fcfbc726d4,0x000fe2083edc2830, 0x000fe212bc3bfeb4,0x000fe21c745adfe3,0x000fe225678a8895,0x000fe22d95fa23f4, 0x000fe234ffb62282,0x000fe23ba4a800d9,0x000fe2418495fddc,0x000fe2469f22bffb, 0x000fe24af3cce90d,0x000fe24e81ee9858,0x000fe25148bcda19,0x000fe253474703fe, 0x000fe2547c75fdc6,0x000fe254e70b754f,0x000fe25485a0fd1a,0x000fe25356a71450, 0x000fe2515864173a,0x000fe24e88f316f1,0x000fe24ae64296fa,0x000fe2466e132f60, 0x000fe2411df611bd,0x000fe23af34b6f73,0x000fe233eb40bf41,0x000fe22c02cee01b, 0x000fe22336b81710,0x000fe2198385e5cc,0x000fe20ee586b707,0x000fe20358cb5dfb, 0x000fe1f6d92465b1,0x000fe1e9621f2c9e,0x000fe1daef02c8da,0x000fe1cb7accb0a6, 0x000fe1bb002d22c9,0x000fe1a9798349b8,0x000fe196e0d9140c,0x000fe1832fdebc44, 0x000fe16e5fe5f931,0x000fe15869dccfcf,0x000fe1414647fe78,0x000fe128ed3cf8b2, 0x000fe10f565b69cf,0x000fe0f478c633ab,0x000fe0d84b1bdd9e,0x000fe0bac36e6688, 0x000fe09bd73a6b5b,0x000fe07b7b5d920a,0x000fe059a40c26d2,0x000fe03644c5d7f8, 0x000fe011504979b2,0x000fdfeab887b95c,0x000fdfc26e94a447,0x000fdf986297e305, 0x000fdf6c83bb8663,0x000fdf3ec0193eed,0x000fdf0f04a5d30a,0x000fdedd3d1aa204, 0x000fdea953dcfc13,0x000fde7331e3100d,0x000fde3abe9626f2,0x000fddffdfb1dbd5, 0x000fddc2791ff351,0x000fdd826cd068c6,0x000fdd3f9a8d3856,0x000fdcf9dfc95b0c, 0x000fdcb1176a55fe,0x000fdc65198ba50b,0x000fdc15bb3b2daa,0x000fdbc2ce2dc4ae, 0x000fdb6c206aaaca,0x000fdb117becb4a1,0x000fdab2a6379bf0,0x000fda4f5fdfb4e9, 0x000fd9e76401f3a3,0x000fd97a67a9ce1f,0x000fd90819221429,0x000fd8901f2d4b02, 0x000fd812182170e1,0x000fd78d98e23cd3,0x000fd7022bb3f082,0x000fd66f4edf96b9, 0x000fd5d473200305,0x000fd530f9ccff94,0x000fd48432b7b351,0x000fd3cd59a8469e, 0x000fd30b9368f90a,0x000fd23dea45f500,0x000fd16349e2e04a,0x000fd07a7a3ef98a, 0x000fcf8219b5df05,0x000fce7895bcfcde,0x000fcd5c220ad5e2,0x000fcc2aadbc17dc, 0x000fcae1d5e81fbc,0x000fc97ed4e778f9,0x000fc7fe6d4d720e,0x000fc65ccf39c2fc, 0x000fc4957623cb03,0x000fc2a2fc826dc7,0x000fc07ee19b01cd,0x000fbe213c1cf493, 0x000fbb8051ac1566,0x000fb890078d120e,0x000fb5411a5b9a95,0x000fb18000547133, 0x000fad334827f1e2,0x000fa839276708b9,0x000fa263b32e37ed,0x000f9b72d1c52cd1, 0x000f930a1a281a05,0x000f889f023d820a,0x000f7b577d2be5f3,0x000f69c650c40a8f, 0x000f51530f0916d8,0x000f2cb0e3c5933e,0x000eeefb15d605d8,0x000e6da6ecf27460] const we = [1.9311480126418366e-15,1.4178028487910829e-17,2.3278824993382448e-17, 3.0487830247064320e-17,3.6665697714474878e-17,4.2179302189289733e-17, 4.7222561556862764e-17,5.1911915446217879e-17,5.6323471083955047e-17, 6.0510082606427647e-17,6.4510165096727506e-17,6.8352646803700541e-17, 7.2059939574689050e-17,7.5649815537392981e-17,7.9136643961951065e-17, 8.2532235563518929e-17,8.5846436168850513e-17,8.9087554865647428e-17, 9.2262679629663719e-17,9.5377914505292719e-17,9.8438560874559257e-17, 1.0144925809006294e-16,1.0441409405585343e-16,1.0733669323436384e-16, 1.1022028745670189e-16,1.1306777346479334e-16,1.1588176009705533e-16, 1.1866460730417886e-16,1.2141845865694359e-16,1.2414526862326387e-16, 1.2684682560606153e-16,1.2952477151912284e-16,1.3218061851538810e-16, 1.3481576335745444e-16,1.3743149982367625e-16,1.4002902946807859e-16, 1.4260947099321287e-16,1.4517386844829297e-16,1.4772319842763584e-16, 1.5025837641447456e-16,1.5278026239101652e-16,1.5528966581595696e-16, 1.5778735005459581e-16,1.6027403633350909e-16,1.6275040728083524e-16, 1.6521711010420076e-16,1.6767475945078279e-16,1.7012393998770646e-16, 1.7256520873568226e-16,1.7499909718432365e-16,1.7742611321380505e-16, 1.7984674284430714e-16,1.8226145183195818e-16,1.8467068712763576e-16, 1.8707487821298258e-16,1.8947443832625899e-16,1.9186976558915995e-16, 1.9426124404443042e-16,1.9664924461299023e-16,1.9903412597830144e-16, 2.0141623540485899e-16,2.0379590949693882e-16,2.0617347490308439e-16, 2.0854924897123771e-16,2.1092354035891528e-16,2.1329664960238294e-16, 2.1566886964838970e-16,2.1804048635167009e-16,2.2041177894111562e-16, 2.2278302045723950e-16,2.2515447816331350e-16,2.2752641393233694e-16, 2.2989908461180186e-16,2.3227274236804366e-16,2.3464763501180916e-16, 2.3702400630653389e-16,2.3940209626069303e-16,2.4178214140547710e-16, 2.4416437505894123e-16,2.4654902757768304e-16,2.4893632659702250e-16, 2.5132649726057970e-16,2.5371976244007951e-16,2.5611634294614988e-16, 2.5851645773082391e-16,2.6092032408240577e-16,2.6332815781331452e-16, 2.6574017344147618e-16,2.6815658436579989e-16,2.7057760303623509e-16, 2.7300344111887955e-16,2.7543430965657619e-16,2.7787041922541278e-16, 2.8031198008751431e-16,2.8275920234049704e-16,2.8521229606393309e-16, 2.8767147146315804e-16,2.9013693901073754e-16,2.9260890958589514e-16, 2.9508759461219033e-16,2.9757320619372521e-16,3.0006595725014739e-16, 3.0256606165070789e-16,3.0507373434762511e-16,3.0758919150899939e-16, 3.1011265065151543e-16,3.1264433077316750e-16,3.1518445248623523e-16, 3.1773323815073683e-16,3.2029091200858335e-16,3.2285770031865573e-16, 3.2543383149302610e-16,3.2801953623454359e-16,3.3061504767600738e-16, 3.3322060152114841e-16,3.3583643618764577e-16,3.3846279295240445e-16, 3.4109991609932597e-16,3.4374805306980633e-16,3.4640745461620167e-16, 3.4907837495850680e-16,3.5176107194449828e-16,3.5445580721360130e-16, 3.5716284636474652e-16,3.5988245912849274e-16,3.6261491954370031e-16, 3.6536050613905045e-16,3.6811950211971757e-16,3.7089219555951389e-16, 3.7367887959883854e-16,3.7647985264877841e-16,3.7929541860172334e-16, 3.8212588704887531e-16,3.8497157350504876e-16,3.8783279964117988e-16, 3.9070989352498183e-16,3.9360318987020748e-16,3.9651303029500381e-16, 3.9943976358986842e-16,4.0238374599574693e-16,4.0534534149283966e-16, 4.0832492210071775e-16,4.1132286819038357e-16,4.1433956880894741e-16, 4.1737542201763194e-16,4.2043083524385856e-16,4.2350622564821518e-16, 4.2660202050715582e-16,4.2971865761233266e-16,4.3285658568752094e-16, 4.3601626482415681e-16,4.3919816693657415e-16,4.4240277623809919e-16, 4.4563058973923611e-16,4.4888211776926172e-16,4.5215788452263475e-16, 4.5545842863172421e-16,4.5878430376746227e-16,4.6213607926964266e-16, 4.6551434080870692e-16,4.6891969108099157e-16,4.7235275053955480e-16, 4.7581415816285534e-16,4.7930457226372470e-16,4.8282467134125866e-16, 4.8637515497845119e-16,4.8995674478861404e-16,4.9357018541385775e-16, 4.9721624557917034e-16,5.0089571920591141e-16,5.0460942658884340e-16, 5.0835821564116245e-16,5.1214296321235415e-16,5.1596457648410618e-16, 5.1982399444994938e-16,5.2372218948478484e-16,5.2766016901098856e-16, 5.3163897726836902e-16,5.3565969719590503e-16,5.3972345243389779e-16, 5.4383140945596370e-16,5.4798477984116296e-16,5.5218482269752343e-16, 5.5643284724928722e-16,5.6073021560139669e-16,5.6507834569605064e-16, 5.6947871447763482e-16,5.7393286128396354e-16,5.7844239148359912e-16, 5.8300898038105864e-16,5.8763437741400573e-16,5.9232041066909314e-16, 5.9706899174600906e-16,6.0188212100252363e-16,6.0676189321700068e-16, 6.1171050370897217e-16,6.1673025496306200e-16,6.2182356380685327e-16, 6.2699296919933262e-16,6.3224114069342115e-16,6.3757088764394262e-16, 6.4298516924135947e-16,6.4848710546189033e-16,6.5407998903644809e-16, 6.5976729855445663e-16,6.6555271283433428e-16,6.7144012671064882e-16, 6.7743366840910103e-16,6.8353771870512740e-16,6.8975693209068478e-16, 6.9609626020748846e-16,7.0256097784459588e-16,7.0915671184495837e-16, 7.1588947332085531e-16,7.2276569364381212e-16,7.2979226475290851e-16, 7.3697658441912426e-16,7.4432660721604146e-16,7.5185090208325131e-16, 7.5955871753377488e-16,7.6746005575784274e-16,7.7556575712157906e-16, 7.8388759686228577e-16,7.9243839615735500e-16,8.0123215021130834e-16, 8.1028417659131464e-16,8.1961128778061250e-16,8.2923199285818092e-16, 8.3916673441467979e-16,8.4943816836487701e-16,8.6007149633349414e-16, 8.7109486293879040e-16,8.8253983380721398e-16,8.9444197485198646e-16, 9.0684155971316690e-16,9.1978444098118649e-16,9.3332313294229516e-16, 9.4751817065249841e-16,9.6243983456584759e-16,9.7817036547844198e-16, 9.9480684723838795e-16,1.0124650144288319e-15,1.0312843657756166e-15, 1.0514351604044550e-15,1.0731281954224043e-15,1.0966288068517408e-15, 1.1222774909350319e-15,1.1505212963006663e-15,1.1819635283304206e-15, 1.2174462832361815e-15,1.2581958069755114e-15,1.3060984107128082e-15, 1.3642786158057857e-15,1.4384889932178723e-15,1.5412190700064194e-15, 1.7091034077168055e-15] const fe = [1.0000000000000000e+00,9.3814368086217470e-01,9.0046992992574648e-01, 8.7170433238120359e-01,8.4778550062398961e-01,8.2699329664305032e-01, 8.0842165152300838e-01,7.9152763697249562e-01,7.7595685204011555e-01, 7.6146338884989628e-01,7.4786862198519510e-01,7.3503809243142348e-01, 7.2286765959357202e-01,7.1127476080507601e-01,7.0019265508278816e-01, 6.8956649611707799e-01,6.7935057226476536e-01,6.6950631673192473e-01, 6.6000084107899970e-01,6.5080583341457110e-01,6.4189671642726609e-01, 6.3325199421436607e-01,6.2485273870366598e-01,6.1668218091520766e-01, 6.0872538207962201e-01,6.0096896636523223e-01,5.9340090169173343e-01, 5.8601031847726803e-01,5.7878735860284503e-01,5.7172304866482582e-01, 5.6480919291240017e-01,5.5803828226258745e-01,5.5140341654064129e-01, 5.4489823767243961e-01,5.3851687200286191e-01,5.3225388026304332e-01, 5.2610421398361973e-01,5.2006317736823360e-01,5.1412639381474856e-01, 5.0828977641064288e-01,5.0254950184134772e-01,4.9690198724154955e-01, 4.9134386959403253e-01,4.8587198734188491e-01,4.8048336393045421e-01, 4.7517519303737737e-01,4.6994482528395998e-01,4.6478975625042618e-01, 4.5970761564213769e-01,4.5469615747461550e-01,4.4975325116275500e-01, 4.4487687341454851e-01,4.4006510084235390e-01,4.3531610321563657e-01, 4.3062813728845883e-01,4.2599954114303434e-01,4.2142872899761658e-01, 4.1691418643300288e-01,4.1245446599716118e-01,4.0804818315203240e-01, 4.0369401253053028e-01,3.9939068447523107e-01,3.9513698183329016e-01, 3.9093173698479711e-01,3.8677382908413765e-01,3.8266218149600983e-01, 3.7859575940958079e-01,3.7457356761590216e-01,3.7059464843514600e-01, 3.6665807978151416e-01,3.6276297335481777e-01,3.5890847294874978e-01, 3.5509375286678746e-01,3.5131801643748334e-01,3.4758049462163698e-01, 3.4388044470450241e-01,3.4021714906678002e-01,3.3658991402867761e-01, 3.3299806876180899e-01,3.2944096426413633e-01,3.2591797239355619e-01, 3.2242848495608917e-01,3.1897191284495724e-01,3.1554768522712895e-01, 3.1215524877417955e-01,3.0879406693456019e-01,3.0546361924459026e-01, 3.0216340067569353e-01,2.9889292101558179e-01,2.9565170428126120e-01, 2.9243928816189257e-01,2.8925522348967775e-01,2.8609907373707683e-01, 2.8297041453878075e-01,2.7986883323697292e-01,2.7679392844851736e-01, 2.7374530965280297e-01,2.7072259679906002e-01,2.6772541993204479e-01, 2.6475341883506220e-01,2.6180624268936298e-01,2.5888354974901623e-01, 2.5598500703041538e-01,2.5311029001562946e-01,2.5025908236886230e-01, 2.4743107566532763e-01,2.4462596913189211e-01,2.4184346939887721e-01, 2.3908329026244918e-01,2.3634515245705964e-01,2.3362878343743335e-01, 2.3093391716962741e-01,2.2826029393071670e-01,2.2560766011668407e-01, 2.2297576805812019e-01,2.2036437584335949e-01,2.1777324714870053e-01, 2.1520215107537868e-01,2.1265086199297828e-01,2.1011915938898826e-01, 2.0760682772422204e-01,2.0511365629383771e-01,2.0263943909370902e-01, 2.0018397469191127e-01,1.9774706610509887e-01,1.9532852067956322e-01, 1.9292814997677135e-01,1.9054576966319539e-01,1.8818119940425432e-01, 1.8583426276219711e-01,1.8350478709776746e-01,1.8119260347549629e-01, 1.7889754657247831e-01,1.7661945459049488e-01,1.7435816917135349e-01, 1.7211353531532006e-01,1.6988540130252766e-01,1.6767361861725019e-01, 1.6547804187493600e-01,1.6329852875190182e-01,1.6113493991759203e-01, 1.5898713896931421e-01,1.5685499236936523e-01,1.5473836938446808e-01, 1.5263714202744286e-01,1.5055118500103989e-01,1.4848037564386679e-01, 1.4642459387834494e-01,1.4438372216063478e-01,1.4235764543247220e-01, 1.4034625107486245e-01,1.3834942886358020e-01,1.3636707092642886e-01, 1.3439907170221363e-01,1.3244532790138752e-01,1.3050573846833077e-01, 1.2858020454522817e-01,1.2666862943751067e-01,1.2477091858083096e-01, 1.2288697950954514e-01,1.2101672182667483e-01,1.1916005717532768e-01, 1.1731689921155557e-01,1.1548716357863353e-01,1.1367076788274431e-01, 1.1186763167005630e-01,1.1007767640518538e-01,1.0830082545103380e-01, 1.0653700405000166e-01,1.0478613930657017e-01,1.0304816017125772e-01, 1.0132299742595363e-01,9.9610583670637132e-02,9.7910853311492199e-02, 9.6223742550432798e-02,9.4549189376055859e-02,9.2887133556043541e-02, 9.1237516631040155e-02,8.9600281910032858e-02,8.7975374467270218e-02, 8.6362741140756913e-02,8.4762330532368119e-02,8.3174093009632383e-02, 8.1597980709237419e-02,8.0033947542319905e-02,7.8481949201606421e-02, 7.6941943170480503e-02,7.5413888734058410e-02,7.3897746992364746e-02, 7.2393480875708738e-02,7.0901055162371829e-02,6.9420436498728755e-02, 6.7951593421936601e-02,6.6494496385339774e-02,6.5049117786753749e-02, 6.3615431999807334e-02,6.2193415408540995e-02,6.0783046445479633e-02, 5.9384305633420266e-02,5.7997175631200659e-02,5.6621641283742877e-02, 5.5257689676697037e-02,5.3905310196046087e-02,5.2564494593071692e-02, 5.1235237055126281e-02,4.9917534282706372e-02,4.8611385573379497e-02, 4.7316792913181548e-02,4.6033761076175170e-02,4.4762297732943282e-02, 4.3502413568888183e-02,4.2254122413316234e-02,4.1017441380414819e-02, 3.9792391023374125e-02,3.8578995503074857e-02,3.7377282772959361e-02, 3.6187284781931423e-02,3.5009037697397410e-02,3.3842582150874330e-02, 3.2687963508959535e-02,3.1545232172893609e-02,3.0414443910466604e-02, 2.9295660224637393e-02,2.8188948763978636e-02,2.7094383780955800e-02, 2.6012046645134217e-02,2.4942026419731783e-02,2.3884420511558171e-02, 2.2839335406385240e-02,2.1806887504283581e-02,2.0787204072578117e-02, 1.9780424338009743e-02,1.8786700744696030e-02,1.7806200410911362e-02, 1.6839106826039948e-02,1.5885621839973163e-02,1.4945968011691148e-02, 1.4020391403181938e-02,1.3109164931254991e-02,1.2212592426255381e-02, 1.1331013597834597e-02,1.0464810181029979e-02,9.6144136425022099e-03, 8.7803149858089753e-03,7.9630774380170400e-03,7.1633531836349839e-03, 6.3819059373191791e-03,5.6196422072054830e-03,4.8776559835423923e-03, 4.1572951208337953e-03,3.4602647778369040e-03,2.7887987935740761e-03, 2.1459677437189063e-03,1.5362997803015724e-03,9.6726928232717454e-04, 4.5413435384149677e-04] ## Constants const ziggurat_nor_r = 3.6541528853610087963519472518 const ziggurat_nor_inv_r = inv(ziggurat_nor_r) const ziggurat_exp_r = 7.6971174701310497140446280481 n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/misc.jl['������# This file is a part of Julia. License is MIT: https://julialang.org/license ## rand!(::BitArray) && bitrand function rand!(rng::AbstractRNG, B::BitArray, ::SamplerType{Bool}) isempty(B) && return B Bc = B.chunks rand!(rng, Bc) Bc[end] &= Base._msk_end(B) return B end """ bitrand([rng=default_rng()], [dims...]) Generate a `BitArray` of random boolean values. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> bitrand(rng, 10) 10-element BitVector: 0 0 0 0 1 0 0 0 1 1 ``` """ bitrand(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(undef, dims)) bitrand(r::AbstractRNG, dims::Integer...) = rand!(r, BitArray(undef, convert(Dims, dims))) bitrand(dims::Dims) = rand!(BitArray(undef, dims)) bitrand(dims::Integer...) = rand!(BitArray(undef, convert(Dims, dims))) ## randstring (often useful for temporary filenames/dirnames) """ randstring([rng=default_rng()], [chars], [len=8]) Create a random string of length `len`, consisting of characters from `chars`, which defaults to the set of upper- and lower-case letters and the digits 0-9. The optional `rng` argument specifies a random number generator, see [Random Numbers](@ref). # Examples ```jldoctest julia> Random.seed!(3); randstring() "Lxz5hUwn" julia> randstring(MersenneTwister(3), 'a':'z', 6) "ocucay" julia> randstring("ACGT") "TGCTCCTC" ``` !!! note `chars` can be any collection of characters, of type `Char` or `UInt8` (more efficient), provided [`rand`](@ref) can randomly pick characters from it. """ function randstring end let b = UInt8['0':'9';'A':'Z';'a':'z'] global randstring function randstring(r::AbstractRNG, chars=b, n::Integer=8) T = eltype(chars) if T === UInt8 str = Base._string_n(n) GC.@preserve str rand!(r, UnsafeView(pointer(str), n), chars) return str else v = Vector{T}(undef, n) rand!(r, v, chars) return String(v) end end randstring(r::AbstractRNG, n::Integer) = randstring(r, b, n) randstring(chars=b, n::Integer=8) = randstring(default_rng(), chars, n) randstring(n::Integer) = randstring(default_rng(), b, n) end ## randsubseq & randsubseq! # Fill S (resized as needed) with a random subsequence of A, where # each element of A is included in S with independent probability p. # (Note that this is different from the problem of finding a random # size-m subset of A where m is fixed!) function randsubseq!(r::AbstractRNG, S::AbstractArray, A::AbstractArray, p::Real) require_one_based_indexing(S, A) 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) n = length(A) p == 1 && return copyto!(resize!(S, n), A) empty!(S) p == 0 && return S nexpected = p * length(A) sizehint!(S, round(Int,nexpected + 5*sqrt(nexpected))) if p > 0.15 # empirical threshold for trivial O(n) algorithm to be better for i = 1:n rand(r) <= p && push!(S, A[i]) end else # Skip through A, in order, from each element i to the next element i+s # included in S. The probability that the next included element is # s==k (k > 0) is (1-p)^(k-1) * p, and hence the probability (CDF) that # s is in {1,...,k} is 1-(1-p)^k = F(k). Thus, we can draw the skip s # from this probability distribution via the discrete inverse-transform # method: s = ceil(F^{-1}(u)) where u = rand(), which is simply # s = ceil(log(rand()) / log1p(-p)). # -log(rand()) is an exponential variate, so can use randexp(). L = -1 / log1p(-p) # L > 0 i = 0 while true s = randexp(r) * L s >= n - i && return S # compare before ceil to avoid overflow push!(S, A[i += ceil(Int,s)]) end # [This algorithm is similar in spirit to, but much simpler than, # the one by Vitter for a related problem in "Faster methods for # random sampling," Comm. ACM Magazine 7, 703-718 (1984).] end return S end """ randsubseq!([rng=default_rng(),] S, A, p) Like [`randsubseq`](@ref), but the results are stored in `S` (which is resized as needed). # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> S = Int64[]; julia> randsubseq!(rng, S, 1:8, 0.3) 2-element Vector{Int64}: 7 8 julia> S 2-element Vector{Int64}: 7 8 ``` """ randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(default_rng(), S, A, p) randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) """ randsubseq([rng=default_rng(),] A, p) -> Vector Return a vector consisting of a random subsequence of the given array `A`, where each element of `A` is included (in order) with independent probability `p`. (Complexity is linear in `p*length(A)`, so this function is efficient even if `p` is small and `A` is large.) Technically, this process is known as "Bernoulli sampling" of `A`. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> randsubseq(rng, 1:8, 0.3) 2-element Vector{Int64}: 7 8 ``` """ randsubseq(A::AbstractArray, p::Real) = randsubseq(default_rng(), A, p) ## rand Less Than Masked 52 bits (helper function) "Return a sampler generating a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." ltm52(n::Int, mask::Int=nextpow(2, n)-1) = LessThan(n-1, Masked(mask, UInt52Raw(Int))) ## shuffle & shuffle! """ shuffle!([rng=default_rng(),] v::AbstractArray) In-place version of [`shuffle`](@ref): randomly permute `v` in-place, optionally supplying the random-number generator `rng`. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> shuffle!(rng, Vector(1:16)) 16-element Vector{Int64}: 2 15 5 14 1 9 10 6 11 3 16 7 4 12 8 13 ``` """ function shuffle!(r::AbstractRNG, a::AbstractArray) require_one_based_indexing(a) n = length(a) n <= 1 && return a # nextpow below won't work with n == 0 @assert n <= Int64(2)^52 mask = nextpow(2, n) - 1 for i = n:-1:2 (mask >> 1) == i && (mask >>= 1) j = 1 + rand(r, ltm52(i, mask)) a[i], a[j] = a[j], a[i] end return a end shuffle!(a::AbstractArray) = shuffle!(default_rng(), a) """ shuffle([rng=default_rng(),] v::AbstractArray) Return a randomly permuted copy of `v`. The optional `rng` argument specifies a random number generator (see [Random Numbers](@ref)). To permute `v` in-place, see [`shuffle!`](@ref). To obtain randomly permuted indices, see [`randperm`](@ref). # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> shuffle(rng, Vector(1:10)) 10-element Vector{Int64}: 6 1 10 2 3 9 5 7 4 8 ``` """ shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) shuffle(a::AbstractArray) = shuffle(default_rng(), a) shuffle(r::AbstractRNG, a::Base.OneTo) = randperm(r, last(a)) ## randperm & randperm! """ randperm([rng=default_rng(),] n::Integer) Construct a random permutation of length `n`. The optional `rng` argument specifies a random number generator (see [Random Numbers](@ref)). The element type of the result is the same as the type of `n`. To randomly permute an arbitrary vector, see [`shuffle`](@ref) or [`shuffle!`](@ref). !!! compat "Julia 1.1" In Julia 1.1 `randperm` returns a vector `v` with `eltype(v) == typeof(n)` while in Julia 1.0 `eltype(v) == Int`. # Examples ```jldoctest julia> randperm(MersenneTwister(1234), 4) 4-element Vector{Int64}: 2 1 4 3 ``` """ randperm(r::AbstractRNG, n::T) where {T <: Integer} = randperm!(r, Vector{T}(undef, n)) randperm(n::Integer) = randperm(default_rng(), n) """ randperm!([rng=default_rng(),] A::Array{<:Integer}) Construct in `A` a random permutation of length `length(A)`. The optional `rng` argument specifies a random number generator (see [Random Numbers](@ref)). To randomly permute an arbitrary vector, see [`shuffle`](@ref) or [`shuffle!`](@ref). # Examples ```jldoctest julia> randperm!(MersenneTwister(1234), Vector{Int}(undef, 4)) 4-element Vector{Int64}: 2 1 4 3 ``` """ function randperm!(r::AbstractRNG, a::Array{<:Integer}) n = length(a) @assert n <= Int64(2)^52 n == 0 && return a a[1] = 1 mask = 3 @inbounds for i = 2:n j = 1 + rand(r, ltm52(i, mask)) if i != j # a[i] is undef (and could be #undef) a[i] = a[j] end a[j] = i i == 1+mask && (mask = 2mask + 1) end return a end randperm!(a::Array{<:Integer}) = randperm!(default_rng(), a) ## randcycle & randcycle! """ randcycle([rng=default_rng(),] n::Integer) Construct a random cyclic permutation of length `n`. The optional `rng` argument specifies a random number generator, see [Random Numbers](@ref). The element type of the result is the same as the type of `n`. !!! compat "Julia 1.1" In Julia 1.1 `randcycle` returns a vector `v` with `eltype(v) == typeof(n)` while in Julia 1.0 `eltype(v) == Int`. # Examples ```jldoctest julia> randcycle(MersenneTwister(1234), 6) 6-element Vector{Int64}: 3 5 4 6 1 2 ``` """ randcycle(r::AbstractRNG, n::T) where {T <: Integer} = randcycle!(r, Vector{T}(undef, n)) randcycle(n::Integer) = randcycle(default_rng(), n) """ randcycle!([rng=default_rng(),] A::Array{<:Integer}) Construct in `A` a random cyclic permutation of length `length(A)`. The optional `rng` argument specifies a random number generator, see [Random Numbers](@ref). # Examples ```jldoctest julia> randcycle!(MersenneTwister(1234), Vector{Int}(undef, 6)) 6-element Vector{Int64}: 3 5 4 6 1 2 ``` """ function randcycle!(r::AbstractRNG, a::Array{<:Integer}) n = length(a) n == 0 && return a @assert n <= Int64(2)^52 a[1] = 1 mask = 3 @inbounds for i = 2:n j = 1 + rand(r, ltm52(i-1, mask)) a[i] = a[j] a[j] = i i == 1+mask && (mask = 2mask + 1) end return a end randcycle!(a::Array{<:Integer}) = randcycle!(default_rng(), a) u���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Random/src/XoshiroSimd.jlz+������# This file is a part of Julia. License is MIT: https://julialang.org/license module XoshiroSimd # Getting the xoroshiro RNG to reliably vectorize is somewhat of a hassle without Simd.jl. import ..Random: TaskLocalRNG, rand, rand!, Xoshiro, CloseOpen01, UnsafeView, SamplerType, SamplerTrivial using Base: BitInteger_types using Base.Libc: memcpy using Core.Intrinsics: llvmcall # Vector-width. Influences random stream. xoshiroWidth() = Val(8) # Simd threshold. Influences random stream. simdThreshold(::Type{T}) where T = 64 simdThreshold(::Type{Bool}) = 640 @inline _rotl45(x::UInt64) = (x<<45)|(x>>19) @inline _shl17(x::UInt64) = x<<17 @inline _rotl23(x::UInt64) = (x<<23)|(x>>41) @inline _plus(x::UInt64,y::UInt64) = x+y @inline _xor(x::UInt64,y::UInt64) = xor(x,y) @inline _and(x::UInt64, y::UInt64) = x & y @inline _or(x::UInt64, y::UInt64) = x | y @inline _lshr(x, y::Int32) = _lshr(x, y % Int64) @inline _lshr(x::UInt64, y::Int64) = llvmcall(""" %res = lshr i64 %0, %1 ret i64 %res """, UInt64, Tuple{UInt64, Int64}, x, y) @inline _bits2float(x::UInt64, ::Type{Float64}) = reinterpret(UInt64, Float64(x >>> 11) * 0x1.0p-53) @inline function _bits2float(x::UInt64, ::Type{Float32}) #= # this implementation uses more high bits, but is harder to vectorize x = x >>> 16 # discard low 16 bits u = Float32(x >>> 24) * Float32(0x1.0p-24) l = Float32(x & 0x00ffffff) * Float32(0x1.0p-24) =# ui = (x>>>32) % UInt32 li = x % UInt32 u = Float32(ui >>> 8) * Float32(0x1.0p-24) l = Float32(li >>> 8) * Float32(0x1.0p-24) (UInt64(reinterpret(UInt32, u)) << 32) | UInt64(reinterpret(UInt32, l)) end # required operations. These could be written more concisely with `ntuple`, but the compiler # sometimes refuses to properly vectorize. for N in [4,8,16] let code, s, fshl = "llvm.fshl.v$(N)i64", VT = :(NTuple{$N, VecElement{UInt64}}) s = ntuple(_->VecElement(UInt64(45)), N) @eval @inline _rotl45(x::$VT) = ccall($fshl, llvmcall, $VT, ($VT, $VT, $VT), x, x, $s) s = ntuple(_->VecElement(UInt64(23)), N) @eval @inline _rotl23(x::$VT) = ccall($fshl, llvmcall, $VT, ($VT, $VT, $VT), x, x, $s) code = """ %lshiftOp = shufflevector <1 x i64> <i64 17>, <1 x i64> undef, <$N x i32> zeroinitializer %res = shl <$N x i64> %0, %lshiftOp ret <$N x i64> %res """ @eval @inline _shl17(x::$VT) = llvmcall($code, $VT, Tuple{$VT}, x) code = """ %res = add <$N x i64> %1, %0 ret <$N x i64> %res """ @eval @inline _plus(x::$VT, y::$VT) = llvmcall($code, $VT, Tuple{$VT, $VT}, x, y) code = """ %res = xor <$N x i64> %1, %0 ret <$N x i64> %res """ @eval @inline _xor(x::$VT, y::$VT) = llvmcall($code, $VT, Tuple{$VT, $VT}, x, y) code = """ %res = and <$N x i64> %1, %0 ret <$N x i64> %res """ @eval @inline _and(x::$VT, y::$VT) = llvmcall($code, $VT, Tuple{$VT, $VT}, x, y) code = """ %res = or <$N x i64> %1, %0 ret <$N x i64> %res """ @eval @inline _or(x::$VT, y::$VT) = llvmcall($code, $VT, Tuple{$VT, $VT}, x, y) code = """ %tmp = insertelement <1 x i64> undef, i64 %1, i32 0 %shift = shufflevector <1 x i64> %tmp, <1 x i64> %tmp, <$N x i32> zeroinitializer %res = lshr <$N x i64> %0, %shift ret <$N x i64> %res """ @eval @inline _lshr(x::$VT, y::Int64) = llvmcall($code, $VT, Tuple{$VT, Int64}, x, y) code = """ %shiftamt = shufflevector <1 x i64> <i64 11>, <1 x i64> undef, <$N x i32> zeroinitializer %sh = lshr <$N x i64> %0, %shiftamt %f = uitofp <$N x i64> %sh to <$N x double> %scale = shufflevector <1 x double> <double 0x3ca0000000000000>, <1 x double> undef, <$N x i32> zeroinitializer %m = fmul <$N x double> %f, %scale %i = bitcast <$N x double> %m to <$N x i64> ret <$N x i64> %i """ @eval @inline _bits2float(x::$VT, ::Type{Float64}) = llvmcall($code, $VT, Tuple{$VT}, x) code = """ %as32 = bitcast <$N x i64> %0 to <$(2N) x i32> %shiftamt = shufflevector <1 x i32> <i32 8>, <1 x i32> undef, <$(2N) x i32> zeroinitializer %sh = lshr <$(2N) x i32> %as32, %shiftamt %f = uitofp <$(2N) x i32> %sh to <$(2N) x float> %scale = shufflevector <1 x float> <float 0x3e70000000000000>, <1 x float> undef, <$(2N) x i32> zeroinitializer %m = fmul <$(2N) x float> %f, %scale %i = bitcast <$(2N) x float> %m to <$N x i64> ret <$N x i64> %i """ @eval @inline _bits2float(x::$VT, ::Type{Float32}) = llvmcall($code, $VT, Tuple{$VT}, x) end end function forkRand(rng::Union{TaskLocalRNG, Xoshiro}, ::Val{N}) where N # constants have nothing up their sleeve. For more discussion, cf rng_split in task.c # 0x02011ce34bce797f == hash(UInt(1))|0x01 # 0x5a94851fb48a6e05 == hash(UInt(2))|0x01 # 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 # 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 s0 = ntuple(i->VecElement(0x02011ce34bce797f * rand(rng, UInt64)), Val(N)) s1 = ntuple(i->VecElement(0x5a94851fb48a6e05 * rand(rng, UInt64)), Val(N)) s2 = ntuple(i->VecElement(0x3688cf5d48899fa7 * rand(rng, UInt64)), Val(N)) s3 = ntuple(i->VecElement(0x867b4bb4c42e5661 * rand(rng, UInt64)), Val(N)) (s0, s1, s2, s3) end _id(x, T) = x @inline function xoshiro_bulk(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, T::Union{Type{UInt8}, Type{Bool}, Type{Float32}, Type{Float64}}, ::Val{N}, f::F = _id) where {N, F} if len >= simdThreshold(T) written = xoshiro_bulk_simd(rng, dst, len, T, Val(N), f) len -= written dst += written end if len != 0 xoshiro_bulk_nosimd(rng, dst, len, T, f) end nothing end @noinline function xoshiro_bulk_nosimd(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, ::Type{T}, f::F) where {T, F} if rng isa TaskLocalRNG task = current_task() s0, s1, s2, s3 = task.rngState0, task.rngState1, task.rngState2, task.rngState3 else (; s0, s1, s2, s3) = rng::Xoshiro end i = 0 while i+8 <= len res = _plus(_rotl23(_plus(s0,s3)),s0) unsafe_store!(reinterpret(Ptr{UInt64}, dst + i), f(res, T)) t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) i += 8 end if i < len res = _plus(_rotl23(_plus(s0,s3)),s0) t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) ref = Ref(f(res, T)) # TODO: This may make the random-stream dependent on system endianness GC.@preserve ref memcpy(dst+i, Base.unsafe_convert(Ptr{Cvoid}, ref), len-i) end if rng isa TaskLocalRNG task.rngState0, task.rngState1, task.rngState2, task.rngState3 = s0, s1, s2, s3 else rng.s0, rng.s1, rng.s2, rng.s3 = s0, s1, s2, s3 end nothing end @noinline function xoshiro_bulk_nosimd(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, ::Type{Bool}, f) if rng isa TaskLocalRNG task = current_task() s0, s1, s2, s3 = task.rngState0, task.rngState1, task.rngState2, task.rngState3 else (; s0, s1, s2, s3) = rng::Xoshiro end i = 0 while i+8 <= len res = _plus(_rotl23(_plus(s0,s3)),s0) shift = 0 while i+8 <= len && shift < 8 resLoc = _and(_lshr(res, shift), 0x0101010101010101) unsafe_store!(reinterpret(Ptr{UInt64}, dst + i), resLoc) i += 8 shift += 1 end t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) end if i < len # we may overgenerate some bytes here, if len mod 64 <= 56 and len mod 8 != 0 res = _plus(_rotl23(_plus(s0,s3)),s0) resLoc = _and(res, 0x0101010101010101) ref = Ref(resLoc) GC.@preserve ref memcpy(dst+i, Base.unsafe_convert(Ptr{Cvoid}, ref), len-i) t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) end if rng isa TaskLocalRNG task.rngState0, task.rngState1, task.rngState2, task.rngState3 = s0, s1, s2, s3 else rng.s0, rng.s1, rng.s2, rng.s3 = s0, s1, s2, s3 end nothing end @noinline function xoshiro_bulk_simd(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, ::Type{T}, ::Val{N}, f::F) where {T,N,F} s0, s1, s2, s3 = forkRand(rng, Val(N)) i = 0 while i + 8*N <= len res = _plus(_rotl23(_plus(s0,s3)),s0) t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) unsafe_store!(reinterpret(Ptr{NTuple{N,VecElement{UInt64}}}, dst + i), f(res, T)) i += 8*N end return i end @noinline function xoshiro_bulk_simd(rng::Union{TaskLocalRNG, Xoshiro}, dst::Ptr{UInt8}, len::Int, ::Type{Bool}, ::Val{N}, f) where {N} s0, s1, s2, s3 = forkRand(rng, Val(N)) msk = ntuple(i->VecElement(0x0101010101010101), Val(N)) i = 0 while i + 64*N <= len res = _plus(_rotl23(_plus(s0,s3)),s0) t = _shl17(s1) s2 = _xor(s2, s0) s3 = _xor(s3, s1) s1 = _xor(s1, s2) s0 = _xor(s0, s3) s2 = _xor(s2, t) s3 = _rotl45(s3) for k=0:7 tmp = _lshr(res, k) toWrite = _and(tmp, msk) unsafe_store!(reinterpret(Ptr{NTuple{N,VecElement{UInt64}}}, dst + i + k*N*8), toWrite) end i += 64*N end return i end function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Float32}, ::SamplerTrivial{CloseOpen01{Float32}}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*4, Float32, xoshiroWidth(), _bits2float) dst end function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Float64}, ::SamplerTrivial{CloseOpen01{Float64}}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*8, Float64, xoshiroWidth(), _bits2float) dst end for T in BitInteger_types @eval function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Union{Array{$T}, UnsafeView{$T}}, ::SamplerType{$T}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst)*sizeof($T), UInt8, xoshiroWidth()) dst end end function rand!(rng::Union{TaskLocalRNG, Xoshiro}, dst::Array{Bool}, ::SamplerType{Bool}) GC.@preserve dst xoshiro_bulk(rng, convert(Ptr{UInt8}, pointer(dst)), length(dst), Bool, xoshiroWidth()) dst end end # module j���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Tar/src/Tar.jlจM������module Tar using ArgTools const true_predicate = _ -> true # 2 MiB to take advantage of THP if enabled const DEFAULT_BUFFER_SIZE = 2 * 1024 * 1024 # TODO: add some version of this method to Base !hasmethod(skip, Tuple{Union{Base.Process, Base.ProcessChain}, Integer}) && function Base.skip(io::Union{Base.Process, Base.ProcessChain}, n::Integer) n < 0 && throw(ArgumentError("cannot skip backwards when reading from a process")) isempty(skip_buffer) && resize!(skip_buffer, DEFAULT_BUFFER_SIZE) while n > 0 n -= readbytes!(io, skip_buffer, min(n, length(skip_buffer))) end # TODO: our skip data should throw if there's not enough data return io end const skip_buffer = UInt8[] # method for this exists in Base since Julia 1.4 but not before !hasmethod(read!, Tuple{IO, AbstractArray}) && function Base.read!(s::IO, a::AbstractArray{T}) where T if isbitstype(T) && (a isa Array || a isa Base.FastContiguousSubArray{T,<:Any,<:Array{T}}) GC.@preserve a unsafe_read(s, pointer(a), sizeof(a)) else for i in eachindex(a) a[i] = read(s, T) end end return a end function can_symlink(dir::AbstractString) # guaranteed to be an empty directory link_path = joinpath(dir, "link") return try symlink("target", link_path) true catch err err isa Base.IOError || rethrow() false finally rm(link_path, force=true) end end include("header.jl") include("create.jl") include("extract.jl") ## official API: create, list, extract, rewrite, tree_hash """ create( [ predicate, ] dir, [ tarball ]; [ skeleton, ] [ portable = false ] ) -> tarball predicate :: String --> Bool dir :: AbstractString tarball :: Union{AbstractString, AbstractCmd, IO} skeleton :: Union{AbstractString, AbstractCmd, IO} portable :: Bool Create a tar archive ("tarball") of the directory `dir`. The resulting archive is written to the path `tarball` or if no path is specified, a temporary path is created and returned by the function call. If `tarball` is an IO object then the tarball content is written to that handle instead (the handle is left open). If a `predicate` function is passed, it is called on each system path that is encountered while recursively searching `dir` and `path` is only included in the tarball if `predicate(path)` is true. If `predicate(path)` returns false for a directory, then the directory is excluded entirely: nothing under that directory will be included in the archive. If the `skeleton` keyword is passed then the file or IO handle given is used as a "skeleton" to generate the tarball. You create a skeleton file by passing the `skeleton` keyword to the `extract` command. If `create` is called with that skeleton file and the extracted files haven't changed, an identical tarball is recreated. The `skeleton` and `predicate` arguments cannot be used together. If the `portable` flag is true then path names are checked for validity on Windows, which ensures that they don't contain illegal characters or have names that are reserved. See https://stackoverflow.com/a/31976060/659248 for details. """ function create( predicate::Function, dir::AbstractString, tarball::Union{ArgWrite, Nothing} = nothing; skeleton::Union{ArgRead, Nothing} = nothing, portable::Bool = false, ) check_create_dir(dir) if skeleton === nothing arg_write(tarball) do tar create_tarball(predicate, tar, dir, portable=portable) end else predicate === true_predicate || error("create: predicate and skeleton cannot be used together") check_create_skeleton(skeleton) arg_read(skeleton) do skeleton arg_write(tarball) do tar recreate_tarball(tar, dir, skeleton, portable=portable) end end end end function create( dir::AbstractString, tarball::Union{ArgWrite, Nothing} = nothing; skeleton::Union{ArgRead, Nothing} = nothing, portable::Bool = false, ) create(true_predicate, dir, tarball, skeleton=skeleton, portable=portable) end """ list(tarball; [ strict = true ]) -> Vector{Header} list(callback, tarball; [ strict = true ]) callback :: Header, [ <data> ] --> Any tarball :: Union{AbstractString, AbstractCmd, IO} strict :: Bool List the contents of a tar archive ("tarball") located at the path `tarball`. If `tarball` is an IO handle, read the tar contents from that stream. Returns a vector of `Header` structs. See [`Header`](@ref) for details. If a `callback` is provided then instead of returning a vector of headers, the callback is called on each `Header`. This can be useful if the number of items in the tarball is large or if you want examine items prior to an error in the tarball. If the `callback` function can accept a second argument of either type `Vector{UInt8}` or `Vector{Pair{Symbol, String}}` then it will be called with a representation of the raw header data either as a single byte vector or as a vector of pairs mapping field names to the raw data for that field (if these fields are concatenated together, the result is the raw data of the header). By default `list` will error if it encounters any tarball contents which the `extract` function would refuse to extract. With `strict=false` it will skip these checks and list all the the contents of the tar file whether `extract` would extract them or not. Beware that malicious tarballs can do all sorts of crafty and unexpected things to try to trick you into doing something bad. If the `tarball` argument is a skeleton file (see `extract` and `create`) then `list` will detect that from the file header and appropriately list or iterate the headers of the skeleton file. """ function list( callback::Function, tarball::ArgRead; raw::Bool = false, strict::Bool = !raw, ) raw && strict && error("`raw=true` and `strict=true` options are incompatible") arg_read(tarball) do tar iterate_headers(callback, tar, raw=raw, strict=strict) end end function list( tarball::ArgRead; raw::Bool = false, strict::Bool = !raw, ) headers = Header[] list(tarball, raw=raw, strict=strict) do hdr push!(headers, hdr) end return headers end """ extract( [ predicate, ] tarball, [ dir ]; [ skeleton = <none>, ] [ copy_symlinks = <auto>, ] [ set_permissions = true, ] ) -> dir predicate :: Header --> Bool tarball :: Union{AbstractString, AbstractCmd, IO} dir :: AbstractString skeleton :: Union{AbstractString, AbstractCmd, IO} copy_symlinks :: Bool set_permissions :: Bool Extract a tar archive ("tarball") located at the path `tarball` into the directory `dir`. If `tarball` is an IO object instead of a path, then the archive contents will be read from that IO stream. The archive is extracted to `dir` which must either be an existing empty directory or a non-existent path which can be created as a new directory. If `dir` is not specified, the archive is extracted into a temporary directory which is returned by `extract`. If a `predicate` function is passed, it is called on each `Header` object that is encountered while extracting `tarball` and the entry is only extracted if the `predicate(hdr)` is true. This can be used to selectively extract only parts of an archive, to skip entries that cause `extract` to throw an error, or to record what is extracted during the extraction process. Before it is passed to the predicate function, the `Header` object is somewhat modified from the raw header in the tarball: the `path` field is normalized to remove `.` entries and replace multiple consecutive slashes with a single slash. If the entry has type `:hardlink`, the link target path is normalized the same way so that it will match the path of the target entry; the size field is set to the size of the target path (which must be an already-seen file). If the `skeleton` keyword is passed then a "skeleton" of the extracted tarball is written to the file or IO handle given. This skeleton file can be used to recreate an identical tarball by passing the `skeleton` keyword to the `create` function. The `skeleton` and `predicate` arguments cannot be used together. If `copy_symlinks` is `true` then instead of extracting symbolic links as such, they will be extracted as copies of what they link to if they are internal to the tarball and if it is possible to do so. Non-internal symlinks, such as a link to `/etc/passwd` will not be copied. Symlinks which are in any way cyclic will also not be copied and will instead be skipped. By default, `extract` will detect whether symlinks can be created in `dir` or not and will automatically copy symlinks if they cannot be created. If `set_permissions` is `false`, no permissions are set on the extracted files. """ function extract( predicate::Function, tarball::ArgRead, dir::Union{AbstractString, Nothing} = nothing; skeleton::Union{ArgWrite, Nothing} = nothing, copy_symlinks::Union{Bool, Nothing} = nothing, set_permissions::Bool = true, ) predicate === true_predicate || skeleton === nothing || error("extract: predicate and skeleton cannot be used together") skeleton = something(skeleton, devnull) check_extract_tarball(tarball) check_extract_dir(dir) arg_read(tarball) do tar arg_mkdir(dir) do dir if copy_symlinks === nothing copy_symlinks = !can_symlink(dir) end arg_write(skeleton) do skeleton extract_tarball( predicate, tar, dir, skeleton = skeleton, copy_symlinks = copy_symlinks, set_permissions = set_permissions, ) end end end end function extract( tarball::ArgRead, dir::Union{AbstractString, Nothing} = nothing; skeleton::Union{ArgWrite, Nothing} = nothing, copy_symlinks::Union{Bool, Nothing} = nothing, set_permissions::Bool = true, ) extract( true_predicate, tarball, dir, skeleton = skeleton, copy_symlinks = copy_symlinks, set_permissions = set_permissions, ) end """ rewrite( [ predicate, ] old_tarball, [ new_tarball ]; [ portable = false, ] ) -> new_tarball predicate :: Header --> Bool old_tarball :: Union{AbstractString, AbtractCmd, IO} new_tarball :: Union{AbstractString, AbtractCmd, IO} portable :: Bool Rewrite `old_tarball` to the standard format that `create` generates, while also checking that it doesn't contain anything that would cause `extract` to raise an error. This is functionally equivalent to doing Tar.create(Tar.extract(predicate, old_tarball), new_tarball) However, it never extracts anything to disk and instead uses the `seek` function to navigate the old tarball's data. If no `new_tarball` argument is passed, the new tarball is written to a temporary file whose path is returned. If a `predicate` function is passed, it is called on each `Header` object that is encountered while extracting `old_tarball` and the entry is skipped unless `predicate(hdr)` is true. This can be used to selectively rewrite only parts of an archive, to skip entries that would cause `extract` to throw an error, or to record what content is encountered during the rewrite process. Before it is passed to the predicate function, the `Header` object is somewhat modified from the raw header in the tarball: the `path` field is normalized to remove `.` entries and replace multiple consecutive slashes with a single slash. If the entry has type `:hardlink`, the link target path is normalized the same way so that it will match the path of the target entry; the size field is set to the size of the target path (which must be an already-seen file). If the `portable` flag is true then path names are checked for validity on Windows, which ensures that they don't contain illegal characters or have names that are reserved. See https://stackoverflow.com/a/31976060/659248 for details. """ function rewrite( predicate::Function, old_tarball::ArgRead, new_tarball::Union{ArgWrite, Nothing} = nothing; portable::Bool = false, ) old_tarball = check_rewrite_old_tarball(old_tarball) arg_read(old_tarball) do old_tar arg_write(new_tarball) do new_tar rewrite_tarball(predicate, old_tar, new_tar, portable=portable) end end end function rewrite( old_tarball::ArgRead, new_tarball::Union{ArgWrite, Nothing} = nothing; portable::Bool = false, ) rewrite(true_predicate, old_tarball, new_tarball, portable=portable) end """ tree_hash([ predicate, ] tarball; [ algorithm = "git-sha1", ] [ skip_empty = false ]) -> hash::String predicate :: Header --> Bool tarball :: Union{AbstractString, AbstractCmd, IO} algorithm :: AbstractString skip_empty :: Bool Compute a tree hash value for the file tree that the tarball contains. By default, this uses git's tree hashing algorithm with the SHA1 secure hash function (like current versions of git). This means that for any tarball whose file tree git can representโ€”i.e. one with only files, symlinks and non-empty directoriesโ€”the hash value computed by this function will be the same as the hash value git would compute for that file tree. Note that tarballs can represent file trees with empty directories, which git cannot store, and this function can generate hashes for those, which will, by default (see `skip_empty` below for how to change this behavior), differ from the hash of a tarball which omits those empty directories. In short, the hash function agrees with git on all trees which git can represent, but extends (in a consistent way) the domain of hashable trees to other trees which git cannot represent. If a `predicate` function is passed, it is called on each `Header` object that is encountered while processing `tarball` and an entry is only hashed if `predicate(hdr)` is true. This can be used to selectively hash only parts of an archive, to skip entries that cause `extract` to throw an error, or to record what is extracted during the hashing process. Before it is passed to the predicate function, the `Header` object is somewhat modified from the raw header in the tarball: the `path` field is normalized to remove `.` entries and replace multiple consecutive slashes with a single slash. If the entry has type `:hardlink`, the link target path is normalized the same way so that it will match the path of the target entry; the size field is set to the size of the target path (which must be an already-seen file). Currently supported values for `algorithm` are `git-sha1` (the default) and `git-sha256`, which uses the same basic algorithm as `git-sha1` but replaces the SHA1 hash function with SHA2-256, the hash function that git will transition to using in the future (due to known attacks on SHA1). Support for other file tree hashing algorithms may be added in the future. The `skip_empty` option controls whether directories in the tarball which recursively contain no files or symlinks are included in the hash or ignored. In general, if you are hashing the content of a tarball or a file tree, you care about all directories, not just non-empty ones, so including these in the computed hash is the default. So why does this function even provide the option to skip empty directories? Because git refuses to store empty directories and will ignore them if you try to add them to a repo. So if you compute a reference tree hash by by adding files to a git repo and then asking git for the tree hash, the hash value that you get will match the hash value computed by `tree_hash` with `skip_empty=true`. In other words, this option allows `tree_hash` to emulate how git would hash a tree with empty directories. If you are hashing trees that may contain empty directories (i.e. do not come from a git repo), however, it is recommended that you hash them using a tool (such as this one) that does not ignore empty directories. """ function tree_hash( predicate::Function, tarball::ArgRead; algorithm::AbstractString = "git-sha1", skip_empty::Bool = false, ) HashType = algorithm == "git-sha1" ? SHA.SHA1_CTX : algorithm == "git-sha256" ? SHA.SHA256_CTX : error("invalid tree hashing algorithm: $algorithm") check_tree_hash_tarball(tarball) arg_read(tarball) do tar git_tree_hash(predicate, tar, HashType, skip_empty) end end function tree_hash( tarball::ArgRead; algorithm::AbstractString = "git-sha1", skip_empty::Bool = false, ) tree_hash( true_predicate, tarball, algorithm = algorithm, skip_empty = skip_empty, ) end ## error checking utility functions check_create_dir(dir::AbstractString) = isdir(dir) || error(""" `dir` not a directory: $dir USAGE: create([predicate,] dir, [tarball]) """) check_create_skeleton(skeleton::AbstractString) = isfile(skeleton) || error(""" `skeleton` not a file: $skeleton USAGE: create([predicate,] dir, [tarball]; [skeleton = <file>]) """) check_create_skeleton(skeleton::ArgRead) = nothing check_list_tarball(tarball::AbstractString) = isfile(tarball) || error(""" `tarball` not a file: $tarball USAGE: list([predicate,] tarball) """) check_extract_tarball(tarball::AbstractString) = isfile(tarball) || error(""" `tarball` not a file: $tarball USAGE: extract([predicate,] tarball, [dir]) """) check_extract_tarball(tarball::ArgWrite) = nothing function check_extract_dir(dir::AbstractString) st = stat(dir) ispath(st) && !isdir(st) && error(""" `dir` not a directory: $dir USAGE: extract([predicate,] tarball, [dir]) """) isdir(st) && !isempty(readdir(dir)) && error(""" `dir` not empty: $dir USAGE: extract([predicate,] tarball, [dir]) """) end check_extract_dir(dir::Nothing) = nothing check_rewrite_old_tarball(old_tarball::AbstractString) = isfile(old_tarball) ? old_tarball : error(""" `old_tarball` not a file: $old_tarball USAGE: rewrite([predicate,] old_tarball, [new_tarball]) """) check_rewrite_old_tarball(old_tarball::ArgRead) = applicable(seek, old_tarball, 0) ? old_tarball : IOBuffer(read(old_tarball)) check_tree_hash_tarball(tarball::AbstractString) = isfile(tarball) || error(""" `tarball` not a file: $tarball USAGE: tree_hash([predicate,] tarball) """) check_tree_hash_tarball(tarball::ArgRead) = nothing const Str = Union{String, SubString{String}} # Special names on Windows: CON PRN AUX NUL COM1-9 LPT1-9 # we spell out uppercase/lowercase because of locales const WIN_SPECIAL_NAMES = r"^( [Cc][Oo][Nn] | [Pp][Rr][Nn] | [Aa][Uu][Xx] | [Nn][Uu][Ll] | ( [Cc][Oo][Mm] | [Ll][Pp][Tt] )[1-9] )(\.|$)"x function check_windows_path( path :: AbstractString, parts :: AbstractVector{<:Str} = split(path, r"/+"), ) for part in parts isempty(part) && continue if !isvalid(part) error("invalid Unicode: $(repr(part)) in $(repr(path))") end for ch in part ch < ' ' || ch โˆˆ "\"*:<>?\\|" || continue error("illegal Windows char: $(repr(ch)) in $(repr(path))") end if occursin(WIN_SPECIAL_NAMES, part) error("reserved Windows name: $(repr(part)) in $(repr(path))") end end end end # module m���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Tar/src/header.jlD������""" The `Header` type is a struct representing the essential metadata for a single record in a tar file with this definition: struct Header path :: String # path relative to the root type :: Symbol # type indicator (see below) mode :: UInt16 # mode/permissions (best viewed in octal) size :: Int64 # size of record data in bytes link :: String # target path of a symlink end Types are represented with the following symbols: `file`, `hardlink`, `symlink`, `chardev`, `blockdev`, `directory`, `fifo`, or for unknown types, the typeflag character as a symbol. Note that [`extract`](@ref) refuses to extract records types other than `file`, `symlink` and `directory`; [`list`](@ref) will only list other kinds of records if called with `strict=false`. The tar format includes various other metadata about records, including user and group IDs, user and group names, and timestamps. The `Tar` package, by design, completely ignores these. When creating tar files, these fields are always set to zero/empty. When reading tar files, these fields are ignored aside from verifying header checksums for each header record for all fields. """ struct Header path::String type::Symbol mode::UInt16 size::Int64 link::String end function Header( hdr::Header; path::AbstractString = hdr.path, type::Symbol = hdr.type, mode::Integer = hdr.mode, size::Integer = hdr.size, link::AbstractString = hdr.link, ) Header(path, type, mode, size, link) end function Base.show(io::IO, hdr::Header) show(io, Header) print(io, "(") show(io, hdr.path) print(io, ", ") show(io, hdr.type) print(io, ", 0o", string(hdr.mode, base=8, pad=3), ", ") show(io, hdr.size) print(io, ", ") show(io, hdr.link) print(io, ")") end const TYPE_SYMBOLS = ( '0' => :file, '\0' => :file, # legacy encoding '1' => :hardlink, '2' => :symlink, '3' => :chardev, '4' => :blockdev, '5' => :directory, '6' => :fifo, ) function to_symbolic_type(type::Char) for (t, s) in TYPE_SYMBOLS type == t && return s end isascii(type) || error("invalid type flag: $(repr(type))") return Symbol(type) end function from_symbolic_type(sym::Symbol) for (t, s) in TYPE_SYMBOLS sym == s && return t end str = String(sym) ncodeunits(str) == 1 && isascii(str[1]) || throw(ArgumentError("invalid type symbol: $(repr(sym))")) return str[1] end function check_header(hdr::Header) errors = String[] err(e::String) = push!(errors, e) # error checks isempty(hdr.path) && err("path is empty") 0x0 in codeunits(hdr.path) && err("path contains NUL bytes") 0x0 in codeunits(hdr.link) && err("link contains NUL bytes") !isempty(hdr.path) && hdr.path[1] == '/' && err("path is absolute") occursin(r"(^|/)\.\.(/|$)", hdr.path) && err("path contains '..' component") hdr.type in (:file, :hardlink, :symlink, :directory) || err("unsupported entry type") hdr.type โˆ‰ (:hardlink, :symlink) && !isempty(hdr.link) && err("non-link with link path") hdr.type โˆˆ (:hardlink, :symlink) && isempty(hdr.link) && err("$(hdr.type) with empty link path") hdr.type โˆˆ (:hardlink, :symlink) && hdr.size != 0 && err("$(hdr.type) with non-zero size") hdr.type == :hardlink && hdr.link[1] == '/' && err("hardlink with absolute link path") hdr.type == :hardlink && occursin(r"(^|/)\.\.(/|$)", hdr.link) && err("hardlink contains '..' component") hdr.type == :directory && hdr.size != 0 && err("directory with non-zero size") hdr.type != :directory && endswith(hdr.path, "/") && err("non-directory path ending with '/'") hdr.type != :directory && (hdr.path == "." || endswith(hdr.path, "/.")) && err("non-directory path ending with '.' component") hdr.size < 0 && err("negative file size") isempty(errors) && return # contruct error message if length(errors) == 1 msg = errors[1] * "\n" else msg = "tar header with multiple errors:\n" for e in sort!(errors) msg *= " * $e\n" end end msg *= repr(hdr) error(msg) end function path_header(sys_path::AbstractString, tar_path::AbstractString) st = lstat(sys_path) if islink(st) size = 0 type = :symlink mode = 0o755 link = readlink(sys_path) elseif isdir(st) size = 0 type = :directory mode = 0o755 link = "" elseif isfile(st) size = filesize(st) type = :file # Windows can't re-use the `lstat` result, we need to ask # the system whether it's executable or not via `access()` if Sys.iswindows() mode = Sys.isexecutable(sys_path) ? 0o755 : 0o644 else mode = iszero(filemode(st) & 0o100) ? 0o644 : 0o755 end link = "" else error("unsupported file type: $(repr(sys_path))") end Header(tar_path, type, mode, size, link) end m���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Tar/src/create.jl“*������function create_tarball( predicate::Function, tar::IO, root::String; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), portable::Bool = false, ) write_tarball(tar, root, buf=buf) do sys_path, tar_path portable && check_windows_path(tar_path) hdr = path_header(sys_path, tar_path) hdr.type != :directory && return hdr, sys_path paths = Dict{String,String}() for name in readdir(sys_path) sys_pathโ€ฒ = joinpath(sys_path, name) predicate(sys_pathโ€ฒ) || continue paths[name] = sys_pathโ€ฒ end return hdr, paths end end function recreate_tarball( tar::IO, root::String, skeleton::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), portable::Bool = false, ) check_skeleton_header(skeleton, buf=buf) globals = Dict{String,String}() while !eof(skeleton) hdr = read_header(skeleton, globals=globals, buf=buf, tee=tar) hdr === nothing && break check_header(hdr) portable && check_windows_path(hdr.path) sys_path = joinpath(root, hdr.path) if hdr.type == :file write_data(tar, sys_path, size=hdr.size, buf=buf) end end end function rewrite_tarball( predicate::Function, old_tar::IO, new_tar::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), portable::Bool = false, ) tree = Dict{String,Any}() read_tarball(predicate, old_tar; buf=buf) do hdr, parts portable && check_windows_path(hdr.path, parts) isempty(parts) && return node = tree name = pop!(parts) for part in parts nodeโ€ฒ = get(node, part, nothing) if !(nodeโ€ฒ isa Dict) nodeโ€ฒ = node[part] = Dict{String,Any}() end node = nodeโ€ฒ end if hdr.type == :hardlink nodeโ€ฒ = tree for part in split(hdr.link, '/') nodeโ€ฒ = nodeโ€ฒ[part] end hdrโ€ฒ = Header(nodeโ€ฒ[1], path=hdr.path, mode=hdr.mode) node[name] = (hdrโ€ฒ, nodeโ€ฒ[2]) else if !(hdr.type == :directory && get(node, name, nothing) isa Dict) node[name] = (hdr, position(old_tar)) end skip_data(old_tar, hdr.size) end end write_tarball(new_tar, tree, buf=buf) do node, tar_path if node isa Dict hdr = Header(tar_path, :directory, 0o755, 0, "") return hdr, node else hdr, pos = node mode = hdr.type == :file && iszero(hdr.mode & 0o100) ? 0o644 : 0o755 hdrโ€ฒ = Header(hdr; path=tar_path, mode=mode) data = hdr.type == :directory ? nothing : (old_tar, pos) return hdrโ€ฒ, data end end end function write_tarball( callback::Function, tar::IO, sys_path::Any, tar_path::String = "."; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) hdr, data = callback(sys_path, tar_path)::Tuple{Header,Any} if hdr.type == :directory data isa Union{Nothing, AbstractDict{<:AbstractString}} || error("callback must return a dict of strings, got: $(repr(data))") else data isa Union{Nothing, AbstractString, IO, Tuple{IO,Integer}} || error("callback must return nothing, string or IO, got: $(repr(data))") end w = 0 if tar_path != "." w += write_tarball(tar, hdr, data, buf=buf) end data isa AbstractDict && for name in sort!(collect(keys(data))) sys_pathโ€ฒ = data[name] tar_pathโ€ฒ = tar_path == "." ? name : "$tar_path/$name" w += write_tarball(callback, tar, sys_pathโ€ฒ, tar_pathโ€ฒ, buf=buf) end if tar_path == "." && w == 0 w += write_tarball(tar, hdr, data, buf=buf) end return w end function write_tarball( tar::IO, hdr::Header, data::Any = nothing; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) check_header(hdr) w = write_header(tar, hdr, buf=buf) if hdr.type == :file data isa Union{AbstractString, IO, Tuple{IO,Integer}} || throw(ArgumentError("file record requires path or IO: $(repr(hdr))")) w += write_data(tar, data, size=hdr.size, buf=buf) end return w end function write_header( tar::IO, hdr::Header; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) # extract values path = hdr.path size = hdr.size link = hdr.link # check for NULs 0x0 in codeunits(path) && throw(ArgumentError("path contains NUL bytes: $(repr(path))")) 0x0 in codeunits(link) && throw(ArgumentError("link contains NUL bytes: $(repr(path))")) # determine if an extended header is needed extended = Pair{String,String}[] # WARNING: don't change the order of these insertions # they are inserted and emitted in sorted order by key if ncodeunits(link) > 100 push!(extended, "linkpath" => link) link = "" # empty in standard header end prefix = "" name = path if ncodeunits(path) > 100 if ncodeunits(path) < 256 i = findprev('/', path, 100) if i !== nothing # try splitting into prefix and name prefix = path[1:prevind(path, i)] name = path[nextind(path, i):end] end end if ncodeunits(name) > 100 || ncodeunits(prefix) > 155 push!(extended, "path" => path) prefix = name = "" # empty in standard header end end if size โ‰ฅ 68719476736 # 8^12 push!(extended, "size" => string(size)) # still written in binary in standard header end # emit extended header if necessary w = 0 if !isempty(extended) @assert issorted(extended) w += write_extended_header(tar, extended, buf=buf) end # emit standard header std_hdr = Header(hdr; link=link) w += write_standard_header(tar, std_hdr, name=name, prefix=prefix, buf=buf) end function write_extended_header( tar::IO, metadata::Vector{Pair{String,String}}; type::Symbol = :x, # default: non-global extended header name::AbstractString = "", prefix::AbstractString = "", link::AbstractString = "", mode::Integer = 0o000, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) type in (:x, :g) || throw(ArgumentError("invalid type flag for extended header: $(repr(type))")) d = IOBuffer() for (key, val) in metadata entry = " $key=$val\n" n = l = ncodeunits(entry) while n < l + ndigits(n) n = l + ndigits(n) end @assert n == l + ndigits(n) write(d, "$n$entry") end path = isempty(name) || isempty(prefix) ? "$prefix$name" : "$prefix/$name" hdr = Header(path, type, mode, position(d), link) w = write_standard_header(tar, hdr, name=name, prefix=prefix, buf=buf) w += write_data(tar, seekstart(d), size=hdr.size, buf=buf) end function write_standard_header( tar::IO, hdr::Header; name::AbstractString = hdr.path, prefix::AbstractString = "", buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) name = String(name) prefix = String(prefix) type = from_symbolic_type(hdr.type) link = hdr.link # octal strings for size and mode m = string(hdr.mode, base=8, pad=6) s = string(hdr.size, base=8, pad=11) # error checking (presumes checks done by write_header) hdr.size < 0 && throw(ArgumentError("negative file size is invalid: $(hdr.size)")) ncodeunits(prefix) โ‰ค 155 || throw(ArgumentError("path prefix too long for standard header: $(repr(prefix))")) ncodeunits(name) โ‰ค 100 || throw(ArgumentError("path name too long for standard header: $(repr(name))")) ncodeunits(link) โ‰ค 100 || throw(ArgumentError("symlink target too long for standard header: $(repr(link))")) ncodeunits(m) โ‰ค 6 || throw(ArgumentError("mode too large for standard header: 0o$m")) isascii(type) || throw(ArgumentError("non-ASCII type flag value: $(repr(type))")) # construct header block data = view(buf, 1:512) h = IOBuffer(fill!(data, 0x00), write=true, truncate=false) write(h, name) # name seek(h, 100) write(h, "$m \0") # mode write(h, "000000 \0") # uid write(h, "000000 \0") # gid if ncodeunits(s) โ‰ค 12 # size write(h, s) if ncodeunits(s) < 12 write(h, ' ') end else # emulate GNU tar: write binary size with leading bit set # can encode up to 2^95; Int64 size field only up to 2^63-1 write(h, 0x80 | ((hdr.size >> (8*11)) % UInt8)) for i = 10:-1:0 write(h, (hdr.size >> 8i) % UInt8) end end write(h, "00000000000 ") # mtime write(h, " ") # chksum (blank) write(h, type) # typeflag @assert position(h) == 157 write(h, link) # linkname seek(h, 257) write(h, "ustar\0") # magic write(h, "00") # version skip(h, 64) # uname & gname write(h, "000000 \0") # devmajor write(h, "000000 \0") # devminor @assert position(h) == 345 write(h, prefix) # prefix # fix the checksum c = string(sum(data), base=8, pad=6) @assert ncodeunits(c) โ‰ค 6 seek(h, 148) write(h, "$c\0 ") @assert position(h) == 156 # write header w = write(tar, data) @assert w == 512 return w end function write_data( tar::IO, data::IO; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) size < 0 && throw(ArgumentError("cannot write negative data: $size")) w, t = 0, round_up(size) while size > 0 b = Int(min(size, length(buf)))::Int n = Int(readbytes!(data, buf, b))::Int n < b && eof(data) && throw(EOFError()) w += write(tar, view(buf, 1:n)) size -= n t -= n end @assert size == 0 @assert 0 โ‰ค t < 512 t > 0 && (w += write(tar, fill!(view(buf, 1:t), 0))) return w end function write_data( tar::IO, (data, pos)::Tuple{IO,Integer}; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) seek(data, pos) write_data(tar, data, size=size, buf=buf) end function write_data( tar::IO, file::String; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) open(file) do data write_data(tar, data, size=size, buf=buf) eof(data) || error("data file too large: $data") end end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Tar/src/extract.jlซc������import SHA function iterate_headers( callback::Function, tar::IO; raw::Bool = false, strict::Bool = !raw, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) eof(tar) && return hdr = read_standard_header(tar, buf=buf) hdr === nothing && return if !raw globals = Dict{String,String}() end if hdr.type == :g && hdr.path == SKELETON_MAGIC skip_data(tar, hdr.size) skeleton = true else if !raw hdr = read_header(tar, hdr, globals=globals, buf=buf) end skeleton = false @goto loop end while !eof(tar) hdr = if raw read_standard_header(tar, buf=buf) else read_header(tar, globals=globals, buf=buf) end @label loop hdr === nothing && break strict && check_header(hdr) if hasmethod(callback, Tuple{Header, Vector{Pair{Symbol, String}}}) callback(hdr, dump_header(buf)) elseif hasmethod(callback, Tuple{Header, Vector{UInt8}}) callback(hdr, buf[1:512]) else callback(hdr) end if !skeleton || hdr.type in (:g, :x) || hdr.path == "././@LongLink" && hdr.type in (:L, :K) skip_data(tar, hdr.size) end end end # follow chains of symlinks follow_symlink_chain(seen::Vector, what::Any, paths) = what == :symlink ? what : seen[end] follow_symlink_chain(seen::Vector, what::String, paths) = what in seen ? :symlink : follow_symlink_chain(push!(seen, what), paths[what], paths) # our `cp` doesn't copy ACL properties, so manually set them via `chmod` function copy_mode(src::String, dst::String) chmod(dst, filemode(src)) isdir(dst) || return for name in readdir(dst) sub_src = joinpath(src, name) sub_dst = joinpath(dst, name) copy_mode(sub_src, sub_dst) end end function extract_tarball( predicate::Function, tar::IO, root::String; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), skeleton::IO = devnull, copy_symlinks::Bool = false, set_permissions::Bool = true, ) root = normpath(root) paths = read_tarball(predicate, tar; buf=buf, skeleton=skeleton) do hdr, parts Sys.iswindows() && check_windows_path(hdr.path, parts) # get the file system version of the path sys_path = isempty(parts) ? "." : reduce(joinpath, parts) isabspath(sys_path) && error("attempt to extract absolute path at $(repr(sys_path)) from $(repr(hdr.path))") sys_path = sys_path == "." ? root : normpath(root, sys_path) startswith(sys_path, root) || error("attempt to extract relative path outside of root at $(repr(sys_path)) from $(repr(hdr.path))") src_path = joinpath(root, hdr.link) dir = dirname(sys_path) st = stat(dir) # ensure dirname(sys_path) is a directory if !isdir(st) ispath(st) && rm(dir, force=true, recursive=true) mkpath(dir) elseif hdr.type != :hardlink || src_path != sys_path st = lstat(sys_path) hdr.type == :directory && isdir(st) && return # from callback ispath(st) && rm(sys_path, force=true, recursive=true) end # create the path if hdr.type == :directory mkdir(sys_path) elseif hdr.type == :symlink copy_symlinks || symlink(hdr.link, sys_path) elseif hdr.type == :hardlink src_path != sys_path && cp(src_path, sys_path) elseif hdr.type == :file read_data(tar, sys_path, size=hdr.size, buf=buf) else # should already be caught by check_header error("unsupported tarball entry type: $(hdr.type)") end # apply tarball permissions if set_permissions && hdr.type in (:file, :hardlink) exec = 0o100 & hdr.mode != 0 tar_mode = exec ? 0o755 : 0o644 sys_mode = filemode(sys_path) if exec # copy read bits to execute bits with # at least the user execute bit on sys_mode |= 0o100 | (sys_mode & 0o444) >> 2 # TODO: would be better to have the system # create an executable with default mode but # we don't have a way to do that afaik end chmod(sys_path, tar_mode & sys_mode) end end copy_symlinks || return # resolve the internal targets of symlinks for (path, what) in paths what isa String || continue target = link_target(paths, path, what) paths[path] = something(target, :symlink) end for (path, what) in paths what isa AbstractString || continue paths[path] = follow_symlink_chain([path], what, paths) end # copies that need to be made copies = Pair{String,String}[] for (path, what) in paths what isa AbstractString || continue push!(copies, path => String(what)::String) end sort!(copies, by=last) while !isempty(copies) i = 1 while i โ‰ค length(copies) path, what = copies[i] # check if source is complete yet if any(startswith(p, "$what/") for (p, w) in copies) # `what` is an incomplete directory # need to wait for source to be complete i += 1 else # source complete, can copy now deleteat!(copies, i) src = reduce(joinpath, init=root, split(what, '/')) dst = reduce(joinpath, init=root, split(path, '/')) cp(src, dst) if set_permissions && Sys.iswindows() copy_mode(src, dst) end end end end end # resolve symlink target or nothing if not valid function link_target( paths::Dict{String}, path::AbstractString, link::AbstractString, ) first(link) == '/' && return path_parts = split(path, r"/+") link_parts = split(link, r"/+") pop!(path_parts) part = nothing # remember the last part while !isempty(link_parts) part = popfirst!(link_parts) part in ("", ".") && continue if part == ".." isempty(path_parts) && return pop!(path_parts) else push!(path_parts, part) prefix = join(path_parts, '/') prefix in keys(paths) || return isempty(link_parts) && break what = paths[prefix] if what isa AbstractString what = convert(String, what)::String prefix = link_target(paths, prefix, what) prefix === nothing && return path_parts = split(prefix, '/') end end end isempty(path_parts) && return target = join(path_parts, '/') # if link ends in `/` or `.` target must be a directory part in ("", ".") && paths[target] !== :directory && return # can't copy a circular link to a prefix of itself (path == target || startswith(path, "$target/")) && return return target end function git_tree_hash( predicate::Function, tar::IO, HashType::DataType, skip_empty::Bool; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) # build tree with leaves for files and symlinks tree = Dict{String,Any}() read_tarball(predicate, tar; buf=buf) do hdr, parts isempty(parts) && return name = pop!(parts) node = tree for part in parts nodeโ€ฒ = get(node, part, nothing) if !(nodeโ€ฒ isa Dict) nodeโ€ฒ = node[part] = Dict{String,Any}() end node = nodeโ€ฒ end if hdr.type == :directory if !(get(node, name, nothing) isa Dict) node[name] = Dict{String,Any}() end return elseif hdr.type == :symlink mode = "120000" hash = git_object_hash("blob", HashType) do io write(io, hdr.link) end elseif hdr.type == :hardlink mode = iszero(hdr.mode & 0o100) ? "100644" : "100755" nodeโ€ฒ = tree for part in split(hdr.link, '/') nodeโ€ฒ = nodeโ€ฒ[part] end hash = nodeโ€ฒ[2] # hash of linked file elseif hdr.type == :file mode = iszero(hdr.mode & 0o100) ? "100644" : "100755" hash = git_file_hash(tar, hdr.size, HashType, buf=buf) else error("unsupported type for git tree hashing: $(hdr.type)") end node[name] = (mode, hash) end # prune directories that don't contain any files if skip_empty prune_empty!(node::Tuple) = true function prune_empty!(node::Dict) filter!(node) do (name, child) prune_empty!(child) end return !isempty(node) end prune_empty!(tree) end # reduce the tree to a single hash value hash_tree(node::Tuple) = node function hash_tree(node::Dict) by((name, child)) = child isa Dict ? "$name/" : name hash = git_object_hash("tree", HashType) do io for (name, child) in sort!(collect(node), by=by) mode, hash = hash_tree(child) print(io, mode, ' ', name, '\0') write(io, hex2bytes(hash)) end end return "40000", hash end return hash_tree(tree)[end] end function git_object_hash( emit::Function, kind::AbstractString, HashType::DataType, ) ctx = HashType() body = codeunits(sprint(emit)) SHA.update!(ctx, codeunits("$kind $(length(body))\0")) SHA.update!(ctx, body) return bytes2hex(SHA.digest!(ctx)) end function git_file_hash( tar::IO, size::Integer, HashType::DataType; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) ctx = HashType() SHA.update!(ctx, codeunits("blob $size\0")) # TODO: this largely duplicates the logic of read_data # read_data could be used directly if SHA offered an interface # where you write data to an IO object and it maintains a hash padded_size = round_up(size) while padded_size > 0 max_read_len = Int(min(padded_size, length(buf)))::Int read_len = Int(readbytes!(tar, buf, max_read_len))::Int read_len < max_read_len && eof(tar) && throw(EOFError()) nonpadded_view = view(buf, 1:Int(min(read_len, size))) SHA.update!(ctx, nonpadded_view) size -= length(nonpadded_view) padded_size -= read_len end @assert size == padded_size == 0 return bytes2hex(SHA.digest!(ctx)) end const SKELETON_MAGIC = "%!skeleton:\x83\xe6\xa8\xfe" function write_skeleton_header( skeleton::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) skeleton === devnull || write_extended_header( skeleton, type = :g, name = SKELETON_MAGIC, buf = buf, ["comment" => "Tar.jl skeleton file", "size" => "0"], ) end function check_skeleton_header( skeleton::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), ) hdr = read_standard_header(skeleton, buf=buf) hdr.type == :g && hdr.path == SKELETON_MAGIC || error("not a skeleton file: $skeleton") skip_data(skeleton, hdr.size) end function read_tarball( callback::Function, predicate::Function, tar::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), skeleton::IO = devnull, ) write_skeleton_header(skeleton, buf=buf) # symbols for path types except symlinks store the link paths = Dict{String,Any}() globals = Dict{String,String}() while !eof(tar) hdr = read_header(tar, globals=globals, buf=buf, tee=skeleton) hdr === nothing && break err = nothing # normalize path and check for symlink attacks path = "" for part in split(hdr.path, '/') # check_header checks for ".." later (isempty(part) || part == ".") && continue if err === nothing && get(paths, path, nothing) isa String err = """ Tarball contains path with symlink prefix: - path = $(repr(hdr.path)) - prefix = $(repr(path)) Refusing to extract โ€” possible attack! """ end path = isempty(path) ? String(part) : "$path/$part" end hdrโ€ฒ = Header(hdr, path=path) # check that hardlinks refer to already-seen files if err === nothing && hdr.type == :hardlink parts = filter!(split(hdr.link, '/')) do part # check_header checks for ".." later !isempty(part) && part != "." end link = join(parts, '/') hdr = Header(hdr, link=link) hdrโ€ฒ = Header(hdrโ€ฒ, link=link) what = get(paths, link, Symbol("non-existent")) if what isa Int64 # plain file hdrโ€ฒ = Header(hdrโ€ฒ, size=what) else err = """ Tarball contains hardlink with $what target: - path = $(repr(hdr.path)) - target = $(repr(hdr.link)) Refusing to extract โ€” possible attack! """ end end # check if we should extract or skip if !(predicate(hdrโ€ฒ)::Bool) # pass normalized header skip_data(tar, hdr.size) continue end check_header(hdr) err === nothing || error(err) # record info about path paths[path] = hdr.type == :symlink ? hdr.link : hdr.type == :file ? hdr.size : hdr.type # apply callback, checking that it consumes IO correctly before = applicable(position, tar) ? position(tar) : 0 callback(hdr, split(path, '/', keepempty=false)) applicable(position, tar) || continue advanced = position(tar) - before expected = round_up(hdr.size) advanced == expected || error("callback read $advanced bytes instead of $expected") end return paths end function read_header( io::IO; globals::Dict{String,String} = Dict{String,String}(), buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, ) hdr = read_standard_header(io, buf=buf, tee=tee) hdr === nothing && return nothing read_header(io, hdr, globals=globals, buf=buf, tee=tee) end function read_header( io::IO, hdr::Header; # initial header globals::Dict{String,String} = Dict{String,String}(), buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, ) # process zero or more extended headers metadata = copy(globals) while true if hdr.type in (:g, :x) # POSIX extended headers let hdr=hdr # https://github.com/JuliaLang/julia/issues/15276 read_extended_metadata(io, hdr.size, buf=buf, tee=tee) do key, val if key in ("size", "path", "linkpath") if hdr.type == :g globals[key] = val end metadata[key] = val end end end elseif hdr.path == "././@LongLink" && hdr.type in (:L, :K) # GNU long name or link header data = read_data(io, size=hdr.size, buf=buf, tee=tee) data[end] == 0 || error("malformed GNU long header (trailing `\\0` expected): " * repr(String(data))) key = hdr.type == :L ? "path" : "linkpath" metadata[key] = String(@view data[1:end-1]) else break # non-extension header block end hdr = read_standard_header(io, buf=buf, tee=tee) hdr === nothing && throw(EOFError()) end # determine final values for size, path & link size = hdr.size if "size" in keys(metadata) val = metadata["size"] size = tryparse(UInt64, val) size === nothing && error("invalid extended header size value: $(repr(val))") end path = get(metadata, "path", hdr.path) link = get(metadata, "linkpath", hdr.link) # construct and return Header object return Header(hdr; path=path, size=size, link=link) end using Base.Checked: mul_with_overflow, add_with_overflow function read_extended_metadata( callback::Function, io::IO, size::Integer; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, ) size > typemax(Int32) && error("read_extended_metadata called with too large size: $size") size = Int(size) data = read_data(io, size=size, buf=buf, tee=tee) malformed() = error("malformed extended header metadata: $(repr(String(data)))") i = 0 while i < size j, m = i, 0 while j โ‰ค size byte = data[j += 1] byte == UInt8(' ') && break UInt8('0') โ‰ค byte โ‰ค UInt8('9') || malformed() m, fm = mul_with_overflow(m, 10) m, fa = add_with_overflow(m, Int(byte - UInt8('0'))) fm | fa && error("extended header record size too large: $(repr(String(data)))") end k, l = j, i + m while k โ‰ค l byte = data[k += 1] byte == UInt8('=') && break end # data[i] is end of previous # data[i+1:j-1] is length in decimal # data[j] is ` ` (space) # data[j+1:k-1] is key string # data[k] is `=` (equals) # data[k+1:l-1] is value string # data[l] is `\n` (newline) i+1 < j < k < l || malformed() @assert data[j] == UInt8(' ') @assert data[k] == UInt8('=') data[l] == UInt('\n') || malformed() i = l # next starting point # pass key, value back to caller key = String(@view data[j+1:k-1]) val = String(@view data[k+1:l-1]) callback(key, val) end end # For reference see # https://www.gnu.org/software/tar/manual/html_node/Standard.html const HEADER_FIELDS = ( # field, offset, size (:name, 0, 100), (:mode, 100, 8), (:uid, 108, 8), (:gid, 116, 8), (:size, 124, 12), (:mtime, 136, 12), (:chksum, 148, 8), (:typeflag, 156, 1), (:linkname, 157, 100), (:magic, 257, 6), (:version, 263, 2), (:uname, 265, 32), (:gname, 297, 32), (:devmajor, 329, 8), (:devminor, 337, 8), (:prefix, 345, 155), (:rest, 500, 12), ) function index_range(field::Symbol) for (fld, off, len) in HEADER_FIELDS fld == field && return off .+ (1:len) end error("[internal error] invalid field name: $field") end dump_header(buf::AbstractVector{UInt8}) = [ fld => String(buf[off .+ (1:len)]) for (fld, off, len) in HEADER_FIELDS ] function header_error(buf::AbstractVector{UInt8}, msg::AbstractString) sprint() do io println(io, msg, "\n[header block data]:") for (field, value) in dump_header(buf) print(io, " ", rpad(field, 8), " = ") show(io, value) println(io) end end |> error end function header_error(buf::AbstractVector{UInt8}, fld::Symbol) value = read_header_str(buf, fld) header_error(buf, "malformed $fld field: $(repr(value))") end function read_standard_header( io::IO; buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, ) data = read_data(io, size=512, buf=buf, tee=tee) # zero block indicates end of tarball if all(iszero, data) while !eof(io) r = Int(readbytes!(io, buf))::Int write(tee, view(buf, 1:r)) end return nothing end # verify valid header try check_version_field(buf) check_checksum_field(buf) catch err if err isa ErrorException msg = match(r"^(.*?)\s*\[header block data\]"s, convert(Union{String, SubString{String}}, err.msg)::Union{String, SubString{String}}).captures[1] msg = "This does not appear to be a TAR file/stream โ€” $msg. Note: Tar.jl does not handle decompression; if the tarball is compressed you must use an external command like `gzcat` or package like CodecZlib.jl to decompress it. See the README file for examples." err = ErrorException(msg) end rethrow(err) end # extract fields we care about size = read_header_size(buf) name = read_header_str(buf, :name) mode = read_header_int(buf, :mode) type = read_header_chr(buf, :typeflag) link = read_header_str(buf, :linkname) prefix = read_header_str(buf, :prefix) # check that mode isn't too big mode โ‰ค typemax(typemax(UInt16)) || header_error(buf, "mode value too large: $(string(mode, base=8))") # combine prefix & name fields path = isempty(prefix) ? name : "$prefix/$name" return Header(path, to_symbolic_type(type), mode, size, link) end function check_version_field(buf::AbstractVector{UInt8}) version = read_header_str(buf, :version) occursin(r"^0* *$", version) && return header_error(buf, "invalid version string for tar file: $(repr(version))") end function check_checksum_field(buf::AbstractVector{UInt8}) chksum = read_header_int(buf, :chksum) actual = let r = index_range(:chksum) sum(i in r ? UInt8(' ') : buf[i] for i = 1:512) end chksum == actual && return header_error(buf, "incorrect header checksum = $chksum; should be $actual") end function read_header_size(buf::AbstractVector{UInt8}) r = index_range(:size) b1 = buf[r[1]] # high bit set for binary b1 & 0x80 == 0 && return read_header_int(buf, :size) b1 == 0x80 && return read_header_bin(buf, :size, r[1]+1:r[end]) val = String(buf[r]) header_error(buf, "binary integer size value too large: $(repr(val))") end function read_header_chr(buf::AbstractVector{UInt8}, fld::Symbol) r = index_range(fld) length(r) == 1 || error("[internal error] not a character field: $fld") return Char(buf[first(r)]) end function read_header_str(buf::AbstractVector{UInt8}, fld::Symbol) r = index_range(fld) for i in r byte = buf[i] byte == 0 && return String(buf[first(r):i-1]) end return String(buf[r]) end function read_header_int(buf::AbstractVector{UInt8}, fld::Symbol) r = index_range(fld) n = Int64(0) before = true for i in r byte = buf[i] before && byte == UInt8(' ') && continue byte in (0x00, UInt8(' ')) && break UInt8('0') <= byte <= UInt8('7') || header_error(buf, fld) if leading_zeros(n) <= 3 val = String(buf[r]) header_error(buf, "octal integer $fld value too large: $(repr(val))") end n <<= 3 n |= byte - 0x30 before = false end before && header_error(buf, fld) return n end function read_header_bin( buf::AbstractVector{UInt8}, fld::Symbol, r::UnitRange{<:Integer} = index_range(fld), ) n = Int64(0) for i in r if leading_zeros(n) <= 8 val = String(buf[r]) header_error(buf, "binary integer $fld value too large: $(repr(val))") end n <<= 8 n |= buf[i] end return n end round_up(size) = 512 * ((size + 511) รท 512) function skip_data(tar::IO, size::Integer) size < 0 && throw(ArgumentError("[internal error] negative skip: $size")) size > 0 && skip(tar, round_up(size)) end function read_data( tar::IO, file::IO; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, )::Nothing padded_size = round_up(size) while padded_size > 0 max_read_len = Int(min(padded_size, length(buf)))::Int read_len = Int(readbytes!(tar, buf, max_read_len))::Int write(tee, view(buf, 1:read_len)) read_len < max_read_len && eof(tar) && throw(EOFError()) size -= write(file, view(buf, 1:Int(min(read_len, size)))) padded_size -= read_len end @assert size == padded_size == 0 return end function read_data( tar::IO, file::AbstractString; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, )::Nothing open(file, write=true) do fileโ€ฒ read_data(tar, fileโ€ฒ, size=size, buf=buf, tee=tee) end end # WARNING: this returns a view into `buf` so you must either use # the result before making another call using `buf` or make a copy function read_data( tar::IO; size::Integer, buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE), tee::IO = devnull, )::AbstractVector{UInt8} padded_size = round_up(size) padded_size > typemax(Int32) && throw(ArgumentError("read_data(tar; size) called with too large size: $size")) padded_size = Int(padded_size) length(buf) < padded_size && resize!(buf, nextpow(2, padded_size)) write(tee, read!(tar, view(buf, 1:padded_size))) return view(buf, 1:size) end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/Dates.jlO ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ Dates The `Dates` module provides `Date`, `DateTime`, `Time` types, and related functions. The types are not aware of time zones, based on UT seconds (86400 seconds a day, avoiding leap seconds), and use the proleptic Gregorian calendar, as specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601). For time zone functionality, see the TimeZones.jl package. ```jldoctest julia> dt = DateTime(2017,12,31,23,59,59,999) 2017-12-31T23:59:59.999 julia> d1 = Date(Month(12), Year(2017)) 2017-12-01 julia> d2 = Date("2017-12-31", DateFormat("y-m-d")) 2017-12-31 julia> yearmonthday(d2) (2017, 12, 31) julia> d2-d1 30 days ``` Please see the manual section on [`Date`](@ref) and [`DateTime`](@ref) for more information. """ module Dates import Base: ==, isless, div, fld, mod, rem, gcd, lcm, +, -, *, /, %, broadcast using Printf: @sprintf using Base.Iterators include("types.jl") include("periods.jl") include("accessors.jl") include("query.jl") include("arithmetic.jl") include("conversions.jl") include("ranges.jl") include("adjusters.jl") include("rounding.jl") include("io.jl") include("parse.jl") include("deprecated.jl") export Period, DatePeriod, TimePeriod, Year, Quarter, Month, Week, Day, Hour, Minute, Second, Millisecond, Microsecond, Nanosecond, TimeZone, UTC, TimeType, DateTime, Date, Time, # periods.jl canonicalize, # accessors.jl yearmonthday, yearmonth, monthday, year, month, week, day, hour, minute, second, millisecond, dayofmonth, microsecond, nanosecond, # query.jl dayofweek, isleapyear, daysinmonth, daysinyear, dayofyear, dayname, dayabbr, dayofweekofmonth, daysofweekinmonth, monthname, monthabbr, quarterofyear, dayofquarter, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, Mon, Tue, Wed, Thu, Fri, Sat, Sun, January, February, March, April, May, June, July, August, September, October, November, December, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, # conversions.jl unix2datetime, datetime2unix, now, today, rata2datetime, datetime2rata, julian2datetime, datetime2julian, # adjusters.jl firstdayofweek, lastdayofweek, firstdayofmonth, lastdayofmonth, firstdayofyear, lastdayofyear, firstdayofquarter, lastdayofquarter, adjust, tonext, toprev, tofirst, tolast, # io.jl ISODateTimeFormat, ISODateFormat, ISOTimeFormat, DateFormat, RFC1123Format, @dateformat_str end # module n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/types.jl:<������# This file is a part of Julia. License is MIT: https://julialang.org/license abstract type AbstractTime end """ Period Year Quarter Month Week Day Hour Minute Second Millisecond Microsecond Nanosecond `Period` types represent discrete, human representations of time. """ abstract type Period <: AbstractTime end """ DatePeriod Year Quarter Month Week Day Intervals of time greater than or equal to a day. Conventional comparisons between `DatePeriod`s are not all valid. (eg `Week(1) == Day(7)`, but `Year(1) != Day(365)`) """ abstract type DatePeriod <: Period end """ TimePeriod Hour Minute Second Millisecond Microsecond Nanosecond Intervals of time less than a day. Conversions between all `TimePeriod`s are permissible. (eg `Hour(1) == Minute(60) == Second(3600)`) """ abstract type TimePeriod <: Period end for T in (:Year, :Quarter, :Month, :Week, :Day) @eval struct $T <: DatePeriod value::Int64 $T(v::Number) = new(v) end end for T in (:Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond) @eval struct $T <: TimePeriod value::Int64 $T(v::Number) = new(v) end end """ Year(v) Quarter(v) Month(v) Week(v) Day(v) Hour(v) Minute(v) Second(v) Millisecond(v) Microsecond(v) Nanosecond(v) Construct a `Period` type with the given `v` value. Input must be losslessly convertible to an [`Int64`](@ref). """ Period(v) """ Instant `Instant` types represent integer-based, machine representations of time as continuous timelines starting from an epoch. """ abstract type Instant <: AbstractTime end """ UTInstant{T} The `UTInstant` represents a machine timeline based on UT time (1 day = one revolution of the earth). The `T` is a `Period` parameter that indicates the resolution or precision of the instant. """ struct UTInstant{P<:Period} <: Instant periods::P end # Convenience default constructors UTM(x) = UTInstant(Millisecond(x)) UTD(x) = UTInstant(Day(x)) # Calendar types provide rules for interpreting instant # timelines in human-readable form. abstract type Calendar <: AbstractTime end # ISOCalendar implements the ISO 8601 standard (en.wikipedia.org/wiki/ISO_8601) # Notably based on the proleptic Gregorian calendar # ISOCalendar provides interpretation rules for UTInstants to civil date and time parts struct ISOCalendar <: Calendar end """ TimeZone Geographic zone generally based on longitude determining what the time is at a certain location. Some time zones observe daylight savings (eg EST -> EDT). For implementations and more support, see the [`TimeZones.jl`](https://github.com/JuliaTime/TimeZones.jl) package """ abstract type TimeZone end """ UTC `UTC`, or Coordinated Universal Time, is the [`TimeZone`](@ref) from which all others are measured. It is associated with the time at 0ยฐ longitude. It is not adjusted for daylight savings. """ struct UTC <: TimeZone end """ TimeType `TimeType` types wrap `Instant` machine instances to provide human representations of the machine instant. `Time`, `DateTime` and `Date` are subtypes of `TimeType`. """ abstract type TimeType <: AbstractTime end abstract type AbstractDateTime <: TimeType end """ DateTime `DateTime` wraps a `UTInstant{Millisecond}` and interprets it according to the proleptic Gregorian calendar. """ struct DateTime <: AbstractDateTime instant::UTInstant{Millisecond} DateTime(instant::UTInstant{Millisecond}) = new(instant) end """ Date `Date` wraps a `UTInstant{Day}` and interprets it according to the proleptic Gregorian calendar. """ struct Date <: TimeType instant::UTInstant{Day} Date(instant::UTInstant{Day}) = new(instant) end """ Time `Time` wraps a `Nanosecond` and represents a specific moment in a 24-hour day. """ struct Time <: TimeType instant::Nanosecond Time(instant::Nanosecond) = new(mod(instant, 86400000000000)) end # Convert y,m,d to # of Rata Die days # Works by shifting the beginning of the year to March 1, # so a leap day is the very last day of the year const SHIFTEDMONTHDAYS = (306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275) function totaldays(y, m, d) # If we're in Jan/Feb, shift the given year back one z = m < 3 ? y - 1 : y mdays = SHIFTEDMONTHDAYS[m] # days + month_days + year_days return d + mdays + 365z + fld(z, 4) - fld(z, 100) + fld(z, 400) - 306 end # If the year is divisible by 4, except for every 100 years, except for every 400 years isleapyear(y) = (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0)) # Number of days in month const DAYSINMONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y)) ### UTILITIES ### # These are necessary because the type constructors for TimeType subtypes can # throw, and we want to be able to use tryparse without requiring a try/catch. # This is made easier by providing a helper function that checks arguments, so # we can validate arguments in tryparse. """ validargs(::Type{<:TimeType}, args...) -> Union{ArgumentError, Nothing} Determine whether the given arguments constitute valid inputs for the given type. Returns either an `ArgumentError`, or [`nothing`](@ref) in case of success. """ function validargs end # Julia uses 24-hour clocks internally, but user input can be AM/PM with 12pm == noon and 12am == midnight. @enum AMPM AM PM TWENTYFOURHOUR function adjusthour(h::Int64, ampm::AMPM) ampm == TWENTYFOURHOUR && return h ampm == PM && h < 12 && return h + 12 ampm == AM && h == 12 && return Int64(0) return h end ### CONSTRUCTORS ### # Core constructors """ DateTime(y, [m, d, h, mi, s, ms]) -> DateTime Construct a `DateTime` type by parts. Arguments must be convertible to [`Int64`](@ref). """ function DateTime(y::Int64, m::Int64=1, d::Int64=1, h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0, ampm::AMPM=TWENTYFOURHOUR) err = validargs(DateTime, y, m, d, h, mi, s, ms, ampm) err === nothing || throw(err) h = adjusthour(h, ampm) rata = ms + 1000 * (s + 60mi + 3600h + 86400 * totaldays(y, m, d)) return DateTime(UTM(rata)) end function validargs(::Type{DateTime}, y::Int64, m::Int64, d::Int64, h::Int64, mi::Int64, s::Int64, ms::Int64, ampm::AMPM=TWENTYFOURHOUR) 0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)") 0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))") if ampm == TWENTYFOURHOUR # 24-hour clock -1 < h < 24 || (h == 24 && mi==s==ms==0) || return ArgumentError("Hour: $h out of range (0:23)") else 0 < h < 13 || return ArgumentError("Hour: $h out of range (1:12)") end -1 < mi < 60 || return ArgumentError("Minute: $mi out of range (0:59)") -1 < s < 60 || return ArgumentError("Second: $s out of range (0:59)") -1 < ms < 1000 || return ArgumentError("Millisecond: $ms out of range (0:999)") return nothing end DateTime(dt::Base.Libc.TmStruct) = DateTime(1900 + dt.year, 1 + dt.month, dt.mday, dt.hour, dt.min, dt.sec) """ Date(y, [m, d]) -> Date Construct a `Date` type by parts. Arguments must be convertible to [`Int64`](@ref). """ function Date(y::Int64, m::Int64=1, d::Int64=1) err = validargs(Date, y, m, d) err === nothing || throw(err) return Date(UTD(totaldays(y, m, d))) end function validargs(::Type{Date}, y::Int64, m::Int64, d::Int64) 0 < m < 13 || return ArgumentError("Month: $m out of range (1:12)") 0 < d < daysinmonth(y, m) + 1 || return ArgumentError("Day: $d out of range (1:$(daysinmonth(y, m)))") return nothing end Date(dt::Base.Libc.TmStruct) = Date(1900 + dt.year, 1 + dt.month, dt.mday) """ Time(h, [mi, s, ms, us, ns]) -> Time Construct a `Time` type by parts. Arguments must be convertible to [`Int64`](@ref). """ function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0, ampm::AMPM=TWENTYFOURHOUR) err = validargs(Time, h, mi, s, ms, us, ns, ampm) err === nothing || throw(err) h = adjusthour(h, ampm) return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h)) end function validargs(::Type{Time}, h::Int64, mi::Int64, s::Int64, ms::Int64, us::Int64, ns::Int64, ampm::AMPM=TWENTYFOURHOUR) if ampm == TWENTYFOURHOUR # 24-hour clock -1 < h < 24 || return ArgumentError("Hour: $h out of range (0:23)") else 0 < h < 13 || return ArgumentError("Hour: $h out of range (1:12)") end -1 < mi < 60 || return ArgumentError("Minute: $mi out of range (0:59)") -1 < s < 60 || return ArgumentError("Second: $s out of range (0:59)") -1 < ms < 1000 || return ArgumentError("Millisecond: $ms out of range (0:999)") -1 < us < 1000 || return ArgumentError("Microsecond: $us out of range (0:999)") -1 < ns < 1000 || return ArgumentError("Nanosecond: $ns out of range (0:999)") return nothing end Time(dt::Base.Libc.TmStruct) = Time(dt.hour, dt.min, dt.sec) # Convenience constructors from Periods function DateTime(y::Year, m::Month=Month(1), d::Day=Day(1), h::Hour=Hour(0), mi::Minute=Minute(0), s::Second=Second(0), ms::Millisecond=Millisecond(0)) return DateTime(value(y), value(m), value(d), value(h), value(mi), value(s), value(ms)) end Date(y::Year, m::Month=Month(1), d::Day=Day(1)) = Date(value(y), value(m), value(d)) function Time(h::Hour, mi::Minute=Minute(0), s::Second=Second(0), ms::Millisecond=Millisecond(0), us::Microsecond=Microsecond(0), ns::Nanosecond=Nanosecond(0)) return Time(value(h), value(mi), value(s), value(ms), value(us), value(ns)) end # To allow any order/combination of Periods """ DateTime(periods::Period...) -> DateTime Construct a `DateTime` type by `Period` type parts. Arguments may be in any order. DateTime parts not provided will default to the value of `Dates.default(period)`. """ function DateTime(period::Period, periods::Period...) y = Year(1); m = Month(1); d = Day(1) h = Hour(0); mi = Minute(0); s = Second(0); ms = Millisecond(0) for p in (period, periods...) isa(p, Year) && (y = p::Year) isa(p, Month) && (m = p::Month) isa(p, Day) && (d = p::Day) isa(p, Hour) && (h = p::Hour) isa(p, Minute) && (mi = p::Minute) isa(p, Second) && (s = p::Second) isa(p, Millisecond) && (ms = p::Millisecond) end return DateTime(y, m, d, h, mi, s, ms) end """ Date(period::Period...) -> Date Construct a `Date` type by `Period` type parts. Arguments may be in any order. `Date` parts not provided will default to the value of `Dates.default(period)`. """ function Date(period::Period, periods::Period...) y = Year(1); m = Month(1); d = Day(1) for p in (period, periods...) isa(p, Year) && (y = p::Year) isa(p, Month) && (m = p::Month) isa(p, Day) && (d = p::Day) end return Date(y, m, d) end """ Time(period::TimePeriod...) -> Time Construct a `Time` type by `Period` type parts. Arguments may be in any order. `Time` parts not provided will default to the value of `Dates.default(period)`. """ function Time(period::TimePeriod, periods::TimePeriod...) h = Hour(0); mi = Minute(0); s = Second(0) ms = Millisecond(0); us = Microsecond(0); ns = Nanosecond(0) for p in (period, periods...) isa(p, Hour) && (h = p::Hour) isa(p, Minute) && (mi = p::Minute) isa(p, Second) && (s = p::Second) isa(p, Millisecond) && (ms = p::Millisecond) isa(p, Microsecond) && (us = p::Microsecond) isa(p, Nanosecond) && (ns = p::Nanosecond) end return Time(h, mi, s, ms, us, ns) end # Convenience constructor for DateTime from Date and Time """ DateTime(d::Date, t::Time) Construct a `DateTime` type by `Date` and `Time`. Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError`. !!! compat "Julia 1.1" This function requires at least Julia 1.1. ```jldoctest julia> d = Date(2018, 1, 1) 2018-01-01 julia> t = Time(8, 15, 42) 08:15:42 julia> DateTime(d, t) 2018-01-01T08:15:42 ``` """ function DateTime(dt::Date, t::Time) (microsecond(t) > 0 || nanosecond(t) > 0) && throw(InexactError(:DateTime, DateTime, t)) y, m, d = yearmonthday(dt) return DateTime(y, m, d, hour(t), minute(t), second(t), millisecond(t)) end # Fallback constructors DateTime(y, m=1, d=1, h=0, mi=0, s=0, ms=0, ampm::AMPM=TWENTYFOURHOUR) = DateTime(Int64(y), Int64(m), Int64(d), Int64(h), Int64(mi), Int64(s), Int64(ms), ampm) Date(y, m=1, d=1) = Date(Int64(y), Int64(m), Int64(d)) Time(h, mi=0, s=0, ms=0, us=0, ns=0, ampm::AMPM=TWENTYFOURHOUR) = Time(Int64(h), Int64(mi), Int64(s), Int64(ms), Int64(us), Int64(ns), ampm) # Traits, Equality Base.isfinite(::Union{Type{T}, T}) where {T<:TimeType} = true calendar(dt::DateTime) = ISOCalendar calendar(dt::Date) = ISOCalendar """ eps(::Type{DateTime}) -> Millisecond eps(::Type{Date}) -> Day eps(::Type{Time}) -> Nanosecond eps(::TimeType) -> Period Return the smallest unit value supported by the `TimeType`. # Examples ```jldoctest julia> eps(DateTime) 1 millisecond julia> eps(Date) 1 day julia> eps(Time) 1 nanosecond ``` """ Base.eps(::Union{Type{DateTime}, Type{Date}, Type{Time}, TimeType}) Base.eps(::Type{DateTime}) = Millisecond(1) Base.eps(::Type{Date}) = Day(1) Base.eps(::Type{Time}) = Nanosecond(1) Base.eps(::T) where T <: TimeType = eps(T)::Period # zero returns dt::T - dt::T Base.zero(::Type{DateTime}) = Millisecond(0) Base.zero(::Type{Date}) = Day(0) Base.zero(::Type{Time}) = Nanosecond(0) Base.zero(::T) where T <: TimeType = zero(T)::Period Base.typemax(::Union{DateTime, Type{DateTime}}) = DateTime(146138512, 12, 31, 23, 59, 59) Base.typemin(::Union{DateTime, Type{DateTime}}) = DateTime(-146138511, 1, 1, 0, 0, 0) Base.typemax(::Union{Date, Type{Date}}) = Date(252522163911149, 12, 31) Base.typemin(::Union{Date, Type{Date}}) = Date(-252522163911150, 1, 1) Base.typemax(::Union{Time, Type{Time}}) = Time(23, 59, 59, 999, 999, 999) Base.typemin(::Union{Time, Type{Time}}) = Time(0) # Date-DateTime promotion, isless, == Base.promote_rule(::Type{Date}, x::Type{DateTime}) = DateTime Base.isless(x::T, y::T) where {T<:TimeType} = isless(value(x), value(y)) Base.isless(x::TimeType, y::TimeType) = isless(promote(x, y)...) (==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y)) (==)(x::TimeType, y::TimeType) = (===)(promote(x, y)...) Base.min(x::AbstractTime) = x Base.max(x::AbstractTime) = x Base.minmax(x::AbstractTime) = (x, x) Base.hash(x::Time, h::UInt) = hash(hour(x), hash(minute(x), hash(second(x), hash(millisecond(x), hash(microsecond(x), hash(nanosecond(x), h)))))) Base.sleep(duration::Period) = sleep(toms(duration) / 1000) function Base.Timer(delay::Period; interval::Period=Second(0)) Timer(toms(delay) / 1000, interval=toms(interval) / 1000) end function Base.timedwait(testcb, timeout::Period; pollint::Period=Millisecond(100)) timedwait(testcb, toms(timeout) / 1000, pollint=toms(pollint) / 1000) end Base.OrderStyle(::Type{<:AbstractTime}) = Base.Ordered() Base.ArithmeticStyle(::Type{<:AbstractTime}) = Base.ArithmeticWraps() p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/periods.jl C������# This file is a part of Julia. License is MIT: https://julialang.org/license #Period types """ Dates.value(x::Period) -> Int64 For a given period, return the value associated with that period. For example, `value(Millisecond(10))` returns 10 as an integer. """ value(x::Period) = x.value # The default constructors for Periods work well in almost all cases # P(x) = new((convert(Int64,x)) # The following definitions are for Period-specific safety for period in (:Year, :Quarter, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond, :Microsecond, :Nanosecond) period_str = string(period) accessor_str = lowercase(period_str) # Convenience method for show() @eval _units(x::$period) = " " * $accessor_str * (abs(value(x)) == 1 ? "" : "s") # AbstractString parsing (mainly for IO code) @eval $period(x::AbstractString) = $period(Base.parse(Int64, x)) # The period type is printed when output, thus it already implies its own typeinfo @eval Base.typeinfo_implicit(::Type{$period}) = true # Period accessors typs = period in (:Microsecond, :Nanosecond) ? ["Time"] : period in (:Hour, :Minute, :Second, :Millisecond) ? ["Time", "DateTime"] : ["Date", "DateTime"] reference = period === :Week ? " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : "" for typ_str in typs @eval begin @doc """ $($period_str)(dt::$($typ_str)) -> $($period_str) The $($accessor_str) part of a $($typ_str) as a `$($period_str)`.$($reference) """ $period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt)) end end @eval begin @doc """ $($period_str)(v) Construct a `$($period_str)` object with the given `v` value. Input must be losslessly convertible to an [`Int64`](@ref). """ $period(v) end end #Print/show/traits Base.print(io::IO, x::Period) = print(io, value(x), _units(x)) Base.show(io::IO, ::MIME"text/plain", x::Period) = print(io, x) Base.show(io::IO, p::P) where {P<:Period} = print(io, P, '(', value(p), ')') Base.zero(::Union{Type{P},P}) where {P<:Period} = P(0) Base.one(::Union{Type{P},P}) where {P<:Period} = 1 # see #16116 Base.typemin(::Type{P}) where {P<:Period} = P(typemin(Int64)) Base.typemax(::Type{P}) where {P<:Period} = P(typemax(Int64)) Base.isfinite(::Union{Type{P}, P}) where {P<:Period} = true # Default values (as used by TimeTypes) """ default(p::Period) -> Period Return a sensible "default" value for the input Period by returning `T(1)` for Year, Month, and Day, and `T(0)` for Hour, Minute, Second, and Millisecond. """ function default end default(p::Union{T,Type{T}}) where {T<:DatePeriod} = T(1) default(p::Union{T,Type{T}}) where {T<:TimePeriod} = T(0) (-)(x::P) where {P<:Period} = P(-value(x)) ==(x::P, y::P) where {P<:Period} = value(x) == value(y) Base.isless(x::P, y::P) where {P<:Period} = isless(value(x), value(y)) # Period Arithmetic, grouped by dimensionality: for op in (:+, :-, :lcm, :gcd) @eval ($op)(x::P, y::P) where {P<:Period} = P(($op)(value(x), value(y))) end /(x::P, y::P) where {P<:Period} = /(value(x), value(y)) /(x::P, y::Real) where {P<:Period} = P(/(value(x), y)) div(x::P, y::P, r::RoundingMode) where {P<:Period} = div(value(x), value(y), r) div(x::P, y::Real, r::RoundingMode) where {P<:Period} = P(div(value(x), Int64(y), r)) for op in (:rem, :mod) @eval begin ($op)(x::P, y::P) where {P<:Period} = P(($op)(value(x), value(y))) ($op)(x::P, y::Real) where {P<:Period} = P(($op)(value(x), Int64(y))) end end (*)(x::P, y::Real) where {P<:Period} = P(value(x) * y) (*)(y::Real, x::Period) = x * y (*)(A::Period, B::AbstractArray) = Broadcast.broadcast_preserving_zero_d(*, A, B) (*)(A::AbstractArray, B::Period) = Broadcast.broadcast_preserving_zero_d(*, A, B) for op in (:(==), :isless, :/, :rem, :mod, :lcm, :gcd) @eval ($op)(x::Period, y::Period) = ($op)(promote(x, y)...) end div(x::Period, y::Period, r::RoundingMode) = div(promote(x, y)..., r) # intfuncs Base.gcdx(a::T, b::T) where {T<:Period} = ((g, x, y) = gcdx(value(a), value(b)); return T(g), x, y) Base.abs(a::T) where {T<:Period} = T(abs(value(a))) Base.sign(x::Period) = sign(value(x)) # return (next coarser period, conversion factor): coarserperiod(::Type{P}) where {P<:Period} = (P, 1) coarserperiod(::Type{Nanosecond}) = (Microsecond, 1000) coarserperiod(::Type{Microsecond}) = (Millisecond, 1000) coarserperiod(::Type{Millisecond}) = (Second, 1000) coarserperiod(::Type{Second}) = (Minute, 60) coarserperiod(::Type{Minute}) = (Hour, 60) coarserperiod(::Type{Hour}) = (Day, 24) coarserperiod(::Type{Day}) = (Week, 7) coarserperiod(::Type{Month}) = (Year, 12) # Stores multiple periods in greatest to least order by type, not values, # canonicalized to eliminate zero periods, merge equal period types, # and convert more-precise periods to less-precise periods when possible """ CompoundPeriod A `CompoundPeriod` is useful for expressing time periods that are not a fixed multiple of smaller periods. For example, "a year and a day" is not a fixed number of days, but can be expressed using a `CompoundPeriod`. In fact, a `CompoundPeriod` is automatically generated by addition of different period types, e.g. `Year(1) + Day(1)` produces a `CompoundPeriod` result. """ struct CompoundPeriod <: AbstractTime periods::Vector{Period} function CompoundPeriod(p::Vector{Period}) n = length(p) if n > 1 # We sort periods in decreasing order (rev = true) according to the length of # the period's type (by = tons โˆ˜ oneunit). We sort by type, not value, so that # we can merge equal types. # # This works by computing how many nanoseconds are in a single period, and sorting # by that. For example, (tons โˆ˜ oneunit)(Week(10)) = tons(oneunit(Week(10))) = # tons(Week(1)) โ‰ˆ 6.0e14, which is less than (tons โˆ˜ oneunit)(Month(-2)) โ‰ˆ 2.6e15 sort!(p, rev = true, by = tons โˆ˜ oneunit) # canonicalize p by merging equal period types and removing zeros i = j = 1 while j <= n k = j + 1 while k <= n && typeof(p[j]) == typeof(p[k]) p[j] += p[k] k += 1 end if !iszero(p[j]) p[i] = p[j] i += 1 end j = k end n = i - 1 # new length p = resize!(p, n) elseif n == 1 && value(p[1]) == 0 p = Period[] end return new(p) end end """ Dates.periods(::CompoundPeriod) -> Vector{Period} Return the `Vector` of `Period`s that comprise the given `CompoundPeriod`. !!! compat "Julia 1.7" This function requires Julia 1.7 or later. """ periods(x::CompoundPeriod) = x.periods """ CompoundPeriod(periods) -> CompoundPeriod Construct a `CompoundPeriod` from a `Vector` of `Period`s. All `Period`s of the same type will be added together. # Examples ```jldoctest julia> Dates.CompoundPeriod(Dates.Hour(12), Dates.Hour(13)) 25 hours julia> Dates.CompoundPeriod(Dates.Hour(-1), Dates.Minute(1)) -1 hour, 1 minute julia> Dates.CompoundPeriod(Dates.Month(1), Dates.Week(-2)) 1 month, -2 weeks julia> Dates.CompoundPeriod(Dates.Minute(50000)) 50000 minutes ``` """ CompoundPeriod(p::Vector{<:Period}) = CompoundPeriod(Vector{Period}(p)) CompoundPeriod(t::Time) = CompoundPeriod(Period[Hour(t), Minute(t), Second(t), Millisecond(t), Microsecond(t), Nanosecond(t)]) CompoundPeriod(p::Period...) = CompoundPeriod(Period[p...]) """ canonicalize(::CompoundPeriod) -> CompoundPeriod Reduces the `CompoundPeriod` into its canonical form by applying the following rules: * Any `Period` large enough be partially representable by a coarser `Period` will be broken into multiple `Period`s (eg. `Hour(30)` becomes `Day(1) + Hour(6)`) * `Period`s with opposite signs will be combined when possible (eg. `Hour(1) - Day(1)` becomes `-Hour(23)`) # Examples ```jldoctest julia> canonicalize(Dates.CompoundPeriod(Dates.Hour(12), Dates.Hour(13))) 1 day, 1 hour julia> canonicalize(Dates.CompoundPeriod(Dates.Hour(-1), Dates.Minute(1))) -59 minutes julia> canonicalize(Dates.CompoundPeriod(Dates.Month(1), Dates.Week(-2))) 1 month, -2 weeks julia> canonicalize(Dates.CompoundPeriod(Dates.Minute(50000))) 4 weeks, 6 days, 17 hours, 20 minutes ``` """ canonicalize(x::Period) = canonicalize(CompoundPeriod(x)) function canonicalize(x::CompoundPeriod) # canonicalize Periods by pushing "overflow" into a coarser period. p = x.periods n = length(p) if n > 0 pc = sizehint!(Period[], n) P = typeof(p[n]) v = value(p[n]) i = n - 1 while true Pc, f = coarserperiod(P) if i > 0 && typeof(p[i]) == P v += value(p[i]) i -= 1 end v0 = f == 1 ? v : rem(v, f) v0 != 0 && push!(pc, P(v0)) if v != v0 P = Pc v = div(v - v0, f) elseif i > 0 P = typeof(p[i]) v = value(p[i]) i -= 1 else break end end p = reverse!(pc) n = length(p) else return x end # reduce the amount of mixed positive/negative Periods. if n > 0 pc = sizehint!(Period[], n) i = n while i > 0 j = i # Determine sign of the largest period in this group which # can be converted into via coarserperiod. last = Union{} current = typeof(p[i]) while i > 0 && current != last if typeof(p[i]) == current i -= 1 end last, current = current, coarserperiod(current)[1] end s = sign(value(p[i + 1])) # Adjust all the periods in the group based upon the # largest period sign. P = typeof(p[j]) v = 0 while j > i Pc, f = coarserperiod(P) if j > 0 && typeof(p[j]) == P v += value(p[j]) j -= 1 end v0 = f == 1 ? v : mod(v, f * s) v0 != 0 && push!(pc, P(v0)) if v != v0 P = Pc v = div(v - v0, f) elseif j > 0 P = typeof(p[j]) v = 0 else break end end end p = reverse!(pc) end return CompoundPeriod(p) end Base.convert(::Type{CompoundPeriod}, x::Period) = CompoundPeriod(Period[x]) function Base.string(x::CompoundPeriod) if isempty(x.periods) return "empty period" else s = "" for p in x.periods s *= ", " * string(p) end return s[3:end] end end Base.show(io::IO,x::CompoundPeriod) = print(io, string(x)) Base.convert(::Type{T}, x::CompoundPeriod) where T<:Period = isconcretetype(T) ? sum(T, x.periods) : throw(MethodError(convert,(T,x))) # E.g. Year(1) + Day(1) (+)(x::Period,y::Period) = CompoundPeriod(Period[x, y]) (+)(x::CompoundPeriod, y::Period) = CompoundPeriod(vcat(x.periods, y)) (+)(y::Period, x::CompoundPeriod) = x + y (+)(x::CompoundPeriod, y::CompoundPeriod) = CompoundPeriod(vcat(x.periods, y.periods)) # E.g. Year(1) - Month(1) (-)(x::Period, y::Period) = CompoundPeriod(Period[x, -y]) (-)(x::CompoundPeriod, y::Period) = CompoundPeriod(vcat(x.periods, -y)) (-)(x::CompoundPeriod) = CompoundPeriod(-x.periods) (-)(y::Union{Period, CompoundPeriod}, x::CompoundPeriod) = (-x) + y GeneralPeriod = Union{Period, CompoundPeriod} (+)(x::GeneralPeriod) = x (==)(x::CompoundPeriod, y::Period) = x == CompoundPeriod(y) (==)(x::Period, y::CompoundPeriod) = y == x (==)(x::CompoundPeriod, y::CompoundPeriod) = canonicalize(x).periods == canonicalize(y).periods Base.isequal(x::CompoundPeriod, y::Period) = isequal(x, CompoundPeriod(y)) Base.isequal(x::Period, y::CompoundPeriod) = isequal(y, x) Base.isequal(x::CompoundPeriod, y::CompoundPeriod) = isequal(x.periods, y.periods) # Capture TimeType+-Period methods (+)(a::TimeType, b::Period, c::Period) = (+)(a, b + c) (+)(a::TimeType, b::Period, c::Period, d::Period...) = (+)((+)(a, b + c), d...) function (+)(x::TimeType, y::CompoundPeriod) for p in y.periods x += p end return x end (+)(x::CompoundPeriod, y::TimeType) = y + x function (-)(x::TimeType, y::CompoundPeriod) for p in y.periods x -= p end return x end # Fixed-value Periods (periods corresponding to a well-defined time interval, # as opposed to variable calendar intervals like Year). const FixedPeriod = Union{Week, Day, Hour, Minute, Second, Millisecond, Microsecond, Nanosecond} # like div but throw an error if remainder is nonzero function divexact(x, y) q, r = divrem(x, y) r == 0 || throw(InexactError(:divexact, Int, x/y)) return q end # TODO: this is needed to prevent undefined Period constructors from # hitting the deprecated construct-to-convert fallback. (::Type{T})(p::Period) where {T<:Period} = convert(T, p)::T # Conversions and promotion rules function define_conversions(periods) for i = eachindex(periods) T, n = periods[i] N = Int64(1) for j = (i - 1):-1:firstindex(periods) # less-precise periods Tc, nc = periods[j] N *= nc vmax = typemax(Int64) รท N vmin = typemin(Int64) รท N @eval function Base.convert(::Type{$T}, x::$Tc) $vmin โ‰ค value(x) โ‰ค $vmax || throw(InexactError(:convert, $T, x)) return $T(value(x) * $N) end end N = n for j = (i + 1):lastindex(periods) # more-precise periods Tc, nc = periods[j] @eval Base.convert(::Type{$T}, x::$Tc) = $T(divexact(value(x), $N)) @eval Base.promote_rule(::Type{$T}, ::Type{$Tc}) = $Tc N *= nc end end end define_conversions([(:Week, 7), (:Day, 24), (:Hour, 60), (:Minute, 60), (:Second, 1000), (:Millisecond, 1000), (:Microsecond, 1000), (:Nanosecond, 1)]) define_conversions([(:Year, 4), (:Quarter, 3), (:Month, 1)]) # fixed is not comparable to other periods, except when both are zero (#37459) const OtherPeriod = Union{Month, Quarter, Year} (==)(x::FixedPeriod, y::OtherPeriod) = iszero(x) & iszero(y) (==)(x::OtherPeriod, y::FixedPeriod) = y == x const zero_or_fixedperiod_seed = UInt === UInt64 ? 0x5b7fc751bba97516 : 0xeae0fdcb const nonzero_otherperiod_seed = UInt === UInt64 ? 0xe1837356ff2d2ac9 : 0x170d1b00 otherperiod_seed(x) = iszero(value(x)) ? zero_or_fixedperiod_seed : nonzero_otherperiod_seed # tons() will overflow for periods longer than ~300,000 years, implying a hash collision # which is relatively harmless given how infrequently such periods should appear Base.hash(x::FixedPeriod, h::UInt) = hash(tons(x), h + zero_or_fixedperiod_seed) # Overflow can also happen here for really long periods (~8e17 years) Base.hash(x::Year, h::UInt) = hash(12 * value(x), h + otherperiod_seed(x)) Base.hash(x::Quarter, h::UInt) = hash(3 * value(x), h + otherperiod_seed(x)) Base.hash(x::Month, h::UInt) = hash(value(x), h + otherperiod_seed(x)) function Base.hash(x::CompoundPeriod, h::UInt) isempty(x.periods) && return hash(0, h + zero_or_fixedperiod_seed) for p in x.periods h = hash(p, h) end return h end Base.isless(x::FixedPeriod, y::OtherPeriod) = throw(MethodError(isless, (x, y))) Base.isless(x::OtherPeriod, y::FixedPeriod) = throw(MethodError(isless, (x, y))) Base.isless(x::Period, y::CompoundPeriod) = CompoundPeriod(x) < y Base.isless(x::CompoundPeriod, y::Period) = x < CompoundPeriod(y) Base.isless(x::CompoundPeriod, y::CompoundPeriod) = tons(x) < tons(y) # truncating conversions to milliseconds, nanoseconds and days: # overflow can happen for periods longer than ~300,000 years toms(c::Nanosecond) = div(value(c), 1000000) toms(c::Microsecond) = div(value(c), 1000) toms(c::Millisecond) = value(c) toms(c::Second) = 1000 * value(c) toms(c::Minute) = 60000 * value(c) toms(c::Hour) = 3600000 * value(c) toms(c::Period) = 86400000 * days(c) toms(c::CompoundPeriod) = isempty(c.periods) ? 0.0 : Float64(sum(toms, c.periods)) tons(x) = toms(x) * 1000000 tons(x::Microsecond) = value(x) * 1000 tons(x::Nanosecond) = value(x) tons(c::CompoundPeriod) = isempty(c.periods) ? 0.0 : Float64(sum(tons, c.periods)) days(c::Millisecond) = div(value(c), 86400000) days(c::Second) = div(value(c), 86400) days(c::Minute) = div(value(c), 1440) days(c::Hour) = div(value(c), 24) days(c::Day) = value(c) days(c::Week) = 7 * value(c) days(c::Year) = 365.2425 * value(c) days(c::Quarter) = 91.310625 * value(c) days(c::Month) = 30.436875 * value(c) days(c::CompoundPeriod) = isempty(c.periods) ? 0.0 : Float64(sum(days, c.periods)) r���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/accessors.jlx������# This file is a part of Julia. License is MIT: https://julialang.org/license # Convert # of Rata Die days to proleptic Gregorian calendar y,m,d,w # Reference: http://mysite.verizon.net/aesir_research/date/date0.htm function yearmonthday(days) z = days + 306; h = 100z - 25; a = fld(h, 3652425); b = a - fld(a, 4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) d = c - div(153m - 457, 5); return m > 12 ? (y + 1, m - 12, d) : (y, m, d) end function year(days) z = days + 306; h = 100z - 25; a = fld(h, 3652425); b = a - fld(a, 4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) return m > 12 ? y + 1 : y end function yearmonth(days) z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) return m > 12 ? (y + 1, m - 12) : (y, m) end function month(days) z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) return m > 12 ? m - 12 : m end function monthday(days) z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) d = c - div(153m - 457, 5); return m > 12 ? (m - 12, d) : (m, d) end function day(days) z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) return c - div(153m - 457, 5) end # https://en.wikipedia.org/wiki/Talk:ISO_week_date#Algorithms const WEEK_INDEX = (15, 23, 3, 11) function week(days) w = div(abs(days - 1), 7) % 20871 c, w = divrem((w + (w >= 10435)), 5218) w = (w * 28 + WEEK_INDEX[c + 1]) % 1461 return div(w, 28) + 1 end function quarter(days) m = month(days) return m < 4 ? 1 : m < 7 ? 2 : m < 10 ? 3 : 4 end # Accessor functions value(dt::TimeType) = dt.instant.periods.value value(t::Time) = t.instant.value days(dt::Date) = value(dt) days(dt::DateTime) = fld(value(dt), 86400000) year(dt::TimeType) = year(days(dt)) quarter(dt::TimeType) = quarter(days(dt)) month(dt::TimeType) = month(days(dt)) week(dt::TimeType) = week(days(dt)) day(dt::TimeType) = day(days(dt)) hour(dt::DateTime) = mod(fld(value(dt), 3600000), 24) minute(dt::DateTime) = mod(fld(value(dt), 60000), 60) second(dt::DateTime) = mod(fld(value(dt), 1000), 60) millisecond(dt::DateTime) = mod(value(dt), 1000) hour(t::Time) = mod(fld(value(t), 3600000000000), Int64(24)) minute(t::Time) = mod(fld(value(t), 60000000000), Int64(60)) second(t::Time) = mod(fld(value(t), 1000000000), Int64(60)) millisecond(t::Time) = mod(fld(value(t), Int64(1000000)), Int64(1000)) microsecond(t::Time) = mod(fld(value(t), Int64(1000)), Int64(1000)) nanosecond(t::Time) = mod(value(t), Int64(1000)) dayofmonth(dt::TimeType) = day(dt) yearmonth(dt::TimeType) = yearmonth(days(dt)) monthday(dt::TimeType) = monthday(days(dt)) yearmonthday(dt::TimeType) = yearmonthday(days(dt)) # Documentation for exported accessors for func in (:year, :month, :quarter) name = string(func) @eval begin @doc """ $($name)(dt::TimeType) -> Int64 The $($name) of a `Date` or `DateTime` as an [`Int64`](@ref). """ $func(dt::TimeType) end end """ week(dt::TimeType) -> Int64 Return the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date) of a `Date` or `DateTime` as an [`Int64`](@ref). Note that the first week of a year is the week that contains the first Thursday of the year, which can result in dates prior to January 4th being in the last week of the previous year. For example, `week(Date(2005, 1, 1))` is the 53rd week of 2004. # Examples ```jldoctest julia> week(Date(1989, 6, 22)) 25 julia> week(Date(2005, 1, 1)) 53 julia> week(Date(2004, 12, 31)) 53 ``` """ week(dt::TimeType) for func in (:day, :dayofmonth) name = string(func) @eval begin @doc """ $($name)(dt::TimeType) -> Int64 The day of month of a `Date` or `DateTime` as an [`Int64`](@ref). """ $func(dt::TimeType) end end """ hour(dt::DateTime) -> Int64 The hour of day of a `DateTime` as an [`Int64`](@ref). """ hour(dt::DateTime) for func in (:minute, :second, :millisecond) name = string(func) @eval begin @doc """ $($name)(dt::DateTime) -> Int64 The $($name) of a `DateTime` as an [`Int64`](@ref). """ $func(dt::DateTime) end end for parts in (["year", "month"], ["month", "day"], ["year", "month", "day"]) name = join(parts) func = Symbol(name) @eval begin @doc """ $($name)(dt::TimeType) -> ($(join(repeated(Int64, length($parts)), ", "))) Simultaneously return the $(join($parts, ", ", " and ")) parts of a `Date` or `DateTime`. """ $func(dt::TimeType) end end for func in (:hour, :minute, :second, :millisecond, :microsecond, :nanosecond) name = string(func) @eval begin @doc """ $($name)(t::Time) -> Int64 The $($name) of a `Time` as an [`Int64`](@ref). """ $func(t::Time) end end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/query.jlฤ1������# This file is a part of Julia. License is MIT: https://julialang.org/license # Date Locales struct DateLocale months::Vector{String} months_abbr::Vector{String} days_of_week::Vector{String} days_of_week_abbr::Vector{String} month_value::Dict{String, Int64} month_abbr_value::Dict{String, Int64} day_of_week_value::Dict{String, Int64} day_of_week_abbr_value::Dict{String, Int64} end function locale_dict(names::Vector{<:AbstractString}) result = Dict{String, Int}() # Keep both the common case-sensitive version of the name and an all lowercase # version for case-insensitive matches. Storing both allows us to avoid using the # lowercase function during parsing. for i in 1:length(names) name = names[i] result[name] = i result[lowercase(name)] = i end return result end """ DateLocale(["January", "February",...], ["Jan", "Feb",...], ["Monday", "Tuesday",...], ["Mon", "Tue",...]) Create a locale for parsing or printing textual month names. Arguments: - `months::Vector`: 12 month names - `months_abbr::Vector`: 12 abbreviated month names - `days_of_week::Vector`: 7 days of week - `days_of_week_abbr::Vector`: 7 days of week abbreviated This object is passed as the last argument to `tryparsenext` and `format` methods defined for each `AbstractDateToken` type. """ function DateLocale(months::Vector, months_abbr::Vector, days_of_week::Vector, days_of_week_abbr::Vector) DateLocale( months, months_abbr, days_of_week, days_of_week_abbr, locale_dict(months), locale_dict(months_abbr), locale_dict(days_of_week), locale_dict(days_of_week_abbr), ) end const ENGLISH = DateLocale( ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], ) const LOCALES = Dict{String, DateLocale}("english" => ENGLISH) for (fn, field) in zip( [:dayname_to_value, :dayabbr_to_value, :monthname_to_value, :monthabbr_to_value], [:day_of_week_value, :day_of_week_abbr_value, :month_value, :month_abbr_value], ) @eval @inline function $fn(word::AbstractString, locale::DateLocale) # Maximize performance by attempting to avoid the use of `lowercase` and trying # a case-sensitive lookup first value = get(locale.$field, word, 0) if value == 0 value = get(locale.$field, lowercase(word), 0) end value end end # Date functions ### Core query functions # Monday = 1....Sunday = 7 dayofweek(days) = mod1(days, 7) # Number of days in year """ daysinyear(dt::TimeType) -> Int Return 366 if the year of `dt` is a leap year, otherwise return 365. # Examples ```jldoctest julia> daysinyear(1999) 365 julia> daysinyear(2000) 366 ``` """ daysinyear(y) = 365 + isleapyear(y) # Day of the year const MONTHDAYS = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334) dayofyear(y, m, d) = MONTHDAYS[m] + d + (m > 2 && isleapyear(y)) ### Days of the Week """ dayofweek(dt::TimeType) -> Int64 Return the day of the week as an [`Int64`](@ref) with `1 = Monday, 2 = Tuesday, etc.`. # Examples ```jldoctest julia> dayofweek(Date("2000-01-01")) 6 ``` """ dayofweek(dt::TimeType) = dayofweek(days(dt)) const Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = 1, 2, 3, 4, 5, 6, 7 const Mon, Tue, Wed, Thu, Fri, Sat, Sun = 1, 2, 3, 4, 5, 6, 7 for (ii, day_ind, short_day, long_day) in ((1, "first", :Mon, :Monday), (2, "second", :Tue, :Tuesday), (3, "third", :Wed, :Wednesday), (4, "fourth", :Thu, :Thursday), (5, "fifth", :Fri, :Friday), (6, "sixth", :Sat, :Saturday), (7, "seventh", :Sun, :Sunday)) short_name = string(short_day) long_name = string(long_day) name_ind = day_ind ind_str = string(ii) @eval begin @doc """ $($long_name) $($short_name) The $($name_ind) day of the week. # Examples ```jldoctest julia> $($long_name) $($ind_str) julia> $($short_name) $($ind_str) ``` """ ($long_day, $short_day) end end dayname(day::Integer, locale::DateLocale) = locale.days_of_week[day] dayabbr(day::Integer, locale::DateLocale) = locale.days_of_week_abbr[day] dayname(day::Integer; locale::AbstractString="english") = dayname(day, LOCALES[locale]) dayabbr(day::Integer; locale::AbstractString="english") = dayabbr(day, LOCALES[locale]) """ dayname(dt::TimeType; locale="english") -> String dayname(day::Integer; locale="english") -> String Return the full day name corresponding to the day of the week of the `Date` or `DateTime` in the given `locale`. Also accepts `Integer`. # Examples ```jldoctest julia> dayname(Date("2000-01-01")) "Saturday" julia> dayname(4) "Thursday" ``` """ function dayname(dt::TimeType;locale::AbstractString="english") dayname(dayofweek(dt); locale=locale) end """ dayabbr(dt::TimeType; locale="english") -> String dayabbr(day::Integer; locale="english") -> String Return the abbreviated name corresponding to the day of the week of the `Date` or `DateTime` in the given `locale`. Also accepts `Integer`. # Examples ```jldoctest julia> dayabbr(Date("2000-01-01")) "Sat" julia> dayabbr(3) "Wed" ``` """ function dayabbr(dt::TimeType;locale::AbstractString="english") dayabbr(dayofweek(dt); locale=locale) end # Convenience methods for each day ismonday(dt::TimeType) = dayofweek(dt) == Mon istuesday(dt::TimeType) = dayofweek(dt) == Tue iswednesday(dt::TimeType) = dayofweek(dt) == Wed isthursday(dt::TimeType) = dayofweek(dt) == Thu isfriday(dt::TimeType) = dayofweek(dt) == Fri issaturday(dt::TimeType) = dayofweek(dt) == Sat issunday(dt::TimeType) = dayofweek(dt) == Sun # i.e. 1st Monday? 2nd Monday? 3rd Wednesday? 5th Sunday? """ dayofweekofmonth(dt::TimeType) -> Int For the day of week of `dt`, return which number it is in `dt`'s month. So if the day of the week of `dt` is Monday, then `1 = First Monday of the month, 2 = Second Monday of the month, etc.` In the range 1:5. # Examples ```jldoctest julia> dayofweekofmonth(Date("2000-02-01")) 1 julia> dayofweekofmonth(Date("2000-02-08")) 2 julia> dayofweekofmonth(Date("2000-02-15")) 3 ``` """ function dayofweekofmonth(dt::TimeType) d = day(dt) return d < 8 ? 1 : d < 15 ? 2 : d < 22 ? 3 : d < 29 ? 4 : 5 end # Total number of a day of week in the month # e.g. are there 4 or 5 Mondays in this month? const TWENTYNINE = BitSet([1, 8, 15, 22, 29]) const THIRTY = BitSet([1, 2, 8, 9, 15, 16, 22, 23, 29, 30]) const THIRTYONE = BitSet([1, 2, 3, 8, 9, 10, 15, 16, 17, 22, 23, 24, 29, 30, 31]) """ daysofweekinmonth(dt::TimeType) -> Int For the day of week of `dt`, return the total number of that day of the week in `dt`'s month. Returns 4 or 5. Useful in temporal expressions for specifying the last day of a week in a month by including `dayofweekofmonth(dt) == daysofweekinmonth(dt)` in the adjuster function. # Examples ```jldoctest julia> daysofweekinmonth(Date("2005-01-01")) 5 julia> daysofweekinmonth(Date("2005-01-04")) 4 ``` """ function daysofweekinmonth(dt::TimeType) y, m, d = yearmonthday(dt) ld = daysinmonth(y, m) return ld == 28 ? 4 : ld == 29 ? ((d in TWENTYNINE) ? 5 : 4) : ld == 30 ? ((d in THIRTY) ? 5 : 4) : (d in THIRTYONE) ? 5 : 4 end ### Months """ January The first month of the year. # Examples ```jldoctest julia> January 1 ``` """ const January = 1 """ Jan Abbreviation for [`January`](@ref). # Examples ```jldoctest julia> Jan 1 ``` """ const Jan = 1 """ February The second month of the year. # Examples ```jldoctest julia> February 2 ``` """ const February = 2 """ Feb Abbreviation for [`February`](@ref). # Examples ```jldoctest julia> Feb 2 ``` """ const Feb = 2 """ March The third month of the year. # Examples ```jldoctest julia> March 3 ``` """ const March = 3 """ Mar Abbreviation for [`March`](@ref). # Examples ```jldoctest julia> Mar 3 ``` """ const Mar = 3 """ April The fourth month of the year. # Examples ```jldoctest julia> April 4 ``` """ const April = 4 """ Apr Abbreviation for [`April`](@ref). # Examples ```jldoctest julia> Apr 4 ``` """ const Apr = 4 """ May The fifth month of the year. # Examples ```jldoctest julia> May 5 ``` """ const May = 5 """ June The sixth month of the year. # Examples ```jldoctest julia> June 6 ``` """ const June = 6 """ Jun Abbreviation for [`June`](@ref). # Examples ```jldoctest julia> Jun 6 ``` """ const Jun = 6 """ July The seventh month of the year. # Examples ```jldoctest julia> July 7 ``` """ const July = 7 """ Jul Abbreviation for [`July`](@ref). # Examples ```jldoctest julia> Jul 7 ``` """ const Jul = 7 """ August The eighth month of the year. # Examples ```jldoctest julia> August 8 ``` """ const August = 8 """ Aug Abbreviation for [`August`](@ref). # Examples ```jldoctest julia> Aug 8 ``` """ const Aug = 8 """ September The ninth month of the year. # Examples ```jldoctest julia> September 9 ``` """ const September = 9 """ Sep Abbreviation for [`September`](@ref). # Examples ```jldoctest julia> Sep 9 ``` """ const Sep = 9 """ October The tenth month of the year. # Examples ```jldoctest julia> October 10 ``` """ const October = 10 """ Oct Abbreviation for [`October`](@ref). # Examples ```jldoctest julia> Oct 10 ``` """ const Oct = 10 """ November The eleventh month of the year. # Examples ```jldoctest julia> November 11 ``` """ const November = 11 """ Nov Abbreviation for [`November`](@ref). # Examples ```jldoctest julia> Nov 11 ``` """ const Nov = 11 """ December The last month of the year. # Examples ```jldoctest julia> December 12 ``` """ const December = 12 """ Dec Abbreviation for [`December`](@ref). # Examples ```jldoctest julia> Dec 12 ``` """ const Dec = 12 monthname(month::Integer, locale::DateLocale) = locale.months[month] monthabbr(month::Integer, locale::DateLocale) = locale.months_abbr[month] monthname(month::Integer; locale::AbstractString="english") = monthname(month, LOCALES[locale]) monthabbr(month::Integer; locale::AbstractString="english") = monthabbr(month, LOCALES[locale]) """ monthname(dt::TimeType; locale="english") -> String monthname(month::Integer, locale="english") -> String Return the full name of the month of the `Date` or `DateTime` or `Integer` in the given `locale`. # Examples ```jldoctest julia> monthname(Date("2005-01-04")) "January" julia> monthname(2) "February" ``` """ function monthname(dt::TimeType; locale::AbstractString="english") monthname(month(dt); locale=locale) end """ monthabbr(dt::TimeType; locale="english") -> String monthabbr(month::Integer, locale="english") -> String Return the abbreviated month name of the `Date` or `DateTime` or `Integer` in the given `locale`. # Examples ```jldoctest julia> monthabbr(Date("2005-01-04")) "Jan" julia> monthabbr(2) "Feb" ``` """ function monthabbr(dt::TimeType; locale::AbstractString="english") monthabbr(month(dt); locale=locale) end """ daysinmonth(dt::TimeType) -> Int Return the number of days in the month of `dt`. Value will be 28, 29, 30, or 31. # Examples ```jldoctest julia> daysinmonth(Date("2000-01")) 31 julia> daysinmonth(Date("2001-02")) 28 julia> daysinmonth(Date("2000-02")) 29 ``` """ daysinmonth(dt::TimeType) = ((y, m) = yearmonth(dt); return daysinmonth(y, m)) ### Years """ isleapyear(dt::TimeType) -> Bool Return `true` if the year of `dt` is a leap year. # Examples ```jldoctest julia> isleapyear(Date("2004")) true julia> isleapyear(Date("2005")) false ``` """ isleapyear(dt::TimeType) = isleapyear(year(dt)) """ dayofyear(dt::TimeType) -> Int Return the day of the year for `dt` with January 1st being day 1. """ dayofyear(dt::TimeType) = ((y, m, d) = yearmonthday(dt); return dayofyear(y, m, d)) daysinyear(dt::TimeType) = 365 + isleapyear(dt) ### Quarters """ quarterofyear(dt::TimeType) -> Int Return the quarter that `dt` resides in. Range of value is 1:4. """ quarterofyear(dt::TimeType) = quarter(dt) const QUARTERDAYS = (0, 31, 59, 0, 30, 61, 0, 31, 62, 0, 31, 61) """ dayofquarter(dt::TimeType) -> Int Return the day of the current quarter of `dt`. Range of value is 1:92. """ function dayofquarter(dt::TimeType) (y, m, d) = yearmonthday(dt) return QUARTERDAYS[m] + d + (m == 3 && isleapyear(y)) end s���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/arithmetic.jlธ������# This file is a part of Julia. License is MIT: https://julialang.org/license # Instant arithmetic (+)(x::Instant) = x (-)(x::T, y::T) where {T<:Instant} = x.periods - y.periods # TimeType arithmetic (+)(x::TimeType) = x (-)(x::T, y::T) where {T<:TimeType} = x.instant - y.instant (-)(x::T, y::T) where {T<:AbstractDateTime} = x.instant - y.instant (-)(x::AbstractDateTime, y::AbstractDateTime) = -(promote(x, y)...) # Date-Time arithmetic """ dt::Date + t::Time -> DateTime The addition of a `Date` with a `Time` produces a `DateTime`. The hour, minute, second, and millisecond parts of the `Time` are used along with the year, month, and day of the `Date` to create the new `DateTime`. Non-zero microseconds or nanoseconds in the `Time` type will result in an `InexactError` being thrown. """ (+)(dt::Date, t::Time) = DateTime(dt ,t) (+)(t::Time, dt::Date) = DateTime(dt, t) # TimeType-Year arithmetic function (+)(dt::DateTime, y::Year) oy, m, d = yearmonthday(dt); ny = oy + value(y); ld = daysinmonth(ny, m) return DateTime(ny, m, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt)) end function (+)(dt::Date,y::Year) oy, m, d = yearmonthday(dt); ny = oy + value(y); ld = daysinmonth(ny, m) return Date(ny, m, d <= ld ? d : ld) end function (-)(dt::DateTime,y::Year) oy, m, d = yearmonthday(dt); ny = oy - value(y); ld = daysinmonth(ny, m) return DateTime(ny, m, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt)) end function (-)(dt::Date,y::Year) oy, m, d = yearmonthday(dt); ny = oy - value(y); ld = daysinmonth(ny, m) return Date(ny, m, d <= ld ? d : ld) end # TimeType-Month arithmetic # monthwrap adds two months with wraparound behavior (i.e. 12 + 1 == 1) monthwrap(m1, m2) = (v = mod1(m1 + m2, 12); return v < 0 ? 12 + v : v) # yearwrap takes a starting year/month and a month to add and returns # the resulting year with wraparound behavior (i.e. 2000-12 + 1 == 2001) yearwrap(y, m1, m2) = y + fld(m1 + m2 - 1, 12) function (+)(dt::DateTime, z::Month) y,m,d = yearmonthday(dt) ny = yearwrap(y, m, value(z)) mm = monthwrap(m, value(z)); ld = daysinmonth(ny, mm) return DateTime(ny, mm, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt)) end function (+)(dt::Date, z::Month) y,m,d = yearmonthday(dt) ny = yearwrap(y, m, value(z)) mm = monthwrap(m, value(z)); ld = daysinmonth(ny, mm) return Date(ny, mm, d <= ld ? d : ld) end function (-)(dt::DateTime, z::Month) y,m,d = yearmonthday(dt) ny = yearwrap(y, m, -value(z)) mm = monthwrap(m, -value(z)); ld = daysinmonth(ny, mm) return DateTime(ny, mm, d <= ld ? d : ld, hour(dt), minute(dt), second(dt), millisecond(dt)) end function (-)(dt::Date, z::Month) y,m,d = yearmonthday(dt) ny = yearwrap(y, m, -value(z)) mm = monthwrap(m, -value(z)); ld = daysinmonth(ny, mm) return Date(ny, mm, d <= ld ? d : ld) end (+)(x::Date, y::Quarter) = x + Month(y) (-)(x::Date, y::Quarter) = x - Month(y) (+)(x::DateTime, y::Quarter) = x + Month(y) (-)(x::DateTime, y::Quarter) = x - Month(y) (+)(x::Date, y::Week) = return Date(UTD(value(x) + 7 * value(y))) (-)(x::Date, y::Week) = return Date(UTD(value(x) - 7 * value(y))) (+)(x::Date, y::Day) = return Date(UTD(value(x) + value(y))) (-)(x::Date, y::Day) = return Date(UTD(value(x) - value(y))) (+)(x::DateTime, y::Period) = return DateTime(UTM(value(x) + toms(y))) (-)(x::DateTime, y::Period) = return DateTime(UTM(value(x) - toms(y))) (+)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) + tons(y))) (-)(x::Time, y::TimePeriod) = return Time(Nanosecond(value(x) - tons(y))) (+)(y::Period, x::TimeType) = x + y # Missing support (+)(x::AbstractTime, y::Missing) = missing (+)(x::Missing, y::AbstractTime) = missing (-)(x::AbstractTime, y::Missing) = missing (-)(x::Missing, y::AbstractTime) = missing # AbstractArray{TimeType}, AbstractArray{TimeType} (-)(x::OrdinalRange{T}, y::OrdinalRange{T}) where {T<:TimeType} = Vector(x) - Vector(y) (-)(x::AbstractRange{T}, y::AbstractRange{T}) where {T<:TimeType} = Vector(x) - Vector(y) # Allow dates, times, and time zones to broadcast as unwrapped scalars Base.Broadcast.broadcastable(x::AbstractTime) = Ref(x) Base.Broadcast.broadcastable(x::TimeZone) = Ref(x) t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/conversions.jl&������# This file is a part of Julia. License is MIT: https://julialang.org/license # Conversion/Promotion """ Date(dt::DateTime) -> Date Convert a `DateTime` to a `Date`. The hour, minute, second, and millisecond parts of the `DateTime` are truncated, so only the year, month and day parts are used in construction. """ Date(dt::TimeType) = convert(Date, dt) """ DateTime(dt::Date) -> DateTime Convert a `Date` to a `DateTime`. The hour, minute, second, and millisecond parts of the new `DateTime` are assumed to be zero. """ DateTime(dt::TimeType) = convert(DateTime, dt) """ Time(dt::DateTime) -> Time Convert a `DateTime` to a `Time`. The hour, minute, second, and millisecond parts of the `DateTime` are used to create the new `Time`. Microsecond and nanoseconds are zero by default. """ Time(dt::DateTime) = convert(Time, dt) Base.convert(::Type{DateTime}, dt::Date) = DateTime(UTM(value(dt) * 86400000)) Base.convert(::Type{Date}, dt::DateTime) = Date(UTD(days(dt))) Base.convert(::Type{Time}, dt::DateTime) = Time(Nanosecond((value(dt) % 86400000) * 1000000)) Base.convert(::Type{DateTime},x::Millisecond) = DateTime(Dates.UTInstant(x)) # Converts Rata Die milliseconds to a DateTime Base.convert(::Type{Millisecond},dt::DateTime) = Millisecond(value(dt)) # Converts DateTime to Rata Die milliseconds Base.convert(::Type{Date},x::Day) = Date(Dates.UTInstant(x)) # Converts Rata Die days to a Date Base.convert(::Type{Day},dt::Date) = Day(value(dt)) # Converts Date to Rata Die days ### External Conversions const UNIXEPOCH = value(DateTime(1970)) #Rata Die milliseconds for 1970-01-01T00:00:00 """ unix2datetime(x) -> DateTime Take the number of seconds since unix epoch `1970-01-01T00:00:00` and convert to the corresponding `DateTime`. """ function unix2datetime(x) # Rounding should match `now` below rata = UNIXEPOCH + trunc(Int64, Int64(1000) * x) return DateTime(UTM(rata)) end """ datetime2unix(dt::DateTime) -> Float64 Take the given `DateTime` and return the number of seconds since the unix epoch `1970-01-01T00:00:00` as a [`Float64`](@ref). """ datetime2unix(dt::DateTime) = (value(dt) - UNIXEPOCH) / 1000.0 """ now() -> DateTime Return a `DateTime` corresponding to the user's system time including the system timezone locale. """ function now() tv = Libc.TimeVal() tm = Libc.TmStruct(tv.sec) return DateTime(tm.year + 1900, tm.month + 1, tm.mday, tm.hour, tm.min, tm.sec, div(tv.usec, 1000)) end """ today() -> Date Return the date portion of `now()`. """ today() = Date(now()) """ now(::Type{UTC}) -> DateTime Return a `DateTime` corresponding to the user's system time as UTC/GMT. For other time zones, see the TimeZones.jl package. # Example ```julia julia> now(UTC) 2023-01-04T10:52:24.864 ``` """ now(::Type{UTC}) = unix2datetime(time()) """ rata2datetime(days) -> DateTime Take the number of Rata Die days since epoch `0000-12-31T00:00:00` and return the corresponding `DateTime`. """ rata2datetime(days) = DateTime(yearmonthday(days)...) """ datetime2rata(dt::TimeType) -> Int64 Return the number of Rata Die days since epoch from the given `Date` or `DateTime`. """ datetime2rata(dt::TimeType) = days(dt) # Julian conversions const JULIANEPOCH = value(DateTime(-4713, 11, 24, 12)) """ julian2datetime(julian_days) -> DateTime Take the number of Julian calendar days since epoch `-4713-11-24T12:00:00` and return the corresponding `DateTime`. """ function julian2datetime(f) rata = JULIANEPOCH + round(Int64, Int64(86400000) * f) return DateTime(UTM(rata)) end """ datetime2julian(dt::DateTime) -> Float64 Take the given `DateTime` and return the number of Julian calendar days since the julian epoch `-4713-11-24T12:00:00` as a [`Float64`](@ref). """ datetime2julian(dt::DateTime) = (value(dt) - JULIANEPOCH) / 86400000.0 o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/ranges.jl� ������# This file is a part of Julia. License is MIT: https://julialang.org/license # Date/DateTime Ranges StepRange{<:Dates.DatePeriod,<:Real}(start, step, stop) = throw(ArgumentError("must specify step as a Period when constructing Dates ranges")) Base.:(:)(a::T, b::T) where {T<:Date} = (:)(a, Day(1), b) # Given a start and end date, how many steps/periods are in between guess(a::DateTime, b::DateTime, c) = floor(Int64, (Int128(value(b)) - Int128(value(a))) / toms(c)) guess(a::Date, b::Date, c) = Int64(div(value(b - a), days(c))) len(a::Time, b::Time, c) = Int64(div(value(b - a), tons(c))) function len(a, b, c) lo, hi, st = min(a, b), max(a, b), abs(c) i = guess(a, b, c) v = lo + st * i prev = v # Ensure `v` does not overflow while v <= hi && prev <= v prev = v v += st i += 1 end return i - 1 end Base.length(r::StepRange{<:TimeType}) = isempty(r) ? Int64(0) : len(r.start, r.stop, r.step) + 1 # Period ranges hook into Int64 overflow detection Base.length(r::StepRange{<:Period}) = length(StepRange(value(r.start), value(r.step), value(r.stop))) Base.checked_length(r::StepRange{<:Period}) = Base.checked_length(StepRange(value(r.start), value(r.step), value(r.stop))) # Overload Base.steprange_last because `step::Period` may be a variable amount of time (e.g. for Month and Year) function Base.steprange_last(start::T, step, stop) where T<:TimeType if isa(step, AbstractFloat) throw(ArgumentError("StepRange should not be used with floating point")) end z = zero(step) step == z && throw(ArgumentError("step cannot be zero")) if stop == start last = stop else if (step > z) != (stop > start) last = Base.steprange_last_empty(start, step, stop) else diff = stop - start if (diff > zero(diff)) != (stop > start) throw(OverflowError("Difference between stop and start overflowed")) end remain = stop - (start + step * len(start, stop, step)) last = stop - remain end end return last end import Base.in function in(x::T, r::StepRange{T}) where T<:TimeType n = len(first(r), x, step(r)) + 1 n >= 1 && n <= length(r) && r[n] == x end Base.iterate(r::StepRange{<:TimeType}) = length(r) <= 0 ? nothing : (r.start, (length(r), 1)) Base.iterate(r::StepRange{<:TimeType}, (l, i)) = l <= i ? nothing : (r.start + r.step * i, (l, i + 1)) +(x::Period, r::AbstractRange{<:TimeType}) = (x + first(r)):step(r):(x + last(r)) +(r::AbstractRange{<:TimeType}, x::Period) = x + r -(r::AbstractRange{<:TimeType}, x::Period) = (first(r)-x):step(r):(last(r)-x) *(x::Period, r::AbstractRange{<:Real}) = (x * first(r)):(x * step(r)):(x * last(r)) *(r::AbstractRange{<:Real}, x::Period) = x * r /(r::AbstractRange{<:P}, x::P) where {P<:Period} = (first(r)/x):(step(r)/x):(last(r)/x) # Combinations of types and periods for which the range step is regular Base.RangeStepStyle(::Type{<:OrdinalRange{<:TimeType, <:FixedPeriod}}) = Base.RangeStepRegular() r���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/adjusters.jlฝ5������# This file is a part of Julia. License is MIT: https://julialang.org/license ### truncation Base.trunc(dt::Date, p::Type{Year}) = Date(UTD(totaldays(year(dt), 1, 1))) Base.trunc(dt::Date, p::Type{Quarter}) = firstdayofquarter(dt) Base.trunc(dt::Date, p::Type{Month}) = firstdayofmonth(dt) Base.trunc(dt::Date, p::Type{Day}) = dt Base.trunc(dt::DateTime, p::Type{Year}) = DateTime(trunc(Date(dt), Year)) Base.trunc(dt::DateTime, p::Type{Quarter}) = DateTime(trunc(Date(dt), Quarter)) Base.trunc(dt::DateTime, p::Type{Month}) = DateTime(trunc(Date(dt), Month)) Base.trunc(dt::DateTime, p::Type{Day}) = DateTime(Date(dt)) Base.trunc(dt::DateTime, p::Type{Hour}) = dt - Minute(dt) - Second(dt) - Millisecond(dt) Base.trunc(dt::DateTime, p::Type{Minute}) = dt - Second(dt) - Millisecond(dt) Base.trunc(dt::DateTime, p::Type{Second}) = dt - Millisecond(dt) Base.trunc(dt::DateTime, p::Type{Millisecond}) = dt Base.trunc(t::Time, p::Type{Hour}) = Time(Hour(t)) Base.trunc(t::Time, p::Type{Minute}) = Time(Hour(t), Minute(t)) Base.trunc(t::Time, p::Type{Second}) = Time(Hour(t), Minute(t), Second(t)) Base.trunc(t::Time, p::Type{Millisecond}) = t - Microsecond(t) - Nanosecond(t) Base.trunc(t::Time, p::Type{Microsecond}) = t - Nanosecond(t) Base.trunc(t::Time, p::Type{Nanosecond}) = t """ trunc(dt::TimeType, ::Type{Period}) -> TimeType Truncates the value of `dt` according to the provided `Period` type. # Examples ```jldoctest julia> trunc(DateTime("1996-01-01T12:30:00"), Day) 1996-01-01T00:00:00 ``` """ Dates.trunc(::Dates.TimeType, ::Type{Dates.Period}) # Adjusters """ firstdayofweek(dt::TimeType) -> TimeType Adjusts `dt` to the Monday of its week. # Examples ```jldoctest julia> firstdayofweek(DateTime("1996-01-05T12:30:00")) 1996-01-01T00:00:00 ``` """ function firstdayofweek end firstdayofweek(dt::Date) = Date(UTD(value(dt) - dayofweek(dt) + 1)) firstdayofweek(dt::DateTime) = DateTime(firstdayofweek(Date(dt))) """ lastdayofweek(dt::TimeType) -> TimeType Adjusts `dt` to the Sunday of its week. # Examples ```jldoctest julia> lastdayofweek(DateTime("1996-01-05T12:30:00")) 1996-01-07T00:00:00 ``` """ function lastdayofweek end lastdayofweek(dt::Date) = Date(UTD(value(dt) + (7 - dayofweek(dt)))) lastdayofweek(dt::DateTime) = DateTime(lastdayofweek(Date(dt))) """ firstdayofmonth(dt::TimeType) -> TimeType Adjusts `dt` to the first day of its month. # Examples ```jldoctest julia> firstdayofmonth(DateTime("1996-05-20")) 1996-05-01T00:00:00 ``` """ function firstdayofmonth end firstdayofmonth(dt::Date) = Date(UTD(value(dt) - day(dt) + 1)) firstdayofmonth(dt::DateTime) = DateTime(firstdayofmonth(Date(dt))) """ lastdayofmonth(dt::TimeType) -> TimeType Adjusts `dt` to the last day of its month. # Examples ```jldoctest julia> lastdayofmonth(DateTime("1996-05-20")) 1996-05-31T00:00:00 ``` """ function lastdayofmonth end function lastdayofmonth(dt::Date) y, m, d = yearmonthday(dt) return Date(UTD(value(dt) + daysinmonth(y, m) - d)) end lastdayofmonth(dt::DateTime) = DateTime(lastdayofmonth(Date(dt))) """ firstdayofyear(dt::TimeType) -> TimeType Adjusts `dt` to the first day of its year. # Examples ```jldoctest julia> firstdayofyear(DateTime("1996-05-20")) 1996-01-01T00:00:00 ``` """ function firstdayofyear end firstdayofyear(dt::Date) = Date(UTD(value(dt) - dayofyear(dt) + 1)) firstdayofyear(dt::DateTime) = DateTime(firstdayofyear(Date(dt))) """ lastdayofyear(dt::TimeType) -> TimeType Adjusts `dt` to the last day of its year. # Examples ```jldoctest julia> lastdayofyear(DateTime("1996-05-20")) 1996-12-31T00:00:00 ``` """ function lastdayofyear end function lastdayofyear(dt::Date) y, m, d = yearmonthday(dt) return Date(UTD(value(dt) + daysinyear(y) - dayofyear(y, m, d))) end lastdayofyear(dt::DateTime) = DateTime(lastdayofyear(Date(dt))) """ firstdayofquarter(dt::TimeType) -> TimeType Adjusts `dt` to the first day of its quarter. # Examples ```jldoctest julia> firstdayofquarter(DateTime("1996-05-20")) 1996-04-01T00:00:00 julia> firstdayofquarter(DateTime("1996-08-20")) 1996-07-01T00:00:00 ``` """ function firstdayofquarter end function firstdayofquarter(dt::Date) y,m = yearmonth(dt) mm = m < 4 ? 1 : m < 7 ? 4 : m < 10 ? 7 : 10 return Date(y, mm, 1) end firstdayofquarter(dt::DateTime) = DateTime(firstdayofquarter(Date(dt))) """ lastdayofquarter(dt::TimeType) -> TimeType Adjusts `dt` to the last day of its quarter. # Examples ```jldoctest julia> lastdayofquarter(DateTime("1996-05-20")) 1996-06-30T00:00:00 julia> lastdayofquarter(DateTime("1996-08-20")) 1996-09-30T00:00:00 ``` """ function lastdayofquarter end function lastdayofquarter(dt::Date) y,m = yearmonth(dt) mm, d = m < 4 ? (3, 31) : m < 7 ? (6, 30) : m < 10 ? (9, 30) : (12, 31) return Date(y, mm, d) end lastdayofquarter(dt::DateTime) = DateTime(lastdayofquarter(Date(dt))) # Temporal Adjusters struct DateFunction f::Function # validate boolean, single-arg inner constructor function DateFunction(@nospecialize(f), dt::TimeType) isa(f(dt), Bool) || throw(ArgumentError("Provided function must take a single TimeType argument and return true or false")) return new(f) end end Base.show(io::IO, df::DateFunction) = println(io, df.f) # Core adjuster function adjust(df::DateFunction, start, step, limit) for i = 1:limit df.f(start) && return start start += step end throw(ArgumentError("Adjustment limit reached: $limit iterations")) end function adjust(func::Function, start; step::Period=Day(1), limit::Int=10000) return adjust(DateFunction(func, start), start, step, limit) end # Constructors using DateFunctions """ Date(f::Function, y[, m, d]; step=Day(1), limit=10000) -> Date Create a `Date` through the adjuster API. The starting point will be constructed from the provided `y, m, d` arguments, and will be adjusted until `f::Function` returns `true`. The step size in adjusting can be provided manually through the `step` keyword. `limit` provides a limit to the max number of iterations the adjustment API will pursue before throwing an error (given that `f::Function` is never satisfied). # Examples ```jldoctest julia> Date(date -> week(date) == 20, 2010, 01, 01) 2010-05-17 julia> Date(date -> year(date) == 2010, 2000, 01, 01) 2010-01-01 julia> Date(date -> month(date) == 10, 2000, 01, 01; limit = 5) ERROR: ArgumentError: Adjustment limit reached: 5 iterations Stacktrace: [...] ``` """ function Date(func::Function, y, m=1, d=1; step::Period=Day(1), limit::Int=10000) return adjust(DateFunction(func, Date(y, m, d)), Date(y, m, d), step, limit) end """ DateTime(f::Function, y[, m, d, h, mi, s]; step=Day(1), limit=10000) -> DateTime Create a `DateTime` through the adjuster API. The starting point will be constructed from the provided `y, m, d...` arguments, and will be adjusted until `f::Function` returns `true`. The step size in adjusting can be provided manually through the `step` keyword. `limit` provides a limit to the max number of iterations the adjustment API will pursue before throwing an error (in the case that `f::Function` is never satisfied). # Examples ```jldoctest julia> DateTime(dt -> second(dt) == 40, 2010, 10, 20, 10; step = Second(1)) 2010-10-20T10:00:40 julia> DateTime(dt -> hour(dt) == 20, 2010, 10, 20, 10; step = Hour(1), limit = 5) ERROR: ArgumentError: Adjustment limit reached: 5 iterations Stacktrace: [...] ``` """ DateTime(::Function, args...) function DateTime(func::Function, y, m=1; step::Period=Day(1), limit::Int=10000) return adjust(DateFunction(func, DateTime(y, m)), DateTime(y, m), step, limit) end function DateTime(func::Function, y, m, d; step::Period=Hour(1), limit::Int=10000) return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d), step, limit) end function DateTime(func::Function, y, m, d, h; step::Period=Minute(1), limit::Int=10000) return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h), step, limit) end function DateTime(func::Function, y, m, d, h, mi; step::Period=Second(1), limit::Int=10000) return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h, mi), step, limit) end function DateTime(func::Function, y, m, d, h, mi, s; step::Period=Millisecond(1), limit::Int=10000) return adjust(DateFunction(func, DateTime(y)), DateTime(y, m, d, h, mi, s), step, limit) end """ Time(f::Function, h, mi=0; step::Period=Second(1), limit::Int=10000) Time(f::Function, h, mi, s; step::Period=Millisecond(1), limit::Int=10000) Time(f::Function, h, mi, s, ms; step::Period=Microsecond(1), limit::Int=10000) Time(f::Function, h, mi, s, ms, us; step::Period=Nanosecond(1), limit::Int=10000) Create a `Time` through the adjuster API. The starting point will be constructed from the provided `h, mi, s, ms, us` arguments, and will be adjusted until `f::Function` returns `true`. The step size in adjusting can be provided manually through the `step` keyword. `limit` provides a limit to the max number of iterations the adjustment API will pursue before throwing an error (in the case that `f::Function` is never satisfied). Note that the default step will adjust to allow for greater precision for the given arguments; i.e. if hour, minute, and second arguments are provided, the default step will be `Millisecond(1)` instead of `Second(1)`. # Examples ```jldoctest julia> Time(t -> minute(t) == 30, 20) 20:30:00 julia> Time(t -> minute(t) == 0, 20) 20:00:00 julia> Time(t -> hour(t) == 10, 3; limit = 5) ERROR: ArgumentError: Adjustment limit reached: 5 iterations Stacktrace: [...] ``` """ Time(::Function, args...) function Time(func::Function, h, mi=0; step::Period=Second(1), limit::Int=10000) return adjust(DateFunction(func, Time(h, mi)), Time(h, mi), step, limit) end function Time(func::Function, h, mi, s; step::Period=Millisecond(1), limit::Int=10000) return adjust(DateFunction(func, Time(h, mi, s)), Time(h, mi, s), step, limit) end function Time(func::Function, h, mi, s, ms; step::Period=Microsecond(1), limit::Int=10000) return adjust(DateFunction(func, Time(h, mi, s, ms)), Time(h, mi, s, ms), step, limit) end function Time(func::Function, h, mi, s, ms, us; step::Period=Nanosecond(1), limit::Int=10000) return adjust(DateFunction(func, Time(h, mi, s, ms, us)), Time(h, mi, s, ms, us), step, limit) end # Return the next TimeType that falls on dow ISDAYOFWEEK = Dict(Mon => DateFunction(ismonday, Date(0)), Tue => DateFunction(istuesday, Date(0)), Wed => DateFunction(iswednesday, Date(0)), Thu => DateFunction(isthursday, Date(0)), Fri => DateFunction(isfriday, Date(0)), Sat => DateFunction(issaturday, Date(0)), Sun => DateFunction(issunday, Date(0))) # "same" indicates whether the current date can be considered or not """ tonext(dt::TimeType, dow::Int; same::Bool=false) -> TimeType Adjusts `dt` to the next day of week corresponding to `dow` with `1 = Monday, 2 = Tuesday, etc`. Setting `same=true` allows the current `dt` to be considered as the next `dow`, allowing for no adjustment to occur. """ tonext(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt + Day(1), Day(1), 7) # Return the next TimeType where func evals true using step in incrementing """ tonext(func::Function, dt::TimeType; step=Day(1), limit=10000, same=false) -> TimeType Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func` returns `true`. `func` must take a single `TimeType` argument and return a [`Bool`](@ref). `same` allows `dt` to be considered in satisfying `func`. """ function tonext(func::Function, dt::TimeType; step::Period=Day(1), limit::Int=10000, same::Bool=false) return adjust(DateFunction(func, dt), same ? dt : dt + step, step, limit) end """ toprev(dt::TimeType, dow::Int; same::Bool=false) -> TimeType Adjusts `dt` to the previous day of week corresponding to `dow` with `1 = Monday, 2 = Tuesday, etc`. Setting `same=true` allows the current `dt` to be considered as the previous `dow`, allowing for no adjustment to occur. """ toprev(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt + Day(-1), Day(-1), 7) """ toprev(func::Function, dt::TimeType; step=Day(-1), limit=10000, same=false) -> TimeType Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func` returns `true`. `func` must take a single `TimeType` argument and return a [`Bool`](@ref). `same` allows `dt` to be considered in satisfying `func`. """ function toprev(func::Function, dt::TimeType; step::Period=Day(-1), limit::Int=10000, same::Bool=false) return adjust(DateFunction(func, dt), same ? dt : dt + step, step, limit) end # Return the first TimeType that falls on dow in the Month or Year """ tofirst(dt::TimeType, dow::Int; of=Month) -> TimeType Adjusts `dt` to the first `dow` of its month. Alternatively, `of=Year` will adjust to the first `dow` of the year. """ function tofirst(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month) dt = of <: Month ? firstdayofmonth(dt) : firstdayofyear(dt) return adjust(ISDAYOFWEEK[dow], dt, Day(1), 366) end # Return the last TimeType that falls on dow in the Month or Year """ tolast(dt::TimeType, dow::Int; of=Month) -> TimeType Adjusts `dt` to the last `dow` of its month. Alternatively, `of=Year` will adjust to the last `dow` of the year. """ function tolast(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month) dt = of <: Month ? lastdayofmonth(dt) : lastdayofyear(dt) return adjust(ISDAYOFWEEK[dow], dt, Day(-1), 366) end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/rounding.jlƒ%������# This file is a part of Julia. License is MIT: https://julialang.org/license # The epochs used for date rounding are based ISO 8601's "year zero" notation const DATEEPOCH = value(Date(0)) const DATETIMEEPOCH = value(DateTime(0)) # According to ISO 8601, the first day of the first week of year 0000 is 0000-01-03 const WEEKEPOCH = value(Date(0, 1, 3)) const ConvertiblePeriod = Union{TimePeriod, Week, Day} const TimeTypeOrPeriod = Union{TimeType, ConvertiblePeriod} """ epochdays2date(days) -> Date Take the number of days since the rounding epoch (`0000-01-01T00:00:00`) and return the corresponding `Date`. """ epochdays2date(i) = Date(UTD(DATEEPOCH + Int64(i))) """ epochms2datetime(milliseconds) -> DateTime Take the number of milliseconds since the rounding epoch (`0000-01-01T00:00:00`) and return the corresponding `DateTime`. """ epochms2datetime(i) = DateTime(UTM(DATETIMEEPOCH + Int64(i))) """ date2epochdays(dt::Date) -> Int64 Take the given `Date` and return the number of days since the rounding epoch (`0000-01-01T00:00:00`) as an [`Int64`](@ref). """ date2epochdays(dt::Date) = value(dt) - DATEEPOCH """ datetime2epochms(dt::DateTime) -> Int64 Take the given `DateTime` and return the number of milliseconds since the rounding epoch (`0000-01-01T00:00:00`) as an [`Int64`](@ref). """ datetime2epochms(dt::DateTime) = value(dt) - DATETIMEEPOCH function Base.floor(dt::Date, p::Year) value(p) < 1 && throw(DomainError(p)) years = year(dt) return Date(years - mod(years, value(p))) end function Base.floor(dt::Date, p::Month) value(p) < 1 && throw(DomainError(p)) y, m = yearmonth(dt) months_since_epoch = y * 12 + m - 1 month_offset = months_since_epoch - mod(months_since_epoch, value(p)) target_month = mod(month_offset, 12) + 1 target_year = div(month_offset, 12) - (month_offset < 0 && target_month != 1) return Date(target_year, target_month) end function Base.floor(dt::Date, p::Quarter) return floor(dt, Month(p)) end function Base.floor(dt::Date, p::Week) value(p) < 1 && throw(DomainError(p)) days = value(dt) - WEEKEPOCH days = days - mod(days, value(Day(p))) return Date(UTD(WEEKEPOCH + Int64(days))) end function Base.floor(dt::Date, p::Day) value(p) < 1 && throw(DomainError(p)) days = date2epochdays(dt) return epochdays2date(days - mod(days, value(p))) end Base.floor(dt::DateTime, p::DatePeriod) = DateTime(Base.floor(Date(dt), p)) function Base.floor(dt::DateTime, p::TimePeriod) value(p) < 1 && throw(DomainError(p)) milliseconds = datetime2epochms(dt) return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p)))) end """ floor(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> T Round `x` down to the nearest multiple of `precision`. If `x` and `precision` are different subtypes of `Period`, the return value will have the same type as `precision`. For convenience, `precision` may be a type instead of a value: `floor(x, Dates.Hour)` is a shortcut for `floor(x, Dates.Hour(1))`. ```jldoctest julia> floor(Day(16), Week) 2 weeks julia> floor(Minute(44), Minute(15)) 30 minutes julia> floor(Hour(36), Day) 1 day ``` Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of inconsistent length. """ function Base.floor(x::ConvertiblePeriod, precision::T) where T <: ConvertiblePeriod value(precision) < 1 && throw(DomainError(precision)) _x, _precision = promote(x, precision) return T(_x - mod(_x, _precision)) end """ floor(dt::TimeType, p::Period) -> TimeType Return the nearest `Date` or `DateTime` less than or equal to `dt` at resolution `p`. For convenience, `p` may be a type instead of a value: `floor(dt, Dates.Hour)` is a shortcut for `floor(dt, Dates.Hour(1))`. ```jldoctest julia> floor(Date(1985, 8, 16), Month) 1985-08-01 julia> floor(DateTime(2013, 2, 13, 0, 31, 20), Minute(15)) 2013-02-13T00:30:00 julia> floor(DateTime(2016, 8, 6, 12, 0, 0), Day) 2016-08-06T00:00:00 ``` """ Base.floor(::Dates.TimeType, ::Dates.Period) """ ceil(dt::TimeType, p::Period) -> TimeType Return the nearest `Date` or `DateTime` greater than or equal to `dt` at resolution `p`. For convenience, `p` may be a type instead of a value: `ceil(dt, Dates.Hour)` is a shortcut for `ceil(dt, Dates.Hour(1))`. ```jldoctest julia> ceil(Date(1985, 8, 16), Month) 1985-09-01 julia> ceil(DateTime(2013, 2, 13, 0, 31, 20), Minute(15)) 2013-02-13T00:45:00 julia> ceil(DateTime(2016, 8, 6, 12, 0, 0), Day) 2016-08-07T00:00:00 ``` """ function Base.ceil(dt::TimeType, p::Period) f = floor(dt, p) return (dt == f) ? f : f + p end """ ceil(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> T Round `x` up to the nearest multiple of `precision`. If `x` and `precision` are different subtypes of `Period`, the return value will have the same type as `precision`. For convenience, `precision` may be a type instead of a value: `ceil(x, Dates.Hour)` is a shortcut for `ceil(x, Dates.Hour(1))`. ```jldoctest julia> ceil(Day(16), Week) 3 weeks julia> ceil(Minute(44), Minute(15)) 45 minutes julia> ceil(Hour(36), Day) 2 days ``` Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of inconsistent length. """ function Base.ceil(x::ConvertiblePeriod, precision::ConvertiblePeriod) f = floor(x, precision) return (x == f) ? f : f + precision end """ floorceil(dt::TimeType, p::Period) -> (TimeType, TimeType) Simultaneously return the `floor` and `ceil` of a `Date` or `DateTime` at resolution `p`. More efficient than calling both `floor` and `ceil` individually. """ function floorceil(dt::TimeType, p::Period) f = floor(dt, p) return f, (dt == f) ? f : f + p end """ floorceil(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> (T, T) Simultaneously return the `floor` and `ceil` of `Period` at resolution `p`. More efficient than calling both `floor` and `ceil` individually. """ function floorceil(x::ConvertiblePeriod, precision::ConvertiblePeriod) f = floor(x, precision) return f, (x == f) ? f : f + precision end """ round(dt::TimeType, p::Period, [r::RoundingMode]) -> TimeType Return the `Date` or `DateTime` nearest to `dt` at resolution `p`. By default (`RoundNearestTiesUp`), ties (e.g., rounding 9:30 to the nearest hour) will be rounded up. For convenience, `p` may be a type instead of a value: `round(dt, Dates.Hour)` is a shortcut for `round(dt, Dates.Hour(1))`. ```jldoctest julia> round(Date(1985, 8, 16), Month) 1985-08-01 julia> round(DateTime(2013, 2, 13, 0, 31, 20), Minute(15)) 2013-02-13T00:30:00 julia> round(DateTime(2016, 8, 6, 12, 0, 0), Day) 2016-08-07T00:00:00 ``` Valid rounding modes for `round(::TimeType, ::Period, ::RoundingMode)` are `RoundNearestTiesUp` (default), `RoundDown` (`floor`), and `RoundUp` (`ceil`). """ function Base.round(dt::TimeType, p::Period, r::RoundingMode{:NearestTiesUp}) f, c = floorceil(dt, p) return (dt - f) < (c - dt) ? f : c end """ round(x::Period, precision::T, [r::RoundingMode]) where T <: Union{TimePeriod, Week, Day} -> T Round `x` to the nearest multiple of `precision`. If `x` and `precision` are different subtypes of `Period`, the return value will have the same type as `precision`. By default (`RoundNearestTiesUp`), ties (e.g., rounding 90 minutes to the nearest hour) will be rounded up. For convenience, `precision` may be a type instead of a value: `round(x, Dates.Hour)` is a shortcut for `round(x, Dates.Hour(1))`. ```jldoctest julia> round(Day(16), Week) 2 weeks julia> round(Minute(44), Minute(15)) 45 minutes julia> round(Hour(36), Day) 2 days ``` Valid rounding modes for `round(::Period, ::T, ::RoundingMode)` are `RoundNearestTiesUp` (default), `RoundDown` (`floor`), and `RoundUp` (`ceil`). Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of inconsistent length. """ function Base.round(x::ConvertiblePeriod, precision::ConvertiblePeriod, r::RoundingMode{:NearestTiesUp}) f, c = floorceil(x, precision) _x, _f, _c = promote(x, f, c) return (_x - _f) < (_c - _x) ? f : c end Base.round(x::TimeTypeOrPeriod, p::Period, r::RoundingMode{:Down}) = Base.floor(x, p) Base.round(x::TimeTypeOrPeriod, p::Period, r::RoundingMode{:Up}) = Base.ceil(x, p) # No implementation of other `RoundingMode`s: rounding to nearest "even" is skipped because # "even" is not defined for Period; rounding toward/away from zero is skipped because ISO # 8601's year 0000 is not really "zero". Base.round(::TimeTypeOrPeriod, p::Period, ::RoundingMode) = throw(DomainError(p)) # Default to RoundNearestTiesUp. Base.round(x::TimeTypeOrPeriod, p::Period) = Base.round(x, p, RoundNearestTiesUp) # Make rounding functions callable using Period types in addition to values. Base.floor(x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.floor(x, oneunit(P)) Base.ceil(x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.ceil(x, oneunit(P)) Base.floor(::Type{Date}, x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.floor(Date(x), oneunit(P)) Base.ceil(::Type{Date}, x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.ceil(Date(x), oneunit(P)) function Base.round(x::TimeTypeOrPeriod, ::Type{P}, r::RoundingMode=RoundNearestTiesUp) where P <: Period return Base.round(x, oneunit(P), r) end function Base.round(::Type{Date}, x::TimeTypeOrPeriod, ::Type{P}, r::RoundingMode=RoundNearestTiesUp) where P <: Period return Base.round(Date(x), oneunit(P), r) end k���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/io.jlฌ_������# This file is a part of Julia. License is MIT: https://julialang.org/license """ AbstractDateToken A token used in parsing or formatting a date time string. Each subtype must define the tryparsenext and format methods. """ abstract type AbstractDateToken end """ tryparsenext(tok::AbstractDateToken, str::String, i::Int, len::Int, locale::DateLocale) `tryparsenext` parses for the `tok` token in `str` starting at index `i`. `len` is the length of the string. parsing can be optionally based on the `locale`. If a `tryparsenext` method does not need a locale, it can leave the argument out in the method definition. If parsing succeeds, returns a tuple of 2 elements `(res, idx)`, where: * `res` is the result of the parsing. * `idx::Int`, is the index _after_ the index at which parsing ended. """ function tryparsenext end """ format(io::IO, tok::AbstractDateToken, dt::TimeType, locale) Format the `tok` token from `dt` and write it to `io`. The formatting can be based on `locale`. All subtypes of `AbstractDateToken` must define this method in order to be able to print a Date / DateTime object according to a `DateFormat` containing that token. """ format(io::IO, tok::AbstractDateToken, dt::TimeType, locale) # fallback to tryparsenext/format methods that don't care about locale @inline function tryparsenext(d::AbstractDateToken, str, i, len, locale) return tryparsenext(d, str, i, len) end function Base.string(t::Time) h, mi, s = hour(t), minute(t), second(t) hh = lpad(h, 2, "0") mii = lpad(mi, 2, "0") ss = lpad(s, 2, "0") nss = tons(Millisecond(t)) + tons(Microsecond(t)) + tons(Nanosecond(t)) ns = nss == 0 ? "" : rstrip(@sprintf("%.9f", nss / 1e+9)[2:end], '0') return "$hh:$mii:$ss$ns" end Base.show(io::IO, ::MIME"text/plain", t::Time) = print(io, t) Base.print(io::IO, t::Time) = print(io, string(t)) function Base.show(io::IO, t::Time) if get(io, :compact, false)::Bool print(io, t) else values = [ hour(t) minute(t) second(t) millisecond(t) microsecond(t) nanosecond(t) ] index = something(findlast(!iszero, values), 1) print(io, Time, "(") for i in 1:index show(io, values[i]) i != index && print(io, ", ") end print(io, ")") end end @inline function format(io, d::AbstractDateToken, dt, locale) format(io, d, dt) end # Information for parsing and formatting date time values. struct DateFormat{S, T<:Tuple} tokens::T locale::DateLocale end ### Token types ### struct DatePart{letter} <: AbstractDateToken width::Int fixed::Bool end @inline min_width(d::DatePart) = d.fixed ? d.width : 1 @inline max_width(d::DatePart) = d.fixed ? d.width : 0 function _show_content(io::IO, d::DatePart{c}) where c for i = 1:d.width print(io, c) end end function Base.show(io::IO, d::DatePart{c}) where c print(io, "DatePart(") _show_content(io, d) print(io, ")") end ### Parse tokens for c in "yYmdHIMS" @eval begin @inline function tryparsenext(d::DatePart{$c}, str, i, len) return tryparsenext_base10(str, i, len, min_width(d), max_width(d)) end end end function tryparsenext(d::DatePart{'p'}, str, i, len) i+1 > len && return nothing c, ii = iterate(str, i)::Tuple{Char, Int} ap = lowercase(c) (ap == 'a' || ap == 'p') || return nothing c, ii = iterate(str, ii)::Tuple{Char, Int} lowercase(c) == 'm' || return nothing return ap == 'a' ? AM : PM, ii end for (tok, fn) in zip("uUeE", Any[monthabbr_to_value, monthname_to_value, dayabbr_to_value, dayname_to_value]) @eval @inline function tryparsenext(d::DatePart{$tok}, str, i, len, locale) next = tryparsenext_word(str, i, len, locale, max_width(d)) next === nothing && return nothing word, i = next val = $fn(word, locale) val == 0 && return nothing return val, i end end # 3-digit (base 10) number following a decimal point. For InexactError below. struct Decimal3 end @inline function tryparsenext(d::DatePart{'s'}, str, i, len) val = tryparsenext_base10(str, i, len, min_width(d), max_width(d)) val === nothing && return nothing ms0, ii = val len = ii - i if len > 3 ms, r = divrem(ms0, Int64(10) ^ (len - 3)) r == 0 || return nothing else ms = ms0 * Int64(10) ^ (3 - len) end return ms, ii end ### Format tokens hour12(dt) = let h = hour(dt); h > 12 ? h - 12 : h == 0 ? 12 : h; end for (c, fn) in zip("YmdHIMS", Any[year, month, day, hour, hour12, minute, second]) @eval function format(io, d::DatePart{$c}, dt) print(io, string($fn(dt), base = 10, pad = d.width)) end end for (tok, fn) in zip("uU", Any[monthabbr, monthname]) @eval function format(io, d::DatePart{$tok}, dt, locale) print(io, $fn(month(dt), locale)) end end function format(io, d::DatePart{'p'}, dt, locale) ampm = hour(dt) < 12 ? "AM" : "PM" # fixme: locale-specific? print(io, ampm) end for (tok, fn) in zip("eE", Any[dayabbr, dayname]) @eval function format(io, ::DatePart{$tok}, dt, locale) print(io, $fn(dayofweek(dt), locale)) end end @inline function format(io, d::DatePart{'y'}, dt) y = year(dt) n = d.width # the last n digits of y # will be 0 padded if y has less than n digits str = string(y, base = 10, pad = n) l = lastindex(str) if l == n # fast path print(io, str) else print(io, SubString(str, l - (n - 1), l)) end end function format(io, d::DatePart{'s'}, dt) ms = millisecond(dt) if ms % 100 == 0 str = string(div(ms, 100)) elseif ms % 10 == 0 str = string(div(ms, 10), pad = 2) else str = string(ms, pad = 3) end print(io, rpad(str, d.width, '0')) end ### Delimiters struct Delim{T, length} <: AbstractDateToken d::T end Delim(d::T) where {T<:AbstractChar} = Delim{T, 1}(d) Delim(d::String) = Delim{String, length(d)}(d) @inline function tryparsenext(d::Delim{<:AbstractChar, N}, str, i::Int, len) where N for j = 1:N i > len && return nothing next = iterate(str, i) @assert next !== nothing c, i = next c != d.d && return nothing end return true, i end @inline function tryparsenext(d::Delim{String, N}, str, i::Int, len) where N i1 = i i2 = firstindex(d.d) for j = 1:N if i1 > len return nothing end next1 = iterate(str, i1) @assert next1 !== nothing c1, i1 = next1 next2 = iterate(d.d, i2) @assert next2 !== nothing c2, i2 = next2 if c1 != c2 return nothing end end return true, i1 end @inline function format(io, d::Delim, dt, locale) print(io, d.d) end function _show_content(io::IO, d::Delim{<:AbstractChar, N}) where N if d.d in keys(CONVERSION_SPECIFIERS) for i = 1:N print(io, '\\', d.d) end else for i = 1:N print(io, d.d) end end end function _show_content(io::IO, d::Delim) for c in d.d if c in keys(CONVERSION_SPECIFIERS) print(io, '\\') end print(io, c) end end function Base.show(io::IO, d::Delim) print(io, "Delim(") _show_content(io, d) print(io, ")") end ### DateFormat construction abstract type DayOfWeekToken end # special addition to Period types # Map conversion specifiers or character codes to tokens. # Note: Allow addition of new character codes added by packages const CONVERSION_SPECIFIERS = Dict{Char, Type}( 'y' => Year, 'Y' => Year, 'm' => Month, 'u' => Month, 'U' => Month, 'e' => DayOfWeekToken, 'E' => DayOfWeekToken, 'd' => Day, 'H' => Hour, 'I' => Hour, 'M' => Minute, 'S' => Second, 's' => Millisecond, 'p' => AMPM, ) # Default values are needed when a conversion specifier is used in a DateFormat for parsing # and we have reached the end of the input string. # Note: Allow `Any` value as a default to support extensibility const CONVERSION_DEFAULTS = IdDict{Type, Any}( Year => Int64(1), Month => Int64(1), DayOfWeekToken => Int64(0), Day => Int64(1), Hour => Int64(0), Minute => Int64(0), Second => Int64(0), Millisecond => Int64(0), Microsecond => Int64(0), Nanosecond => Int64(0), AMPM => TWENTYFOURHOUR, ) # Specifies the required fields in order to parse a TimeType # Note: Allows for addition of new TimeTypes const CONVERSION_TRANSLATIONS = IdDict{Type, Any}( Date => (Year, Month, Day), DateTime => (Year, Month, Day, Hour, Minute, Second, Millisecond, AMPM), Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond, AMPM), ) # The `DateFormat(format, locale)` method just below consumes the following Regex. # Constructing this Regex is fairly expensive; doing so in the method itself can # consume half or better of `DateFormat(format, locale)`'s runtime. So instead we # construct and cache it outside the method body. Note, however, that when # `keys(CONVERSION_SPECIFIERS)` changes, the cached Regex must be updated accordingly; # hence the mutability (Ref-ness) of the cache, the helper method with which to populate # the cache, the cache of the hash of `keys(CONVERSION_SPECIFIERS)` (to facilitate checking # for changes), and the lock (to maintain consistency of these objects across threads when # threads simultaneously modify `CONVERSION_SPECIFIERS` and construct `DateFormat`s). function compute_dateformat_regex(conversion_specifiers) letters = String(collect(keys(conversion_specifiers))) return Regex("(?<!\\\\)([\\Q$letters\\E])\\1*") end const DATEFORMAT_REGEX_LOCK = ReentrantLock() const DATEFORMAT_REGEX_HASH = Ref(hash(keys(CONVERSION_SPECIFIERS))) const DATEFORMAT_REGEX_CACHE = Ref(compute_dateformat_regex(CONVERSION_SPECIFIERS)) """ DateFormat(format::AbstractString, locale="english") -> DateFormat Construct a date formatting object that can be used for parsing date strings or formatting a date object as a string. The following character codes can be used to construct the `format` string: | Code | Matches | Comment | |:-----------|:----------|:--------------------------------------------------------------| | `Y` | 1996, 96 | Returns year of 1996, 0096 | | `y` | 1996, 96 | Same as `Y` on `parse` but discards excess digits on `format` | | `m` | 1, 01 | Matches 1 or 2-digit months | | `u` | Jan | Matches abbreviated months according to the `locale` keyword | | `U` | January | Matches full month names according to the `locale` keyword | | `d` | 1, 01 | Matches 1 or 2-digit days | | `H` | 00 | Matches hours (24-hour clock) | | `I` | 00 | For outputting hours with 12-hour clock | | `M` | 00 | Matches minutes | | `S` | 00 | Matches seconds | | `s` | .500 | Matches milliseconds | | `e` | Mon, Tues | Matches abbreviated days of the week | | `E` | Monday | Matches full name days of the week | | `p` | AM | Matches AM/PM (case-insensitive) | | `yyyymmdd` | 19960101 | Matches fixed-width year, month, and day | Characters not listed above are normally treated as delimiters between date and time slots. For example a `dt` string of "1996-01-15T00:00:00.0" would have a `format` string like "y-m-dTH:M:S.s". If you need to use a code character as a delimiter you can escape it using backslash. The date "1995y01m" would have the format "y\\ym\\m". Note that 12:00AM corresponds 00:00 (midnight), and 12:00PM corresponds to 12:00 (noon). When parsing a time with a `p` specifier, any hour (either `H` or `I`) is interpreted as as a 12-hour clock, so the `I` code is mainly useful for output. Creating a DateFormat object is expensive. Whenever possible, create it once and use it many times or try the [`dateformat""`](@ref @dateformat_str) string macro. Using this macro creates the DateFormat object once at macro expansion time and reuses it later. There are also several [pre-defined formatters](@ref Common-Date-Formatters), listed later. See [`DateTime`](@ref) and [`format`](@ref) for how to use a DateFormat object to parse and write Date strings respectively. """ function DateFormat(f::AbstractString, locale::DateLocale=ENGLISH) tokens = AbstractDateToken[] prev = () prev_offset = 1 # To understand this block, please see the comments attached to the definitions of # DATEFORMAT_REGEX_LOCK, DATEFORMAT_REGEX_HASH, and DATEFORMAT_REGEX_CACHE. lock(DATEFORMAT_REGEX_LOCK) try dateformat_regex_hash = hash(keys(CONVERSION_SPECIFIERS)) if dateformat_regex_hash != DATEFORMAT_REGEX_HASH[] DATEFORMAT_REGEX_HASH[] = dateformat_regex_hash DATEFORMAT_REGEX_CACHE[] = compute_dateformat_regex(CONVERSION_SPECIFIERS) end finally unlock(DATEFORMAT_REGEX_LOCK) end for m in eachmatch(DATEFORMAT_REGEX_CACHE[], f) tran = replace(f[prev_offset:prevind(f, m.offset)], r"\\(.)" => s"\1") if !isempty(prev) letter, width = prev push!(tokens, DatePart{letter}(width, isempty(tran))) end if !isempty(tran) push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran)) end letter = f[m.offset] width = length(m.match) prev = (letter, width) prev_offset = m.offset + width end tran = replace(f[prev_offset:lastindex(f)], r"\\(.)" => s"\1") if !isempty(prev) letter, width = prev push!(tokens, DatePart{letter}(width, false)) end if !isempty(tran) push!(tokens, Delim(length(tran) == 1 ? first(tran) : tran)) end tokens_tuple = (tokens...,) return DateFormat{Symbol(f),typeof(tokens_tuple)}(tokens_tuple, locale) end function DateFormat(f::AbstractString, locale::AbstractString) DateFormat(f, LOCALES[locale]) end function Base.show(io::IO, df::DateFormat{S,T}) where {S,T} print(io, "dateformat\"", S, '"') end Base.Broadcast.broadcastable(x::DateFormat) = Ref(x) """ dateformat"Y-m-d H:M:S" Create a [`DateFormat`](@ref) object. Similar to `DateFormat("Y-m-d H:M:S")` but creates the DateFormat object once during macro expansion. See [`DateFormat`](@ref) for details about format specifiers. """ macro dateformat_str(str) DateFormat(str) end # Standard formats """ Dates.ISODateTimeFormat Describes the ISO8601 formatting for a date and time. This is the default value for `Dates.format` of a `DateTime`. # Example ```jldoctest julia> Dates.format(DateTime(2018, 8, 8, 12, 0, 43, 1), ISODateTimeFormat) "2018-08-08T12:00:43.001" ``` """ const ISODateTimeFormat = DateFormat("yyyy-mm-dd\\THH:MM:SS.s") default_format(::Type{DateTime}) = ISODateTimeFormat """ Dates.ISODateFormat Describes the ISO8601 formatting for a date. This is the default value for `Dates.format` of a `Date`. # Example ```jldoctest julia> Dates.format(Date(2018, 8, 8), ISODateFormat) "2018-08-08" ``` """ const ISODateFormat = DateFormat("yyyy-mm-dd") default_format(::Type{Date}) = ISODateFormat """ Dates.ISOTimeFormat Describes the ISO8601 formatting for a time. This is the default value for `Dates.format` of a `Time`. # Example ```jldoctest julia> Dates.format(Time(12, 0, 43, 1), ISOTimeFormat) "12:00:43.001" ``` """ const ISOTimeFormat = DateFormat("HH:MM:SS.s") default_format(::Type{Time}) = ISOTimeFormat """ Dates.RFC1123Format Describes the RFC1123 formatting for a date and time. # Example ```jldoctest julia> Dates.format(DateTime(2018, 8, 8, 12, 0, 43, 1), RFC1123Format) "Wed, 08 Aug 2018 12:00:43" ``` """ const RFC1123Format = DateFormat("e, dd u yyyy HH:MM:SS") ### API const Locale = Union{DateLocale, String} """ DateTime(dt::AbstractString, format::AbstractString; locale="english") -> DateTime Construct a `DateTime` by parsing the `dt` date time string following the pattern given in the `format` string (see [`DateFormat`](@ref) for syntax). !!! note This method creates a `DateFormat` object each time it is called. It is recommended that you create a [`DateFormat`](@ref) object instead and use that as the second argument to avoid performance loss when using the same format repeatedly. # Example ```jldoctest julia> DateTime("2020-01-01", "yyyy-mm-dd") 2020-01-01T00:00:00 julia> a = ("2020-01-01", "2020-01-02"); julia> [DateTime(d, dateformat"yyyy-mm-dd") for d โˆˆ a] # preferred 2-element Vector{DateTime}: 2020-01-01T00:00:00 2020-01-02T00:00:00 ``` """ function DateTime(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH) return parse(DateTime, dt, DateFormat(format, locale)) end """ DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat) -> DateTime Construct a `DateTime` by parsing the `dt` date time string following the pattern given in the [`DateFormat`](@ref) object, or $ISODateTimeFormat if omitted. Similar to `DateTime(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing similarly formatted date time strings with a pre-created `DateFormat` object. """ DateTime(dt::AbstractString, df::DateFormat=ISODateTimeFormat) = parse(DateTime, dt, df) """ Date(d::AbstractString, format::AbstractString; locale="english") -> Date Construct a `Date` by parsing the `d` date string following the pattern given in the `format` string (see [`DateFormat`](@ref) for syntax). !!! note This method creates a `DateFormat` object each time it is called. It is recommended that you create a [`DateFormat`](@ref) object instead and use that as the second argument to avoid performance loss when using the same format repeatedly. # Example ```jldoctest julia> Date("2020-01-01", "yyyy-mm-dd") 2020-01-01 julia> a = ("2020-01-01", "2020-01-02"); julia> [Date(d, dateformat"yyyy-mm-dd") for d โˆˆ a] # preferred 2-element Vector{Date}: 2020-01-01 2020-01-02 ``` """ function Date(d::AbstractString, format::AbstractString; locale::Locale=ENGLISH) parse(Date, d, DateFormat(format, locale)) end """ Date(d::AbstractString, df::DateFormat=ISODateFormat) -> Date Construct a `Date` by parsing the `d` date string following the pattern given in the [`DateFormat`](@ref) object, or $ISODateFormat if omitted. Similar to `Date(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing similarly formatted date strings with a pre-created `DateFormat` object. """ Date(d::AbstractString, df::DateFormat=ISODateFormat) = parse(Date, d, df) """ Time(t::AbstractString, format::AbstractString; locale="english") -> Time Construct a `Time` by parsing the `t` time string following the pattern given in the `format` string (see [`DateFormat`](@ref) for syntax). !!! note This method creates a `DateFormat` object each time it is called. It is recommended that you create a [`DateFormat`](@ref) object instead and use that as the second argument to avoid performance loss when using the same format repeatedly. # Example ```jldoctest julia> Time("12:34pm", "HH:MMp") 12:34:00 julia> a = ("12:34pm", "2:34am"); julia> [Time(d, dateformat"HH:MMp") for d โˆˆ a] # preferred 2-element Vector{Time}: 12:34:00 02:34:00 ``` """ function Time(t::AbstractString, format::AbstractString; locale::Locale=ENGLISH) parse(Time, t, DateFormat(format, locale)) end """ Time(t::AbstractString, df::DateFormat=ISOTimeFormat) -> Time Construct a `Time` by parsing the `t` date time string following the pattern given in the [`DateFormat`](@ref) object, or $ISOTimeFormat if omitted. Similar to `Time(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing similarly formatted time strings with a pre-created `DateFormat` object. """ Time(t::AbstractString, df::DateFormat=ISOTimeFormat) = parse(Time, t, df) @generated function format(io::IO, dt::TimeType, fmt::DateFormat{<:Any,T}) where T N = fieldcount(T) quote ts = fmt.tokens loc = fmt.locale Base.@nexprs $N i -> format(io, ts[i], dt, loc) end end function format(dt::TimeType, fmt::DateFormat, bufsize=12) # preallocate to reduce resizing io = IOBuffer(Vector{UInt8}(undef, bufsize), read=true, write=true) format(io, dt, fmt) String(io.data[1:io.ptr - 1]) end """ format(dt::TimeType, format::AbstractString; locale="english") -> AbstractString Construct a string by using a `TimeType` object and applying the provided `format`. The following character codes can be used to construct the `format` string: | Code | Examples | Comment | |:-----------|:----------|:-------------------------------------------------------------| | `y` | 6 | Numeric year with a fixed width | | `Y` | 1996 | Numeric year with a minimum width | | `m` | 1, 12 | Numeric month with a minimum width | | `u` | Jan | Month name shortened to 3-chars according to the `locale` | | `U` | January | Full month name according to the `locale` keyword | | `d` | 1, 31 | Day of the month with a minimum width | | `H` | 0, 23 | Hour (24-hour clock) with a minimum width | | `M` | 0, 59 | Minute with a minimum width | | `S` | 0, 59 | Second with a minimum width | | `s` | 000, 500 | Millisecond with a minimum width of 3 | | `e` | Mon, Tue | Abbreviated days of the week | | `E` | Monday | Full day of week name | The number of sequential code characters indicate the width of the code. A format of `yyyy-mm` specifies that the code `y` should have a width of four while `m` a width of two. Codes that yield numeric digits have an associated mode: fixed-width or minimum-width. The fixed-width mode left-pads the value with zeros when it is shorter than the specified width and truncates the value when longer. Minimum-width mode works the same as fixed-width except that it does not truncate values longer than the width. When creating a `format` you can use any non-code characters as a separator. For example to generate the string "1996-01-15T00:00:00" you could use `format`: "yyyy-mm-ddTHH:MM:SS". Note that if you need to use a code character as a literal you can use the escape character backslash. The string "1996y01m" can be produced with the format "yyyy\\ymm\\m". """ function format(dt::TimeType, f::AbstractString; locale::Locale=ENGLISH) format(dt, DateFormat(f, locale)) end # show function Base.print(io::IO, dt::DateTime) str = if millisecond(dt) == 0 format(dt, dateformat"YYYY-mm-dd\THH:MM:SS", 19) else format(dt, dateformat"YYYY-mm-dd\THH:MM:SS.sss", 23) end print(io, str) end function Base.print(io::IO, dt::Date) # don't use format - bypassing IOBuffer creation # saves a bit of time here. y,m,d = yearmonthday(value(dt)) yy = y < 0 ? @sprintf("%05i", y) : lpad(y, 4, "0") mm = lpad(m, 2, "0") dd = lpad(d, 2, "0") print(io, "$yy-$mm-$dd") end for date_type in (:Date, :DateTime) # Human readable output (i.e. "2012-01-01") @eval Base.show(io::IO, ::MIME"text/plain", dt::$date_type) = print(io, dt) # Parsable output (i.e. Date("2012-01-01")) @eval Base.show(io::IO, dt::$date_type) = print(io, typeof(dt), "(\"", dt, "\")") # Parsable output will have type info displayed, thus it is implied @eval Base.typeinfo_implicit(::Type{$date_type}) = true end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/parse.jl™+������# This file is a part of Julia. License is MIT: https://julialang.org/license ### Parsing utilities _directives(::Type{DateFormat{S,T}}) where {S,T} = T.parameters character_codes(df::Type{DateFormat{S,T}}) where {S,T} = character_codes(_directives(df)) function character_codes(directives::Core.SimpleVector) letters = sizehint!(Char[], length(directives)) for (i, directive) in enumerate(directives) if directive <: DatePart letter = first(directive.parameters) push!(letters, letter) end end return letters end genvar(t::DataType) = Symbol(lowercase(string(nameof(t)))) """ tryparsenext_core(str::AbstractString, pos::Int, len::Int, df::DateFormat, raise=false) Parse the string according to the directives within the `DateFormat`. Parsing will start at character index `pos` and will stop when all directives are used or we have parsed up to the end of the string, `len`. When a directive cannot be parsed the returned value will be `nothing` if `raise` is false otherwise an exception will be thrown. If successful, return a 3-element tuple `(values, pos, num_parsed)`: * `values::Tuple`: A tuple which contains a value for each `DatePart` within the `DateFormat` in the order in which they occur. If the string ends before we finish parsing all the directives the missing values will be filled in with default values. * `pos::Int`: The character index at which parsing stopped. * `num_parsed::Int`: The number of values which were parsed and stored within `values`. Useful for distinguishing parsed values from default values. """ @generated function tryparsenext_core(str::AbstractString, pos::Int, len::Int, df::DateFormat, raise::Bool=false) directives = _directives(df) letters = character_codes(directives) tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters] value_names = Symbol[genvar(t) for t in tokens] value_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in tokens) # Pre-assign variables to defaults. Allows us to use `@goto done` without worrying about # unassigned variables. assign_defaults = Expr[] for (name, default) in zip(value_names, value_defaults) push!(assign_defaults, quote $name = $default end) end vi = 1 parsers = Expr[] for i = 1:length(directives) if directives[i] <: DatePart name = value_names[vi] vi += 1 push!(parsers, quote pos > len && @goto done let val = tryparsenext(directives[$i], str, pos, len, locale) val === nothing && @goto error $name, pos = val end num_parsed += 1 directive_index += 1 end) else push!(parsers, quote pos > len && @goto done let val = tryparsenext(directives[$i], str, pos, len, locale) val === nothing && @goto error delim, pos = val end directive_index += 1 end) end end return quote directives = df.tokens locale::DateLocale = df.locale num_parsed = 0 directive_index = 1 $(assign_defaults...) $(parsers...) pos > len || @goto error @label done return $(Expr(:tuple, value_names...)), pos, num_parsed @label error if raise if directive_index > length(directives) throw(ArgumentError("Found extra characters at the end of date time string")) else d = directives[directive_index] throw(ArgumentError("Unable to parse date time. Expected directive $d at char $pos")) end end return nothing end end """ tryparsenext_internal(::Type{<:TimeType}, str, pos, len, df::DateFormat, raise=false) Parse the string according to the directives within the `DateFormat`. The specified `TimeType` type determines the type of and order of tokens returned. If the given `DateFormat` or string does not provide a required token a default value will be used. When the string cannot be parsed the returned value will be `nothing` if `raise` is false otherwise an exception will be thrown. If successful, returns a 2-element tuple `(values, pos)`: * `values::Tuple`: A tuple which contains a value for each token as specified by the passed in type. * `pos::Int`: The character index at which parsing stopped. """ @generated function tryparsenext_internal(::Type{T}, str::AbstractString, pos::Int, len::Int, df::DateFormat, raise::Bool=false) where T<:TimeType letters = character_codes(df) tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters] value_names = Symbol[genvar(t) for t in tokens] output_tokens = CONVERSION_TRANSLATIONS[T] output_names = Symbol[genvar(t) for t in output_tokens] output_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in output_tokens) # Pre-assign output variables to defaults. Ensures that all output variables are # assigned as the value tuple returned from `tryparsenext_core` may not include all # of the required variables. assign_defaults = Expr[ quote $name = $default end for (name, default) in zip(output_names, output_defaults) ] # Unpacks the value tuple returned by `tryparsenext_core` into separate variables. value_tuple = Expr(:tuple, value_names...) return quote val = tryparsenext_core(str, pos, len, df, raise) val === nothing && return nothing values, pos, num_parsed = val $(assign_defaults...) $value_tuple = values return $(Expr(:tuple, output_names...)), pos end end @inline function tryparsenext_base10(str::AbstractString, i::Int, len::Int, min_width::Int=1, max_width::Int=0) i > len && return nothing min_pos = min_width <= 0 ? i : i + min_width - 1 max_pos = max_width <= 0 ? len : min(i + max_width - 1, len) d::Int64 = 0 @inbounds while i <= max_pos c, ii = iterate(str, i)::Tuple{Char, Int} if '0' <= c <= '9' d = d * 10 + (c - '0') else break end i = ii end if i <= min_pos return nothing else return d, i end end @inline function tryparsenext_word(str::AbstractString, i, len, locale, maxchars=0) word_start, word_end = i, 0 max_pos = maxchars <= 0 ? len : min(len, nextind(str, i, maxchars-1)) @inbounds while i <= max_pos c, ii = iterate(str, i)::Tuple{Char, Int} if isletter(c) word_end = i else break end i = ii end if word_end == 0 return nothing else return SubString(str, word_start, word_end), i end end function Base.parse(::Type{DateTime}, s::AbstractString, df::typeof(ISODateTimeFormat)) i, end_pos = firstindex(s), lastindex(s) i > end_pos && throw(ArgumentError("Cannot parse an empty string as a DateTime")) local dy dm = dd = Int64(1) th = tm = ts = tms = Int64(0) let val = tryparsenext_base10(s, i, end_pos, 1) val === nothing && @goto error dy, i = val i > end_pos && @goto error end c, i = iterate(s, i)::Tuple{Char, Int} c != '-' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 2) val === nothing && @goto error dm, i = val i > end_pos && @goto done end c, i = iterate(s, i)::Tuple{Char, Int} c != '-' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 2) val === nothing && @goto error dd, i = val i > end_pos && @goto done end c, i = iterate(s, i)::Tuple{Char, Int} c != 'T' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 2) val === nothing && @goto error th, i = val i > end_pos && @goto done end c, i = iterate(s, i)::Tuple{Char, Int} c != ':' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 2) val === nothing && @goto error tm, i = val i > end_pos && @goto done end c, i = iterate(s, i)::Tuple{Char, Int} c != ':' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 2) val === nothing && @goto error ts, i = val i > end_pos && @goto done end c, i = iterate(s, i)::Tuple{Char, Int} c != '.' && @goto error i > end_pos && @goto done let val = tryparsenext_base10(s, i, end_pos, 1, 3) val === nothing && @goto error tms, j = val tms *= 10 ^ (3 - (j - i)) j > end_pos || @goto error end @label done return DateTime(dy, dm, dd, th, tm, ts, tms) @label error throw(ArgumentError("Invalid DateTime string")) end function Base.parse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = firstindex(str), lastindex(str) pos > len && throw(ArgumentError("Cannot parse an empty string as a Date or Time")) val = tryparsenext_internal(T, str, pos, len, df, true) @assert val !== nothing values, endpos = val return T(values...)::T end function Base.tryparse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = firstindex(str), lastindex(str) pos > len && return nothing res = tryparsenext_internal(T, str, pos, len, df, false) res === nothing && return nothing values, endpos = res if validargs(T, values...) === nothing # TODO: validargs gets called twice, since it's called again in the T constructor return T(values...)::T end return nothing end """ parse_components(str::AbstractString, df::DateFormat) -> Array{Any} Parse the string into its components according to the directives in the `DateFormat`. Each component will be a distinct type, typically a subtype of Period. The order of the components will match the order of the `DatePart` directives within the `DateFormat`. The number of components may be less than the total number of `DatePart`. """ @generated function parse_components(str::AbstractString, df::DateFormat) letters = character_codes(df) tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters] return quote pos, len = firstindex(str), lastindex(str) val = tryparsenext_core(str, pos, len, df, #=raise=#true) @assert val !== nothing values, pos, num_parsed = val types = $(Expr(:tuple, tokens...)) result = Vector{Any}(undef, num_parsed) for (i, typ) in enumerate(types) i > num_parsed && break result[i] = typ(values[i]) # Constructing types takes most of the time end return result end end s���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Dates/src/deprecated.jl� ������# This file is a part of Julia. License is MIT: https://julialang.org/license using Base: @deprecate, depwarn # 1.0 deprecations function (+)(x::AbstractArray{<:TimeType}, y::GeneralPeriod) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .+ y end function (+)(x::StridedArray{<:GeneralPeriod}, y::TimeType) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .+ y end function (+)(y::GeneralPeriod, x::AbstractArray{<:TimeType}) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .+ y end function (+)(y::TimeType, x::StridedArray{<:GeneralPeriod}) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .+ y end function (-)(x::AbstractArray{<:TimeType}, y::GeneralPeriod) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .- y end function (-)(x::StridedArray{<:GeneralPeriod}, y::TimeType) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .- y end # TimeType, AbstractArray{TimeType} function (-)(x::AbstractArray{T}, y::T) where {T<:TimeType} # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) x .- y end function (-)(y::T, x::AbstractArray{T}) where {T<:TimeType} # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) y .- x end for (op, Ty, Tz) in ((:*, Real, :P), (:/, :P, Float64), (:/, Real, :P)) @eval begin function ($op)(X::StridedArray{P}, y::$Ty) where P<:Period # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) Z = similar(X, $Tz) for (Idst, Isrc) in zip(eachindex(Z), eachindex(X)) @inbounds Z[Idst] = ($op)(X[Isrc], y) end return Z end end end function (+)(x::StridedArray{<:GeneralPeriod}) # depwarn("non-broadcasted operations are deprecated for Dates.TimeType; use broadcasting instead", nothing) x end for op in (:+, :-) @eval begin function ($op)(X::StridedArray{<:GeneralPeriod}, Y::StridedArray{<:GeneralPeriod}) # depwarn("non-broadcasted arithmetic is deprecated for Dates.TimeType; use broadcasting instead", nothing) reshape(CompoundPeriod[($op)(x, y) for (x, y) in zip(X, Y)], promote_shape(size(X), size(Y))) end end end @deprecate argerror(msg::String) ArgumentError(msg) false @deprecate argerror() nothing false p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Future/src/Future.jlถ������# This file is a part of Julia. License is MIT: https://julialang.org/license "The `Future` module implements future behavior of already existing functions, which will replace the current version in a future release of Julia." module Future using Random ## copy! # This has now been moved to Base (#29178), and should be deprecated in the # next "deprecation phase". """ Future.copy!(dst, src) -> dst Copy `src` into `dst`. !!! compat "Julia 1.1" This function has moved to `Base` with Julia 1.1, consider using `copy!(dst, src)` instead. `Future.copy!` will be deprecated in the future. """ copy!(dst::AbstractSet, src::AbstractSet) = Base.copy!(dst, src) copy!(dst::AbstractDict, src::AbstractDict) = Base.copy!(dst, src) copy!(dst::AbstractArray, src::AbstractArray) = Base.copy!(dst, src) ## randjump """ randjump(r::MersenneTwister, steps::Integer) -> MersenneTwister Create an initialized `MersenneTwister` object, whose state is moved forward (without generating numbers) from `r` by `steps` steps. One such step corresponds to the generation of two `Float64` numbers. For each different value of `steps`, a large polynomial has to be generated internally. One is already pre-computed for `steps=big(10)^20`. """ function randjump(r::MersenneTwister, steps::Integer) j = Random._randjump(r, Random.DSFMT.calc_jump(steps)) j.adv_jump += 2*big(steps) # convert to BigInt to prevent overflow j end end # module Future „���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/InteractiveUtils/src/InteractiveUtils.jlฮ6������# This file is a part of Julia. License is MIT: https://julialang.org/license module InteractiveUtils Base.Experimental.@optlevel 1 export apropos, edit, less, code_warntype, code_llvm, code_native, methodswith, varinfo, versioninfo, subtypes, supertypes, @which, @edit, @less, @functionloc, @code_warntype, @code_typed, @code_lowered, @code_llvm, @code_native, @time_imports, clipboard import Base.Docs.apropos using Base: unwrap_unionall, rewrap_unionall, isdeprecated, Bottom, show_unquoted, summarysize, signature_type, format_bytes using Base.Libc using Markdown include("editless.jl") include("codeview.jl") include("macros.jl") include("clipboard.jl") """ varinfo(m::Module=Main, pattern::Regex=r""; all=false, imported=false, recursive=false, sortby::Symbol=:name, minsize::Int=0) Return a markdown table giving information about exported global variables in a module, optionally restricted to those matching `pattern`. The memory consumption estimate is an approximate lower bound on the size of the internal structure of the object. - `all` : also list non-exported objects defined in the module, deprecated objects, and compiler-generated objects. - `imported` : also list objects explicitly imported from other modules. - `recursive` : recursively include objects in sub-modules, observing the same settings in each. - `sortby` : the column to sort results by. Options are `:name` (default), `:size`, and `:summary`. - `minsize` : only includes objects with size at least `minsize` bytes. Defaults to `0`. The output of `varinfo` is intended for display purposes only. See also [`names`](@ref) to get an array of symbols defined in a module, which is suitable for more general manipulations. """ function varinfo(m::Module=Base.active_module(), pattern::Regex=r""; all::Bool = false, imported::Bool = false, recursive::Bool = false, sortby::Symbol = :name, minsize::Int=0) sortby in (:name, :size, :summary) || throw(ArgumentError("Unrecognized `sortby` value `:$sortby`. Possible options are `:name`, `:size`, and `:summary`")) rows = Vector{Any}[] workqueue = [(m, ""),] while !isempty(workqueue) m2, prep = popfirst!(workqueue) for v in names(m2; all, imported) if !isdefined(m2, v) || !occursin(pattern, string(v)) continue end value = getfield(m2, v) isbuiltin = value === Base || value === Base.active_module() || value === Core if recursive && !isbuiltin && isa(value, Module) && value !== m2 && nameof(value) === v && parentmodule(value) === m2 push!(workqueue, (value, "$prep$v.")) end ssize_str, ssize = if isbuiltin ("", typemax(Int)) else ss = summarysize(value) (format_bytes(ss), ss) end if ssize >= minsize push!(rows, Any[string(prep, v), ssize_str, summary(value), ssize]) end end end let (col, rev) = if sortby === :name 1, false elseif sortby === :size 4, true elseif sortby === :summary 3, false else @assert "unreachable" end sort!(rows; by=r->r[col], rev) end pushfirst!(rows, Any["name", "size", "summary"]) return Markdown.MD(Any[Markdown.Table(map(r->r[1:3], rows), Symbol[:l, :r, :l])]) end varinfo(pat::Regex; kwargs...) = varinfo(Base.active_module(), pat; kwargs...) """ versioninfo(io::IO=stdout; verbose::Bool=false) Print information about the version of Julia in use. The output is controlled with boolean keyword arguments: - `verbose`: print all additional information !!! warning "Warning" The output of this function may contain sensitive information. Before sharing the output, please review the output and remove any data that should not be shared publicly. See also: [`VERSION`](@ref). """ function versioninfo(io::IO=stdout; verbose::Bool=false) println(io, "Julia Version $VERSION") if !isempty(Base.GIT_VERSION_INFO.commit_short) println(io, "Commit $(Base.GIT_VERSION_INFO.commit_short) ($(Base.GIT_VERSION_INFO.date_string))") end official_release = Base.TAGGED_RELEASE_BANNER == "Official https://julialang.org/ release" if Base.isdebugbuild() || !isempty(Base.TAGGED_RELEASE_BANNER) || (Base.GIT_VERSION_INFO.tagged_commit && !official_release) println(io, "Build Info:") if Base.isdebugbuild() println(io, " DEBUG build") end if !isempty(Base.TAGGED_RELEASE_BANNER) println(io, " ", Base.TAGGED_RELEASE_BANNER) end if Base.GIT_VERSION_INFO.tagged_commit && !official_release println(io, """ Note: This is an unofficial build, please report bugs to the project responsible for this build and not to the Julia project unless you can reproduce the issue using official builds available at https://julialang.org/downloads """ ) end end println(io, "Platform Info:") println(io, " OS: ", Sys.iswindows() ? "Windows" : Sys.isapple() ? "macOS" : Sys.KERNEL, " (", Sys.MACHINE, ")") if verbose lsb = "" if Sys.islinux() try lsb = readchomp(pipeline(`lsb_release -ds`, stderr=devnull)); catch; end end if Sys.iswindows() try lsb = strip(read(`$(ENV["COMSPEC"]) /c ver`, String)); catch; end end if !isempty(lsb) println(io, " ", lsb) end if Sys.isunix() println(io, " uname: ", readchomp(`uname -mprsv`)) end end if verbose cpuio = IOBuffer() # print cpu_summary with correct alignment Sys.cpu_summary(cpuio) for (i, line) in enumerate(split(chomp(String(take!(cpuio))), "\n")) prefix = i == 1 ? " CPU: " : " " println(io, prefix, line) end else cpu = Sys.cpu_info() println(io, " CPU: ", length(cpu), " ร— ", cpu[1].model) end if verbose println(io, " Memory: $(Sys.total_memory()/2^30) GB ($(Sys.free_memory()/2^20) MB free)") try println(io, " Uptime: $(Sys.uptime()) sec"); catch; end print(io, " Load Avg: ") Base.print_matrix(io, Sys.loadavg()') println(io) end println(io, " WORD_SIZE: ", Sys.WORD_SIZE) println(io, " LIBM: ",Base.libm_name) println(io, " LLVM: libLLVM-",Base.libllvm_version," (", Sys.JIT, ", ", Sys.CPU_NAME, ")") println(io, """Threads: $(Threads.nthreads(:default)) default, $(Threads.nthreads(:interactive)) interactive, \ $(Threads.ngcthreads()) GC (on $(Sys.CPU_THREADS) virtual cores)""") function is_nonverbose_env(k::String) return occursin(r"^JULIA_|^DYLD_|^LD_", k) end function is_verbose_env(k::String) return occursin(r"PATH|FLAG|^TERM$|HOME", k) && !is_nonverbose_env(k) end env_strs = String[ String[" $(k) = $(v)" for (k,v) in ENV if is_nonverbose_env(uppercase(k))]; (verbose ? String[" $(k) = $(v)" for (k,v) in ENV if is_verbose_env(uppercase(k))] : String[]); ] if !isempty(env_strs) println(io, "Environment:") for str in env_strs println(io, str) end end end function type_close_enough(@nospecialize(x), @nospecialize(t)) x == t && return true # TODO: handle UnionAll properly return (isa(x, DataType) && isa(t, DataType) && x.name === t.name && x <: t) || (isa(x, Union) && isa(t, DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t))) end # `methodswith` -- shows a list of methods using the type given """ methodswith(typ[, module or function]; supertypes::Bool=false]) Return an array of methods with an argument of type `typ`. The optional second argument restricts the search to a particular module or function (the default is all top-level modules). If keyword `supertypes` is `true`, also return arguments with a parent type of `typ`, excluding type `Any`. """ function methodswith(@nospecialize(t::Type), @nospecialize(f::Base.Callable), meths = Method[]; supertypes::Bool=false) for d in methods(f) if any(function (x) let x = rewrap_unionall(x, d.sig) (type_close_enough(x, t) || (supertypes ? (isa(x, Type) && t <: x && (!isa(x,TypeVar) || x.ub != Any)) : (isa(x,TypeVar) && x.ub != Any && t == x.ub)) && x != Any) end end, unwrap_unionall(d.sig).parameters) push!(meths, d) end end return meths end function _methodswith(@nospecialize(t::Type), m::Module, supertypes::Bool) meths = Method[] for nm in names(m) if isdefined(m, nm) f = getfield(m, nm) if isa(f, Base.Callable) methodswith(t, f, meths; supertypes = supertypes) end end end return unique(meths) end methodswith(@nospecialize(t::Type), m::Module; supertypes::Bool=false) = _methodswith(t, m, supertypes) function methodswith(@nospecialize(t::Type); supertypes::Bool=false) meths = Method[] for mod in Base.loaded_modules_array() append!(meths, _methodswith(t, mod, supertypes)) end return unique(meths) end # subtypes function _subtypes_in!(mods::Array, x::Type) xt = unwrap_unionall(x) if !isabstracttype(x) || !isa(xt, DataType) # Fast path return Type[] end sts = Vector{Any}() while !isempty(mods) m = pop!(mods) xt = xt::DataType for s in names(m, all = true) if isdefined(m, s) && !isdeprecated(m, s) t = getfield(m, s) dt = isa(t, UnionAll) ? unwrap_unionall(t) : t if isa(dt, DataType) if dt.name.name === s && dt.name.module == m && supertype(dt).name == xt.name ti = typeintersect(t, x) ti != Bottom && push!(sts, ti) end elseif isa(t, Module) && nameof(t) === s && parentmodule(t) === m && t !== m t === Base || push!(mods, t) # exclude Base, since it also parented by Main end end end end return permute!(sts, sortperm(map(string, sts))) end subtypes(m::Module, x::Type) = _subtypes_in!([m], x) """ subtypes(T::DataType) Return a list of immediate subtypes of DataType `T`. Note that all currently loaded subtypes are included, including those not visible in the current module. See also [`supertype`](@ref), [`supertypes`](@ref), [`methodswith`](@ref). # Examples ```jldoctest julia> subtypes(Integer) 3-element Vector{Any}: Bool Signed Unsigned ``` """ subtypes(x::Type) = _subtypes_in!(Base.loaded_modules_array(), x) """ supertypes(T::Type) Return a tuple `(T, ..., Any)` of `T` and all its supertypes, as determined by successive calls to the [`supertype`](@ref) function, listed in order of `<:` and terminated by `Any`. See also [`subtypes`](@ref). # Examples ```jldoctest julia> supertypes(Int) (Int64, Signed, Integer, Real, Number, Any) ``` """ function supertypes(T::Type) S = supertype(T) # note: we return a tuple here, not an Array as for subtypes, because in # the future we could evaluate this function statically if desired. return S === T ? (T,) : (T, supertypes(S)...) end # TODO: @deprecate peakflops to LinearAlgebra export peakflops """ peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) `peakflops` computes the peak flop rate of the computer by using double precision [`gemm!`](@ref LinearAlgebra.BLAS.gemm!). For more information see [`LinearAlgebra.peakflops`](@ref). !!! compat "Julia 1.1" This function will be moved from `InteractiveUtils` to `LinearAlgebra` in the future. In Julia 1.1 and later it is available as `LinearAlgebra.peakflops`. """ function peakflops(n::Integer=4096; eltype::DataType=Float64, ntrials::Integer=3, parallel::Bool=false) # Base.depwarn("`peakflops` has moved to the LinearAlgebra module, " * # "add `using LinearAlgebra` to your imports.", :peakflops) let LinearAlgebra = Base.require(Base.PkgId( Base.UUID((0x37e2e46d_f89d_539d,0xb4ee_838fcccc9c8e)), "LinearAlgebra")) return LinearAlgebra.peakflops(n, eltype=eltype, ntrials=ntrials, parallel=parallel) end end function report_bug(kind) @info "Loading BugReporting package..." BugReportingId = Base.PkgId( Base.UUID((0xbcf9a6e7_4020_453c,0xb88e_690564246bb8)), "BugReporting") # Check if the BugReporting package exists in the current environment local BugReporting if Base.locate_package(BugReportingId) === nothing @info "Package `BugReporting` not found - attempting temporary installation" # Create a temporary environment and add BugReporting let Pkg = Base.require(Base.PkgId( Base.UUID((0x44cfe95a_1eb2_52ea,0xb672_e2afdf69b78f)), "Pkg")) mktempdir() do tmp old_load_path = copy(LOAD_PATH) push!(empty!(LOAD_PATH), joinpath(tmp, "Project.toml")) old_active_project = Base.ACTIVE_PROJECT[] Base.ACTIVE_PROJECT[] = nothing Pkg.add(Pkg.PackageSpec(BugReportingId.name, BugReportingId.uuid)) BugReporting = Base.require(BugReportingId) append!(empty!(LOAD_PATH), old_load_path) Base.ACTIVE_PROJECT[] = old_active_project end end else BugReporting = Base.require(BugReportingId) end return Base.invokelatest(BugReporting.make_interactive_report, kind, ARGS) end end |���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/InteractiveUtils/src/editless.jlQ*������# This file is a part of Julia. License is MIT: https://julialang.org/license # editing and paging files using Base: shell_split, shell_escape, find_source_file """ EDITOR_CALLBACKS :: Vector{Function} A vector of editor callback functions, which take as arguments `cmd`, `path`, `line` and `column` and which is then expected to either open an editor and return `true` to indicate that it has handled the request, or return `false` to decline the editing request. """ const EDITOR_CALLBACKS = Function[] """ define_editor(fn, pattern; wait=false) Define a new editor matching `pattern` that can be used to open a file (possibly at a given line number) using `fn`. The `fn` argument is a function that determines how to open a file with the given editor. It should take four arguments, as follows: * `cmd` - a base command object for the editor * `path` - the path to the source file to open * `line` - the line number to open the editor at * `column` - the column number to open the editor at Editors which cannot open to a specific line with a command or a specific column may ignore the `line` and/or `column` argument. The `fn` callback must return either an appropriate `Cmd` object to open a file or `nothing` to indicate that they cannot edit this file. Use `nothing` to indicate that this editor is not appropriate for the current environment and another editor should be attempted. It is possible to add more general editing hooks that need not spawn external commands by pushing a callback directly to the vector `EDITOR_CALLBACKS`. The `pattern` argument is a string, regular expression, or an array of strings and regular expressions. For the `fn` to be called, one of the patterns must match the value of `EDITOR`, `VISUAL` or `JULIA_EDITOR`. For strings, the string must equal the [`basename`](@ref) of the first word of the editor command, with its extension, if any, removed. E.g. "vi" doesn't match "vim -g" but matches "/usr/bin/vi -m"; it also matches `vi.exe`. If `pattern` is a regex it is matched against all of the editor command as a shell-escaped string. An array pattern matches if any of its items match. If multiple editors match, the one added most recently is used. By default julia does not wait for the editor to close, running it in the background. However, if the editor is terminal based, you will probably want to set `wait=true` and julia will wait for the editor to close before resuming. If one of the editor environment variables is set, but no editor entry matches it, the default editor entry is invoked: (cmd, path, line, column) -> `\$cmd \$path` Note that many editors are already defined. All of the following commands should already work: - emacs - emacsclient - vim - nvim - nano - micro - kak - helix - textmate - mate - kate - subl - atom - notepad++ - Visual Studio Code - open - pycharm - bbedit # Example: The following defines the usage of terminal-based `emacs`: define_editor( r"\\bemacs\\b.*\\s(-nw|--no-window-system)\\b", wait=true) do cmd, path, line `\$cmd +\$line \$path` end !!! compat "Julia 1.4" `define_editor` was introduced in Julia 1.4. """ function define_editor(fn::Function, pattern; wait::Bool=false) callback = function (cmd::Cmd, path::AbstractString, line::Integer, column::Integer) editor_matches(pattern, cmd) || return false editor = if !applicable(fn, cmd, path, line, column) # Be backwards compatible with editors that did not define the newly added column argument fn(cmd, path, line) else fn(cmd, path, line, column) end if editor isa Cmd if wait run(editor) # blocks while editor runs else run(pipeline(editor, stderr=stderr), wait=false) end return true elseif editor isa Nothing return false end @warn "invalid editor value returned" pattern=pattern editor=editor return false end pushfirst!(EDITOR_CALLBACKS, callback) end editor_matches(p::Regex, cmd::Cmd) = occursin(p, shell_escape(cmd)) editor_matches(p::String, cmd::Cmd) = p == splitext(basename(first(cmd)))[1] editor_matches(ps::AbstractArray, cmd::Cmd) = any(editor_matches(p, cmd) for p in ps) function define_default_editors() # fallback: just call the editor with the path as argument define_editor(r".*") do cmd, path, line, column `$cmd $path` end # vim family for (editors, wait) in [ [["vim", "vi", "nvim", "mvim"], true], [[r"\bgvim"], false], ] define_editor(editors; wait) do cmd, path, line, column cmd = line == 0 ? `$cmd $path` : column == 0 ? `$cmd +$line $path` : `$cmd "+normal $(line)G$(column)|" $path` end end define_editor("nano"; wait=true) do cmd, path, line, column cmd = `$cmd +$line,$column $path` end # emacs (must check that emacs not running in -t/-nw # before regex match for general emacs) for (editors, wait) in [ [[r"\bemacs"], false], [[r"\bemacs\b.*\s(-nw|--no-window-system)\b", r"\bemacsclient\b.\s*-(-?nw|t|-?tty)\b"], true], ] define_editor(editors; wait) do cmd, path, line, column `$cmd +$line:$column $path` end end # other editors define_editor("gedit") do cmd, path, line, column `$cmd +$line:$column $path` end define_editor(["micro", "kak"]; wait=true) do cmd, path, line, column `$cmd +$line $path` end define_editor(["hx", "helix"]; wait=true) do cmd, path, line, column `$cmd $path:$line:$column` end define_editor(["textmate", "mate", "kate"]) do cmd, path, line, column `$cmd $path -l $line` end define_editor([r"\bsubl", r"\batom", "pycharm", "bbedit"]) do cmd, path, line, column `$cmd $path:$line` end define_editor(["code", "code-insiders"]) do cmd, path, line, column `$cmd -g $path:$line:$column` end define_editor(r"\bnotepad++") do cmd, path, line, column `$cmd $path -n$line` end if Sys.iswindows() define_editor(r"\bCODE\.EXE\b"i) do cmd, path, line, column `$cmd -g $path:$line:$column` end callback = function (cmd::Cmd, path::AbstractString, line::Integer) cmd == `open` || return false # don't emit this ccall on other platforms @static if Sys.iswindows() result = ccall((:ShellExecuteW, "shell32"), stdcall, Int, (Ptr{Cvoid}, Cwstring, Cwstring, Ptr{Cvoid}, Ptr{Cvoid}, Cint), C_NULL, "open", path, C_NULL, C_NULL, 10) systemerror(:edit, result โ‰ค 32) end return true end pushfirst!(EDITOR_CALLBACKS, callback) elseif Sys.isapple() define_editor("open") do cmd, path, line, column `open -t $path` end end end define_default_editors() """ editor() Determine the editor to use when running functions like `edit`. Returns a `Cmd` object. Change editor by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` environment variables. """ function editor() # Note: the editor path can include spaces (if escaped) and flags. for var in ["JULIA_EDITOR", "VISUAL", "EDITOR"] str = get(ENV, var, nothing) str === nothing && continue isempty(str) && error("invalid editor \$$var: $(repr(str))") return Cmd(shell_split(str)) end editor_file = "/etc/alternatives/editor" editor = (Sys.iswindows() || Sys.isapple()) ? "open" : isfile(editor_file) ? realpath(editor_file) : "emacs" return Cmd([editor]) end """ edit(path::AbstractString, line::Integer=0, column::Integer=0) Edit a file or directory optionally providing a line number to edit the file at. Return to the `julia` prompt when you quit the editor. The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. See also [`define_editor`](@ref). """ function edit(path::AbstractString, line::Integer=0, column::Integer=0) path isa String || (path = convert(String, path)) if endswith(path, ".jl") p = find_source_file(path) p !== nothing && (path = p) end cmd = editor() for callback in EDITOR_CALLBACKS if !applicable(callback, cmd, path, line, column) callback(cmd, path, line) && return else callback(cmd, path, line, column) && return end end # shouldn't happen unless someone has removed fallback entry error("no editor found") end """ edit(function, [types]) edit(module) Edit the definition of a function, optionally specifying a tuple of types to indicate which method to edit. For modules, open the main source file. The module needs to be loaded with `using` or `import` first. !!! compat "Julia 1.1" `edit` on modules requires at least Julia 1.1. To ensure that the file can be opened at the given line, you may need to call `define_editor` first. """ function edit(@nospecialize f) ms = methods(f).ms length(ms) == 1 && edit(functionloc(ms[1])...) length(ms) > 1 && return ms length(ms) == 0 && functionloc(f) # throws nothing end edit(m::Method) = edit(functionloc(m)...) edit(@nospecialize(f), idx::Integer) = edit(methods(f).ms[idx]) edit(f, t) = (@nospecialize; edit(functionloc(f, t)...)) edit(file::Nothing, line::Integer) = error("could not find source file for function") edit(m::Module) = edit(pathof(m)) # terminal pager if Sys.iswindows() function less(file::AbstractString, line::Integer) pager = shell_split(get(ENV, "PAGER", "more")) if pager[1] == "more" g = "" line -= 1 else g = "g" end run(Cmd(`$pager +$(line)$(g) \"$file\"`, windows_verbatim = true)) nothing end else function less(file::AbstractString, line::Integer) pager = shell_split(get(ENV, "PAGER", "less")) run(`$pager +$(line)g $file`) nothing end end """ less(file::AbstractString, [line::Integer]) Show a file using the default pager, optionally providing a starting line number. Returns to the `julia` prompt when you quit the pager. """ less(file::AbstractString) = less(file, 1) """ less(function, [types]) Show the definition of a function using the default pager, optionally specifying a tuple of types to indicate which method to see. """ less(f) = less(functionloc(f)...) less(f, @nospecialize t) = less(functionloc(f,t)...) less(file, line::Integer) = error("could not find source file for function") |���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/InteractiveUtils/src/codeview.jlW������# This file is a part of Julia. License is MIT: https://julialang.org/license # highlighting settings const highlighting = Dict{Symbol, Bool}( :warntype => true, :llvm => true, :native => true, ) const llstyle = Dict{Symbol, Tuple{Bool, Union{Symbol, Int}}}( :default => (false, :normal), # e.g. comma, equal sign, unknown token :comment => (false, :light_black), :label => (false, :light_red), :instruction => ( true, :light_cyan), :type => (false, :cyan), :number => (false, :yellow), :bracket => (false, :yellow), :variable => (false, :normal), # e.g. variable, register :keyword => (false, :light_magenta), :funcname => (false, :light_yellow), ) function printstyled_ll(io::IO, x, s::Symbol, trailing_spaces="") printstyled(io, x, bold=llstyle[s][1], color=llstyle[s][2]) print(io, trailing_spaces) end # displaying type warnings function warntype_type_printer(io::IO; @nospecialize(type), used::Bool, show_type::Bool=true, _...) (show_type && used) || return nothing str = "::$type" if !highlighting[:warntype] print(io, str) elseif type isa Union && is_expected_union(type) Base.emphasize(io, str, Base.warn_color()) # more mild user notification elseif type isa Type && (!Base.isdispatchelem(type) || type == Core.Box) Base.emphasize(io, str) else Base.printstyled(io, str, color=:cyan) # show the "good" type end return nothing end # True if one can be pretty certain that the compiler handles this union well, # i.e. must be small with concrete types. function is_expected_union(u::Union) Base.unionlen(u) < 4 || return false for x in Base.uniontypes(u) if !Base.isdispatchelem(x) || x == Core.Box return false end end return true end """ code_warntype([io::IO], f, types; debuginfo=:default) Prints lowered and type-inferred ASTs for the methods matching the given generic function and type signature to `io` which defaults to `stdout`. The ASTs are annotated in such a way as to cause "non-leaf" types which may be problematic for performance to be emphasized (if color is available, displayed in red). This serves as a warning of potential type instability. Not all non-leaf types are particularly problematic for performance, and the performance characteristics of a particular type is an implementation detail of the compiler. `code_warntype` will err on the side of coloring types red if they might be a performance concern, so some types may be colored red even if they do not impact performance. Small unions of concrete types are usually not a concern, so these are highlighted in yellow. Keyword argument `debuginfo` may be one of `:source` or `:none` (default), to specify the verbosity of code comments. See the [`@code_warntype`](@ref man-code-warntype) section in the Performance Tips page of the manual for more information. """ function code_warntype(io::IO, @nospecialize(f), @nospecialize(t=Base.default_tt(f)); debuginfo::Symbol=:default, optimize::Bool=false, kwargs...) debuginfo = Base.IRShow.debuginfo(debuginfo) lineprinter = Base.IRShow.__debuginfo[debuginfo] for (src, rettype) in code_typed(f, t; optimize, kwargs...) if !(src isa Core.CodeInfo) println(io, src) println(io, " failed to infer") continue end lambda_io::IOContext = io p = src.parent nargs::Int = 0 if p isa Core.MethodInstance println(io, p) print(io, " from ") println(io, p.def) p.def isa Method && (nargs = p.def.nargs) if !isempty(p.sparam_vals) println(io, "Static Parameters") sig = p.def.sig warn_color = Base.warn_color() # more mild user notification for i = 1:length(p.sparam_vals) sig = sig::UnionAll name = sig.var.name val = p.sparam_vals[i] print_highlighted(io::IO, v::String, color::Symbol) = if highlighting[:warntype] Base.printstyled(io, v; color) else Base.print(io, v) end if val isa TypeVar if val.lb === Union{} print(io, " ", name, " <: ") print_highlighted(io, "$(val.ub)", warn_color) elseif val.ub === Any print(io, " ", sig.var.name, " >: ") print_highlighted(io, "$(val.lb)", warn_color) else print(io, " ") print_highlighted(io, "$(val.lb)", warn_color) print(io, " <: ", sig.var.name, " <: ") print_highlighted(io, "$(val.ub)", warn_color) end elseif val isa typeof(Vararg) print(io, " ", name, "::") print_highlighted(io, "Int", warn_color) else print(io, " ", sig.var.name, " = ") print_highlighted(io, "$(val)", :cyan) # show the "good" type end println(io) sig = sig.body end end end if src.slotnames !== nothing slotnames = Base.sourceinfo_slotnames(src) lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => slotnames) slottypes = src.slottypes nargs > 0 && println(io, "Arguments") for i = 1:length(slotnames) if i == nargs + 1 println(io, "Locals") end print(io, " ", slotnames[i]) if isa(slottypes, Vector{Any}) warntype_type_printer(io; type=slottypes[i], used=true) end println(io) end end print(io, "Body") warntype_type_printer(io; type=rettype, used=true) println(io) irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer) Base.IRShow.show_ir(lambda_io, src, irshow_config) println(io) end nothing end code_warntype(@nospecialize(f), @nospecialize(t=Base.default_tt(f)); kwargs...) = code_warntype(stdout, f, t; kwargs...) import Base.CodegenParams const GENERIC_SIG_WARNING = "; WARNING: This code may not match what actually runs.\n" const OC_MISMATCH_WARNING = """ ; WARNING: The pre-inferred opaque closure is not callable with the given arguments ; and will error on dispatch with this signature. """ # Printing code representations in IR and assembly function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool, raw::Bool, dump_module::Bool, syntax::Symbol, optimize::Bool, debuginfo::Symbol, binary::Bool) params = CodegenParams(debug_info_kind=Cint(0), safepoint_on_entry=raw, gcstack_arg=raw) _dump_function(f, t, native, wrapper, raw, dump_module, syntax, optimize, debuginfo, binary, params) end function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrapper::Bool, raw::Bool, dump_module::Bool, syntax::Symbol, optimize::Bool, debuginfo::Symbol, binary::Bool, params::CodegenParams) ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") if isa(f, Core.Builtin) throw(ArgumentError("argument is not a generic function")) end warning = "" # get the MethodInstance for the method match if !isa(f, Core.OpaqueClosure) world = Base.get_world_counter() match = Base._which(signature_type(f, t); world) mi = Core.Compiler.specialize_method(match) # TODO: use jl_is_cacheable_sig instead of isdispatchtuple isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING) else world = UInt64(f.world) if Core.Compiler.is_source_inferred(f.source.source) # OC was constructed from inferred source. There's only one # specialization and we can't infer anything more precise either. world = f.source.primary_world mi = f.source.specializations::Core.MethodInstance Core.Compiler.hasintersect(typeof(f).parameters[1], t) || (warning = OC_MISMATCH_WARNING) else mi = Core.Compiler.specialize_method(f.source, Tuple{typeof(f.captures), t.parameters...}, Core.svec()) actual = isdispatchtuple(mi.specTypes) isdispatchtuple(mi.specTypes) || (warning = GENERIC_SIG_WARNING) end end # get the code for it if debuginfo === :default debuginfo = :source elseif debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end if native if syntax !== :att && syntax !== :intel throw(ArgumentError("'syntax' must be either :intel or :att")) end if dump_module # we want module metadata, so use LLVM to generate assembly output str = _dump_function_native_assembly(mi, world, wrapper, syntax, debuginfo, binary, raw, params) else # if we don't want the module metadata, just disassemble what our JIT has str = _dump_function_native_disassembly(mi, world, wrapper, syntax, debuginfo, binary) end else str = _dump_function_llvm(mi, world, wrapper, !raw, dump_module, optimize, debuginfo, params) end str = warning * str return str end function _dump_function_native_disassembly(mi::Core.MethodInstance, world::UInt, wrapper::Bool, syntax::Symbol, debuginfo::Symbol, binary::Bool) str = @ccall jl_dump_method_asm(mi::Any, world::UInt, false::Bool, wrapper::Bool, syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8}, binary::Bool)::Ref{String} return str end struct LLVMFDump tsm::Ptr{Cvoid} # opaque f::Ptr{Cvoid} # opaque end function _dump_function_native_assembly(mi::Core.MethodInstance, world::UInt, wrapper::Bool, syntax::Symbol, debuginfo::Symbol, binary::Bool, raw::Bool, params::CodegenParams) llvmf_dump = Ref{LLVMFDump}() @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump},mi::Any, world::UInt, wrapper::Bool, true::Bool, params::CodegenParams)::Cvoid llvmf_dump[].f == C_NULL && error("could not compile the specified method") str = @ccall jl_dump_function_asm(llvmf_dump::Ptr{LLVMFDump}, false::Bool, syntax::Ptr{UInt8}, debuginfo::Ptr{UInt8}, binary::Bool, raw::Bool)::Ref{String} return str end function _dump_function_llvm( mi::Core.MethodInstance, world::UInt, wrapper::Bool, strip_ir_metadata::Bool, dump_module::Bool, optimize::Bool, debuginfo::Symbol, params::CodegenParams) llvmf_dump = Ref{LLVMFDump}() @ccall jl_get_llvmf_defn(llvmf_dump::Ptr{LLVMFDump}, mi::Any, world::UInt, wrapper::Bool, optimize::Bool, params::CodegenParams)::Cvoid llvmf_dump[].f == C_NULL && error("could not compile the specified method") str = @ccall jl_dump_function_ir(llvmf_dump::Ptr{LLVMFDump}, strip_ir_metadata::Bool, dump_module::Bool, debuginfo::Ptr{UInt8})::Ref{String} return str end """ code_llvm([io=stdout,], f, types; raw=false, dump_module=false, optimize=true, debuginfo=:default) Prints the LLVM bitcodes generated for running the method matching the given generic function and type signature to `io`. If the `optimize` keyword is unset, the code will be shown before LLVM optimizations. All metadata and dbg.* calls are removed from the printed bitcode. For the full IR, set the `raw` keyword to true. To dump the entire module that encapsulates the function (with declarations), set the `dump_module` keyword to true. Keyword argument `debuginfo` may be one of source (default) or none, to specify the verbosity of code comments. """ function code_llvm(io::IO, @nospecialize(f), @nospecialize(types), raw::Bool, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default) d = _dump_function(f, types, false, false, raw, dump_module, :intel, optimize, debuginfo, false) if highlighting[:llvm] && get(io, :color, false)::Bool print_llvm(io, d) else print(io, d) end end code_llvm(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); raw::Bool=false, dump_module::Bool=false, optimize::Bool=true, debuginfo::Symbol=:default) = code_llvm(io, f, types, raw, dump_module, optimize, debuginfo) code_llvm(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); raw=false, dump_module=false, optimize=true, debuginfo::Symbol=:default) = code_llvm(stdout, f, types; raw, dump_module, optimize, debuginfo) """ code_native([io=stdout,], f, types; syntax=:intel, debuginfo=:default, binary=false, dump_module=true) Prints the native assembly instructions generated for running the method matching the given generic function and type signature to `io`. * Set assembly syntax by setting `syntax` to `:intel` (default) for intel syntax or `:att` for AT&T syntax. * Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`. * If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address. * If `dump_module` is `false`, do not print metadata such as rodata or directives. * If `raw` is `false`, uninteresting instructions (like the safepoint function prologue) are elided. See also: [`@code_native`](@ref), [`code_llvm`](@ref), [`code_typed`](@ref) and [`code_lowered`](@ref) """ function code_native(io::IO, @nospecialize(f), @nospecialize(types=Base.default_tt(f)); dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false, debuginfo::Symbol=:default, binary::Bool=false) d = _dump_function(f, types, true, false, raw, dump_module, syntax, true, debuginfo, binary) if highlighting[:native] && get(io, :color, false)::Bool print_native(io, d) else print(io, d) end end code_native(@nospecialize(f), @nospecialize(types=Base.default_tt(f)); dump_module::Bool=true, syntax::Symbol=:intel, raw::Bool=false, debuginfo::Symbol=:default, binary::Bool=false) = code_native(stdout, f, types; dump_module, syntax, raw, debuginfo, binary) code_native(::IO, ::Any, ::Symbol) = error("invalid code_native call") # resolve ambiguous call ## colorized IR and assembly printing const num_regex = r"^(?:\$?-?\d+|0x[0-9A-Fa-f]+|-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)$" function print_llvm(io::IO, code::String) buf = IOBuffer(code) for line in eachline(buf) m = match(r"^(\s*)((?:[^;]|;\")*)(.*)$", line) m === nothing && continue indent, tokens, comment = m.captures print(io, indent) print_llvm_tokens(io, tokens) printstyled_ll(io, comment, :comment) println(io) end end const llvm_types = r"^(?:void|half|float|double|x86_\w+|ppc_\w+|label|metadata|type|opaque|token|i\d+)$" const llvm_cond = r"^(?:[ou]?eq|[ou]?ne|[uso][gl][te]|ord|uno)$" # true|false function print_llvm_tokens(io, tokens) m = match(r"^((?:[^\s:]+:)?)(\s*)(.*)", tokens) if m !== nothing label, spaces, tokens = m.captures printstyled_ll(io, label, :label, spaces) end m = match(r"^(%[^\s=]+)(\s*)=(\s*)(.*)", tokens) if m !== nothing result, spaces, spaces2, tokens = m.captures printstyled_ll(io, result, :variable, spaces) printstyled_ll(io, '=', :default, spaces2) end m = match(r"^([a-z]\w*)(\s*)(.*)", tokens) if m !== nothing inst, spaces, tokens = m.captures iskeyword = occursin(r"^(?:define|declare|type)$", inst) || occursin("=", tokens) printstyled_ll(io, inst, iskeyword ? :keyword : :instruction, spaces) end print_llvm_operands(io, tokens) end function print_llvm_operands(io, tokens) while !isempty(tokens) tokens = print_llvm_operand(io, tokens) end return tokens end function print_llvm_operand(io, tokens) islabel = false while !isempty(tokens) m = match(r"^,(\s*)(.*)", tokens) if m !== nothing spaces, tokens = m.captures printstyled_ll(io, ',', :default, spaces) break end m = match(r"^(\*+|=)(\s*)(.*)", tokens) if m !== nothing sym, spaces, tokens = m.captures printstyled_ll(io, sym, :default, spaces) continue end m = match(r"^(\"[^\"]*\")(\s*)(.*)", tokens) if m !== nothing str, spaces, tokens = m.captures printstyled_ll(io, str, :variable, spaces) continue end m = match(r"^([({\[<])(\s*)(.*)", tokens) if m !== nothing bracket, spaces, tokens = m.captures printstyled_ll(io, bracket, :bracket, spaces) tokens = print_llvm_operands(io, tokens) # enter continue end m = match(r"^([)}\]>])(\s*)(.*)", tokens) if m !== nothing bracket, spaces, tokens = m.captures printstyled_ll(io, bracket, :bracket, spaces) break # leave end m = match(r"^([^\s,*=(){}\[\]<>]+)(\s*)(.*)", tokens) m === nothing && break token, spaces, tokens = m.captures if occursin(llvm_types, token) printstyled_ll(io, token, :type) islabel = token == "label" elseif occursin(llvm_cond, token) # condition code is instruction-level printstyled_ll(io, token, :instruction) elseif occursin(num_regex, token) printstyled_ll(io, token, :number) elseif occursin(r"^@.+$", token) printstyled_ll(io, token, :funcname) elseif occursin(r"^%.+$", token) islabel |= occursin(r"^%[^\d].*$", token) & occursin(r"^\]", tokens) printstyled_ll(io, token, islabel ? :label : :variable) islabel = false elseif occursin(r"^[a-z]\w+$", token) printstyled_ll(io, token, :keyword) else printstyled_ll(io, token, :default) end print(io, spaces) end return tokens end function print_native(io::IO, code::String, arch::Symbol=sys_arch_category()) archv = Val(arch) buf = IOBuffer(code) for line in eachline(buf) m = match(r"^(\s*)((?:[^;#/]|#\S|;\"|/[^/])*)(.*)$", line) m === nothing && continue indent, tokens, comment = m.captures print(io, indent) print_native_tokens(io, tokens, archv) printstyled_ll(io, comment, :comment) println(io) end end function sys_arch_category() if Sys.ARCH === :x86_64 || Sys.ARCH === :i686 :x86 elseif Sys.ARCH === :aarch64 || startswith(string(Sys.ARCH), "arm") :arm else :unsupported end end print_native_tokens(io, line, ::Val) = print(io, line) const x86_ptr = r"^(?:(?:[xyz]mm|[dq])?word|byte|ptr|offset)$" const avx512flags = r"^(?:z|r[nduz]-sae|sae|1to1?\d)$" const arm_cond = r"^(?:eq|ne|cs|ho|cc|lo|mi|pl|vs|vc|hi|ls|[lg][te]|al|nv)$" const arm_keywords = r"^(?:lsl|lsr|asr|ror|rrx|!|/[zm])$" function print_native_tokens(io, tokens, arch::Union{Val{:x86}, Val{:arm}}) x86 = arch isa Val{:x86} m = match(r"^((?:[^\s:]+:|\"[^\"]+\":)?)(\s*)(.*)", tokens) if m !== nothing label, spaces, tokens = m.captures printstyled_ll(io, label, :label, spaces) end haslabel = false m = match(r"^([a-z][\w.]*)(\s*)(.*)", tokens) if m !== nothing instruction, spaces, tokens = m.captures printstyled_ll(io, instruction, :instruction, spaces) haslabel = occursin(r"^(?:bl?|bl?\.\w{2,5}|[ct]bn?z)?$", instruction) end isfuncname = false while !isempty(tokens) m = match(r"^([,:*])(\s*)(.*)", tokens) if m !== nothing sym, spaces, tokens = m.captures printstyled_ll(io, sym, :default, spaces) isfuncname = false continue end m = match(r"^([(){}\[\]])(\s*)(.*)", tokens) if m !== nothing bracket, spaces, tokens = m.captures printstyled_ll(io, bracket, :bracket, spaces) continue end m = match(r"^#([0-9a-fx.-]+)(\s*)(.*)", tokens) if !x86 && m !== nothing && occursin(num_regex, m.captures[1]) num, spaces, tokens = m.captures printstyled_ll(io, "#" * num, :number, spaces) continue end m = match(r"^([^\s,:*(){}\[\]][^\s,:*/(){}\[\]]*)(\s*)(.*)", tokens) m === nothing && break token, spaces, tokens = m.captures if occursin(num_regex, token) printstyled_ll(io, token, :number) elseif x86 && occursin(x86_ptr, token) || occursin(avx512flags, token) printstyled_ll(io, token, :keyword) isfuncname = token == "offset" elseif !x86 && (occursin(arm_keywords, token) || occursin(arm_cond, token)) printstyled_ll(io, token, :keyword) elseif occursin(r"^L.+$", token) printstyled_ll(io, token, :label) elseif occursin(r"^\$.+$", token) printstyled_ll(io, token, :funcname) elseif occursin(r"^%?(?:[a-z][\w.]+|\"[^\"]+\")$", token) islabel = haslabel & !occursin(',', tokens) printstyled_ll(io, token, islabel ? :label : isfuncname ? :funcname : :variable) isfuncname = false else printstyled_ll(io, token, :default) end print(io, spaces) end end z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/InteractiveUtils/src/macros.jl^;������# This file is a part of Julia. License is MIT: https://julialang.org/license # macro wrappers for various reflection functions import Base: typesof, insert!, replace_ref_begin_end!, infer_effects separate_kwargs(args...; kwargs...) = (args, values(kwargs)) """ Transform a dot expression into one where each argument has been replaced by a variable "xj" (with j an integer from 1 to the returned i). The list `args` contains the original arguments that have been replaced. """ function recursive_dotcalls!(ex, args, i=1) if !(ex isa Expr) || ((ex.head !== :. || !(ex.args[2] isa Expr)) && (ex.head !== :call || string(ex.args[1])[1] != '.')) newarg = Symbol('x', i) if Meta.isexpr(ex, :...) push!(args, only(ex.args)) return Expr(:..., newarg), i+1 else push!(args, ex) return newarg, i+1 end end (start, branches) = ex.head === :. ? (1, ex.args[2].args) : (2, ex.args) length_branches = length(branches)::Int for j in start:length_branches branch, i = recursive_dotcalls!(branches[j], args, i) branches[j] = branch end return ex, i end function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[]) if Meta.isexpr(ex0, :ref) ex0 = replace_ref_begin_end!(ex0) end if isa(ex0, Expr) if ex0.head === :do && Meta.isexpr(get(ex0.args, 1, nothing), :call) if length(ex0.args) != 2 return Expr(:call, :error, "ill-formed do call") end i = findlast(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args[1].args) args = copy(ex0.args[1].args) insert!(args, (isnothing(i) ? 2 : 1+i::Int), ex0.args[2]) ex0 = Expr(:call, args...) end if ex0.head === :. || (ex0.head === :call && ex0.args[1] !== :.. && string(ex0.args[1])[1] == '.') codemacro = startswith(string(fcn), "code_") if codemacro && (ex0.head === :call || ex0.args[2] isa Expr) # Manually wrap a dot call in a function args = Any[] ex, i = recursive_dotcalls!(copy(ex0), args) xargs = [Symbol('x', j) for j in 1:i-1] dotfuncname = gensym("dotfunction") dotfuncdef = Expr(:local, Expr(:(=), Expr(:call, dotfuncname, xargs...), ex)) return quote $(esc(dotfuncdef)) local args = $typesof($(map(esc, args)...)) $(fcn)($(esc(dotfuncname)), args; $(kws...)) end elseif !codemacro fully_qualified_symbol = true # of the form A.B.C.D ex1 = ex0 while ex1 isa Expr && ex1.head === :. fully_qualified_symbol = (length(ex1.args) == 2 && ex1.args[2] isa QuoteNode && ex1.args[2].value isa Symbol) fully_qualified_symbol || break ex1 = ex1.args[1] end fully_qualified_symbol &= ex1 isa Symbol if fully_qualified_symbol return quote local arg1 = $(esc(ex0.args[1])) if isa(arg1, Module) $(if string(fcn) == "which" :(which(arg1, $(ex0.args[2]))) else :(error("expression is not a function call")) end) else local args = $typesof($(map(esc, ex0.args)...)) $(fcn)(Base.getproperty, args) end end else return Expr(:call, :error, "dot expressions are not lowered to " * "a single function call, so @$fcn cannot analyze " * "them. You may want to use Meta.@lower to identify " * "which function call to target.") end end end if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args) return quote local arg1 = $(esc(ex0.args[1])) local args, kwargs = $separate_kwargs($(map(esc, ex0.args[2:end])...)) $(fcn)(Core.kwcall, Tuple{typeof(kwargs), Core.Typeof(arg1), map(Core.Typeof, args)...}; $(kws...)) end elseif ex0.head === :call return Expr(:call, fcn, esc(ex0.args[1]), Expr(:call, typesof, map(esc, ex0.args[2:end])...), kws...) elseif ex0.head === :(=) && length(ex0.args) == 2 lhs, rhs = ex0.args if isa(lhs, Expr) if lhs.head === :(.) return Expr(:call, fcn, Base.setproperty!, Expr(:call, typesof, map(esc, lhs.args)..., esc(rhs)), kws...) elseif lhs.head === :ref return Expr(:call, fcn, Base.setindex!, Expr(:call, typesof, esc(lhs.args[1]), esc(rhs), map(esc, lhs.args[2:end])...), kws...) end end elseif ex0.head === :vcat || ex0.head === :typed_vcat if ex0.head === :vcat f, hf = Base.vcat, Base.hvcat args = ex0.args else f, hf = Base.typed_vcat, Base.typed_hvcat args = ex0.args[2:end] end if any(a->isa(a,Expr) && a.head === :row, args) rows = Any[ (isa(x,Expr) && x.head === :row ? x.args : Any[x]) for x in args ] lens = map(length, rows) return Expr(:call, fcn, hf, Expr(:call, typesof, (ex0.head === :vcat ? [] : Any[esc(ex0.args[1])])..., Expr(:tuple, lens...), map(esc, vcat(rows...))...), kws...) else return Expr(:call, fcn, f, Expr(:call, typesof, map(esc, ex0.args)...), kws...) end else for (head, f) in (:ref => Base.getindex, :hcat => Base.hcat, :(.) => Base.getproperty, :vect => Base.vect, Symbol("'") => Base.adjoint, :typed_hcat => Base.typed_hcat, :string => string) if ex0.head === head return Expr(:call, fcn, f, Expr(:call, typesof, map(esc, ex0.args)...), kws...) end end end end if isa(ex0, Expr) && ex0.head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions* return Expr(:call, fcn, esc(ex0.args[1]), Tuple{#=__source__=#LineNumberNode, #=__module__=#Module, Any[ Core.Typeof(a) for a in ex0.args[3:end] ]...}, kws...) end ex = Meta.lower(__module__, ex0) if !isa(ex, Expr) return Expr(:call, :error, "expression is not a function call or symbol") end exret = Expr(:none) if ex.head === :call if any(e->(isa(e, Expr) && e.head === :(...)), ex0.args) && (ex.args[1] === GlobalRef(Core,:_apply_iterate) || ex.args[1] === GlobalRef(Base,:_apply_iterate)) # check for splatting exret = Expr(:call, ex.args[2], fcn, Expr(:tuple, esc(ex.args[3]), Expr(:call, typesof, map(esc, ex.args[4:end])...))) else exret = Expr(:call, fcn, esc(ex.args[1]), Expr(:call, typesof, map(esc, ex.args[2:end])...), kws...) end end if ex.head === :thunk || exret.head === :none exret = Expr(:call, :error, "expression is not a function call, " * "or is too complex for @$fcn to analyze; " * "break it down to simpler parts if possible. " * "In some cases, you may want to use Meta.@lower.") end return exret end """ Same behaviour as `gen_call_with_extracted_types` except that keyword arguments of the form "foo=bar" are passed on to the called function as well. The keyword arguments must be given before the mandatory argument. """ function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0) kws = Expr[] arg = ex0[end] # Mandatory argument for i in 1:length(ex0)-1 x = ex0[i] if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar" if length(x.args) != 2 return Expr(:call, :error, "Invalid keyword argument: $x") end push!(kws, Expr(:kw, esc(x.args[1]), esc(x.args[2]))) else return Expr(:call, :error, "@$fcn expects only one non-keyword argument") end end return gen_call_with_extracted_types(__module__, fcn, arg, kws) end for fname in [:which, :less, :edit, :functionloc] @eval begin macro ($fname)(ex0) gen_call_with_extracted_types(__module__, $(Expr(:quote, fname)), ex0) end end end macro which(ex0::Symbol) ex0 = QuoteNode(ex0) return :(which($__module__, $ex0)) end for fname in [:code_warntype, :code_llvm, :code_native, :infer_effects] @eval begin macro ($fname)(ex0...) gen_call_with_extracted_types_and_kwargs(__module__, $(Expr(:quote, fname)), ex0) end end end macro code_typed(ex0...) thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_typed, ex0) quote local results = $thecall length(results) == 1 ? results[1] : results end end macro code_lowered(ex0...) thecall = gen_call_with_extracted_types_and_kwargs(__module__, :code_lowered, ex0) quote local results = $thecall length(results) == 1 ? results[1] : results end end macro time_imports(ex) quote try Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) $(esc(ex)) finally Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) end end end """ @functionloc Applied to a function or macro call, it evaluates the arguments to the specified call, and returns a tuple `(filename,line)` giving the location for the method that would be called for those arguments. It calls out to the [`functionloc`](@ref) function. """ :@functionloc """ @which Applied to a function or macro call, it evaluates the arguments to the specified call, and returns the `Method` object for the method that would be called for those arguments. Applied to a variable, it returns the module in which the variable was bound. It calls out to the [`which`](@ref) function. See also: [`@less`](@ref), [`@edit`](@ref). """ :@which """ @less Evaluates the arguments to the function or macro call, determines their types, and calls the [`less`](@ref) function on the resulting expression. See also: [`@edit`](@ref), [`@which`](@ref), [`@code_lowered`](@ref). """ :@less """ @edit Evaluates the arguments to the function or macro call, determines their types, and calls the [`edit`](@ref) function on the resulting expression. See also: [`@less`](@ref), [`@which`](@ref). """ :@edit """ @code_typed Evaluates the arguments to the function or macro call, determines their types, and calls [`code_typed`](@ref) on the resulting expression. Use the optional argument `optimize` with @code_typed optimize=true foo(x) to control whether additional optimizations, such as inlining, are also applied. """ :@code_typed """ @code_lowered Evaluates the arguments to the function or macro call, determines their types, and calls [`code_lowered`](@ref) on the resulting expression. """ :@code_lowered """ @code_warntype Evaluates the arguments to the function or macro call, determines their types, and calls [`code_warntype`](@ref) on the resulting expression. """ :@code_warntype """ @code_llvm Evaluates the arguments to the function or macro call, determines their types, and calls [`code_llvm`](@ref) on the resulting expression. Set the optional keyword arguments `raw`, `dump_module`, `debuginfo`, `optimize` by putting them and their value before the function call, like this: @code_llvm raw=true dump_module=true debuginfo=:default f(x) @code_llvm optimize=false f(x) `optimize` controls whether additional optimizations, such as inlining, are also applied. `raw` makes all metadata and dbg.* calls visible. `debuginfo` may be one of `:source` (default) or `:none`, to specify the verbosity of code comments. `dump_module` prints the entire module that encapsulates the function. """ :@code_llvm """ @code_native Evaluates the arguments to the function or macro call, determines their types, and calls [`code_native`](@ref) on the resulting expression. Set any of the optional keyword arguments `syntax`, `debuginfo`, `binary` or `dump_module` by putting it before the function call, like this: @code_native syntax=:intel debuginfo=:default binary=true dump_module=false f(x) * Set assembly syntax by setting `syntax` to `:intel` (default) for Intel syntax or `:att` for AT&T syntax. * Specify verbosity of code comments by setting `debuginfo` to `:source` (default) or `:none`. * If `binary` is `true`, also print the binary machine code for each instruction precedented by an abbreviated address. * If `dump_module` is `false`, do not print metadata such as rodata or directives. See also: [`code_native`](@ref), [`@code_llvm`](@ref), [`@code_typed`](@ref) and [`@code_lowered`](@ref) """ :@code_native """ @time_imports A macro to execute an expression and produce a report of any time spent importing packages and their dependencies. Any compilation time will be reported as a percentage, and how much of which was recompilation, if any. One line is printed per package or package extension. The duration shown is the time to import that package itself, not including the time to load any of its dependencies. On Julia 1.9+ [package extensions](@ref man-extensions) will show as Parent โ†’ Extension. !!! note During the load process a package sequentially imports all of its dependencies, not just its direct dependencies. ```julia-repl julia> @time_imports using CSV 50.7 ms Parsers 17.52% compilation time 0.2 ms DataValueInterfaces 1.6 ms DataAPI 0.1 ms IteratorInterfaceExtensions 0.1 ms TableTraits 17.5 ms Tables 26.8 ms PooledArrays 193.7 ms SentinelArrays 75.12% compilation time 8.6 ms InlineStrings 20.3 ms WeakRefStrings 2.0 ms TranscodingStreams 1.4 ms Zlib_jll 1.8 ms CodecZlib 0.8 ms Compat 13.1 ms FilePathsBase 28.39% compilation time 1681.2 ms CSV 92.40% compilation time ``` !!! compat "Julia 1.8" This macro requires at least Julia 1.8 """ :@time_imports }���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/InteractiveUtils/src/clipboard.jlj������# This file is a part of Julia. License is MIT: https://julialang.org/license # clipboard copy and paste if Sys.isapple() function clipboard(x) pbcopy_cmd = `pbcopy` # OSX shells, especially when run within `tmux` or `screen`, can be # disconnected from the global shell namespace, which causes problems # with clipboards. Luckily, the `reattach-to-user-namespace` utility # dodges these issues quite nicely, so we automatically utilize it if # it is installed. if Sys.which("reattach-to-user-namespace") !== nothing pbcopy_cmd = `reattach-to-user-namespace pbcopy` end open(pipeline(pbcopy_cmd, stderr=stderr), "w") do io print(io, x) end nothing end function clipboard() pbpaste_cmd = `pbpaste` # See above comment in `clipboard(x)` if Sys.which("reattach-to-user-namespace") !== nothing pbpaste_cmd = `reattach-to-user-namespace pbpaste` end return read(pbpaste_cmd, String) end elseif Sys.islinux() || Sys.KERNEL === :FreeBSD _clipboardcmd = nothing const _clipboard_copy = Dict( :xsel => Sys.islinux() ? `xsel --input --clipboard` : `xsel -c`, :xclip => `xclip -silent -in -selection clipboard`, :wlclipboard => `wl-copy` ) const _clipboard_paste = Dict( :xsel => Sys.islinux() ? `xsel --nodetach --output --clipboard` : `xsel -p`, :xclip => `xclip -quiet -out -selection clipboard`, :wlclipboard => `wl-paste` ) function clipboardcmd() global _clipboardcmd _clipboardcmd !== nothing && return _clipboardcmd for cmd in (:xclip, :xsel, :wlclipboard) # wl-clipboard ships wl-copy/paste individually c = cmd === :wlclipboard ? Symbol("wl-copy") : cmd success(pipeline(`which $c`, devnull)) && return _clipboardcmd = cmd end pkgs = @static if Sys.KERNEL === :FreeBSD "x11/xsel or x11/xclip" else "xsel or xclip or wl-clipboard" end error("no clipboard command found, please install $pkgs") end function clipboard(x) c = clipboardcmd() cmd = _clipboard_copy[c] open(pipeline(cmd, stderr=stderr), "w") do io print(io, x) end nothing end function clipboard() c = clipboardcmd() cmd = _clipboard_paste[c] return read(pipeline(cmd, stderr=stderr), String) end elseif Sys.iswindows() function clipboard(x::AbstractString) if Base.containsnul(x) throw(ArgumentError("Windows clipboard strings cannot contain NUL character")) end x_u16 = Base.cwstring(x) pdata = Ptr{UInt16}(C_NULL) function cleanup(cause) errno = cause === :success ? UInt32(0) : Libc.GetLastError() if cause !== :OpenClipboard if cause !== :success && pdata != C_NULL ccall((:GlobalFree, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata) end ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror(:CloseClipboard) # this should never fail end cause === :success || Base.windowserror(cause, errno) nothing end ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL) == 0 && return Base.windowserror(:OpenClipboard) ccall((:EmptyClipboard, "user32"), stdcall, Cint, ()) == 0 && return cleanup(:EmptyClipboard) # copy data to locked, allocated space pdata = ccall((:GlobalAlloc, "kernel32"), stdcall, Ptr{UInt16}, (Cuint, Csize_t), 2 #=GMEM_MOVEABLE=#, sizeof(x_u16)) pdata == C_NULL && return cleanup(:GlobalAlloc) plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata) plock == C_NULL && return cleanup(:GlobalLock) GC.@preserve x_u16 memcpy(plock, Base.unsafe_convert(Ptr{UInt16}, x_u16), sizeof(x_u16)) unlock = ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata) (unlock == 0 && Libc.GetLastError() == 0) || return cleanup(:GlobalUnlock) # this should never fail pset = ccall((:SetClipboardData, "user32"), stdcall, Ptr{UInt16}, (Cuint, Ptr{UInt16}), 13, pdata) # CF_UNICODETEXT pdata != pset && return cleanup(:SetClipboardData) cleanup(:success) end clipboard(x) = clipboard(sprint(print, x)::String) function clipboard() function cleanup(cause) errno = cause === :success ? UInt32(0) : Libc.GetLastError() if cause !== :OpenClipboard ccall((:CloseClipboard, "user32"), stdcall, Cint, ()) == 0 && Base.windowserror(:CloseClipboard) # this should never fail end if cause !== :success && !(cause === :GetClipboardData && (errno == 0x8004006A || errno == 0x800401D3)) # ignore DV_E_CLIPFORMAT and CLIPBRD_E_BAD_DATA from GetClipboardData Base.windowserror(cause, errno) end "" end ccall((:OpenClipboard, "user32"), stdcall, Cint, (Ptr{Cvoid},), C_NULL) == 0 && return Base.windowserror(:OpenClipboard) ccall(:SetLastError, stdcall, Cvoid, (UInt32,), 0) # allow distinguishing if the clipboard simply didn't have text pdata = ccall((:GetClipboardData, "user32"), stdcall, Ptr{UInt16}, (Cuint,), 13) # CF_UNICODETEXT pdata == C_NULL && return cleanup(:GetClipboardData) plock = ccall((:GlobalLock, "kernel32"), stdcall, Ptr{UInt16}, (Ptr{UInt16},), pdata) plock == C_NULL && return cleanup(:GlobalLock) s = try # find NUL terminator (0x0000 16-bit code unit) len = 0 while unsafe_load(plock, len + 1) != 0 len += 1 end # get Vector{UInt16}, transcode data to UTF-8, make a String of it transcode(String, unsafe_wrap(Array, plock, len)) finally unlock = ccall((:GlobalUnlock, "kernel32"), stdcall, Cint, (Ptr{UInt16},), pdata) (unlock != 0 || Libc.GetLastError() == 0) || return cleanup(:GlobalUnlock) # this should never fail cleanup(:success) end return s end else clipboard(x="") = error("`clipboard` function not implemented for $(Sys.KERNEL)") end """ clipboard(x) Send a printed form of `x` to the operating system clipboard ("copy"). """ clipboard(x) """ clipboard() -> String Return a string with the contents of the operating system clipboard ("paste"). """ clipboard() r���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/LibGit2.jlว‡������# This file is a part of Julia. License is MIT: https://julialang.org/license """ Interface to [libgit2](https://libgit2.org/). """ module LibGit2 import Base: == using Base: something, notnothing using Base64: base64decode using NetworkOptions using Printf: @printf using SHA: sha1, sha256 export with, GitRepo, GitConfig using LibGit2_jll const GITHUB_REGEX = r"^(?:(?:ssh://)?git@|git://|https://(?:[\w\.\+\-]+@)?)github.com[:/](([^/].+)/(.+?))(?:\.git)?$"i const REFCOUNT = Threads.Atomic{Int}(0) function ensure_initialized end include("error.jl") include("utils.jl") include("consts.jl") include("types.jl") include("signature.jl") include("oid.jl") include("reference.jl") include("commit.jl") include("repository.jl") include("config.jl") include("walker.jl") include("remote.jl") include("strarray.jl") include("index.jl") include("merge.jl") include("tag.jl") include("blob.jl") include("diff.jl") include("rebase.jl") include("blame.jl") include("status.jl") include("tree.jl") include("gitcredential.jl") include("callbacks.jl") using .Error struct State head::GitHash index::GitHash work::GitHash end """ head(pkg::AbstractString) -> String Return current HEAD [`GitHash`](@ref) of the `pkg` repo as a string. """ function head(pkg::AbstractString) with(GitRepo, pkg) do repo string(head_oid(repo)) end end """ need_update(repo::GitRepo) Equivalent to `git update-index`. Return `true` if `repo` needs updating. """ function need_update(repo::GitRepo) if !isbare(repo) # read updates index from filesystem read!(repo, true) end end """ iscommit(id::AbstractString, repo::GitRepo) -> Bool Check if commit `id` (which is a [`GitHash`](@ref) in string form) is in the repository. # Examples ```julia-repl julia> repo = GitRepo(repo_path); julia> LibGit2.add!(repo, test_file); julia> commit_oid = LibGit2.commit(repo, "add test_file"); julia> LibGit2.iscommit(string(commit_oid), repo) true ``` """ function iscommit(id::AbstractString, repo::GitRepo) res = true try c = GitCommit(repo, id) if c === nothing res = false else close(c) end catch res = false end return res end """ LibGit2.isdirty(repo::GitRepo, pathspecs::AbstractString=""; cached::Bool=false) -> Bool Check if there have been any changes to tracked files in the working tree (if `cached=false`) or the index (if `cached=true`). `pathspecs` are the specifications for options for the diff. # Examples ```julia repo = LibGit2.GitRepo(repo_path) LibGit2.isdirty(repo) # should be false open(joinpath(repo_path, new_file), "a") do f println(f, "here's my cool new file") end LibGit2.isdirty(repo) # now true LibGit2.isdirty(repo, new_file) # now true ``` Equivalent to `git diff-index HEAD [-- <pathspecs>]`. """ isdirty(repo::GitRepo, paths::AbstractString=""; cached::Bool=false) = isdiff(repo, Consts.HEAD_FILE, paths, cached=cached) """ LibGit2.isdiff(repo::GitRepo, treeish::AbstractString, pathspecs::AbstractString=""; cached::Bool=false) Checks if there are any differences between the tree specified by `treeish` and the tracked files in the working tree (if `cached=false`) or the index (if `cached=true`). `pathspecs` are the specifications for options for the diff. # Examples ```julia repo = LibGit2.GitRepo(repo_path) LibGit2.isdiff(repo, "HEAD") # should be false open(joinpath(repo_path, new_file), "a") do f println(f, "here's my cool new file") end LibGit2.isdiff(repo, "HEAD") # now true ``` Equivalent to `git diff-index <treeish> [-- <pathspecs>]`. """ function isdiff(repo::GitRepo, treeish::AbstractString, paths::AbstractString=""; cached::Bool=false) tree = GitTree(repo, "$treeish^{tree}") try diff = diff_tree(repo, tree, paths, cached=cached) result = count(diff) > 0 close(diff) return result finally close(tree) end end """ diff_files(repo::GitRepo, branch1::AbstractString, branch2::AbstractString; kwarg...) -> Vector{AbstractString} Show which files have changed in the git repository `repo` between branches `branch1` and `branch2`. The keyword argument is: * `filter::Set{Consts.DELTA_STATUS}=Set([Consts.DELTA_ADDED, Consts.DELTA_MODIFIED, Consts.DELTA_DELETED]))`, and it sets options for the diff. The default is to show files added, modified, or deleted. Return only the *names* of the files which have changed, *not* their contents. # Examples ```julia LibGit2.branch!(repo, "branch/a") LibGit2.branch!(repo, "branch/b") # add a file to repo open(joinpath(LibGit2.path(repo),"file"),"w") do f write(f, "hello repo\n") end LibGit2.add!(repo, "file") LibGit2.commit(repo, "add file") # returns ["file"] filt = Set([LibGit2.Consts.DELTA_ADDED]) files = LibGit2.diff_files(repo, "branch/a", "branch/b", filter=filt) # returns [] because existing files weren't modified filt = Set([LibGit2.Consts.DELTA_MODIFIED]) files = LibGit2.diff_files(repo, "branch/a", "branch/b", filter=filt) ``` Equivalent to `git diff --name-only --diff-filter=<filter> <branch1> <branch2>`. """ function diff_files(repo::GitRepo, branch1::AbstractString, branch2::AbstractString; filter::Set{Consts.DELTA_STATUS}=Set([Consts.DELTA_ADDED, Consts.DELTA_MODIFIED, Consts.DELTA_DELETED])) b1_id = revparseid(repo, branch1*"^{tree}") b2_id = revparseid(repo, branch2*"^{tree}") tree1 = GitTree(repo, b1_id) tree2 = GitTree(repo, b2_id) files = AbstractString[] try diff = diff_tree(repo, tree1, tree2) for i in 1:count(diff) delta = diff[i] delta === nothing && break if Consts.DELTA_STATUS(delta.status) in filter Base.push!(files, unsafe_string(delta.new_file.path)) end end close(diff) finally close(tree1) close(tree2) end return files end """ is_ancestor_of(a::AbstractString, b::AbstractString, repo::GitRepo) -> Bool Return `true` if `a`, a [`GitHash`](@ref) in string form, is an ancestor of `b`, a [`GitHash`](@ref) in string form. # Examples ```julia-repl julia> repo = GitRepo(repo_path); julia> LibGit2.add!(repo, test_file1); julia> commit_oid1 = LibGit2.commit(repo, "commit1"); julia> LibGit2.add!(repo, test_file2); julia> commit_oid2 = LibGit2.commit(repo, "commit2"); julia> LibGit2.is_ancestor_of(string(commit_oid1), string(commit_oid2), repo) true ``` """ function is_ancestor_of(a::AbstractString, b::AbstractString, repo::GitRepo) A = revparseid(repo, a) merge_base(repo, a, b) == A end """ fetch(repo::GitRepo; kwargs...) Fetches updates from an upstream of the repository `repo`. The keyword arguments are: * `remote::AbstractString="origin"`: which remote, specified by name, of `repo` to fetch from. If this is empty, the URL will be used to construct an anonymous remote. * `remoteurl::AbstractString=""`: the URL of `remote`. If not specified, will be assumed based on the given name of `remote`. * `refspecs=AbstractString[]`: determines properties of the fetch. * `credentials=nothing`: provides credentials and/or settings when authenticating against a private `remote`. * `callbacks=Callbacks()`: user provided callbacks and payloads. Equivalent to `git fetch [<remoteurl>|<repo>] [<refspecs>]`. """ function fetch(repo::GitRepo; remote::AbstractString="origin", remoteurl::AbstractString="", refspecs::Vector{<:AbstractString}=AbstractString[], credentials::Creds=nothing, callbacks::Callbacks=Callbacks()) rmt = if isempty(remoteurl) get(GitRemote, repo, remote) else GitRemoteAnon(repo, remoteurl) end cred_payload = reset!(CredentialPayload(credentials), GitConfig(repo)) if !haskey(callbacks, :credentials) callbacks[:credentials] = (credentials_cb(), cred_payload) elseif haskey(callbacks, :credentials) && credentials !== nothing throw(ArgumentError(string( "Unable to both use the provided `credentials` as a payload when the ", "`callbacks` also contain a credentials payload."))) end result = try remote_callbacks = RemoteCallbacks(callbacks) fo = FetchOptions(callbacks=remote_callbacks) fetch(rmt, refspecs, msg="from $(url(rmt))", options=fo) catch err if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) end rethrow() finally close(rmt) end approve(cred_payload) return result end """ push(repo::GitRepo; kwargs...) Pushes updates to an upstream of `repo`. The keyword arguments are: * `remote::AbstractString="origin"`: the name of the upstream remote to push to. * `remoteurl::AbstractString=""`: the URL of `remote`. * `refspecs=AbstractString[]`: determines properties of the push. * `force::Bool=false`: determines if the push will be a force push, overwriting the remote branch. * `credentials=nothing`: provides credentials and/or settings when authenticating against a private `remote`. * `callbacks=Callbacks()`: user provided callbacks and payloads. Equivalent to `git push [<remoteurl>|<repo>] [<refspecs>]`. """ function push(repo::GitRepo; remote::AbstractString="origin", remoteurl::AbstractString="", refspecs::Vector{<:AbstractString}=AbstractString[], force::Bool=false, credentials::Creds=nothing, callbacks::Callbacks=Callbacks()) rmt = if isempty(remoteurl) get(GitRemote, repo, remote) else GitRemoteAnon(repo, remoteurl) end cred_payload = reset!(CredentialPayload(credentials), GitConfig(repo)) if !haskey(callbacks, :credentials) callbacks[:credentials] = (credentials_cb(), cred_payload) elseif haskey(callbacks, :credentials) && credentials !== nothing throw(ArgumentError(string( "Unable to both use the provided `credentials` as a payload when the ", "`callbacks` also contain a credentials payload."))) end result = try remote_callbacks = RemoteCallbacks(callbacks) push_opts = PushOptions(callbacks=remote_callbacks) push(rmt, refspecs, force=force, options=push_opts) catch err if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) end rethrow() finally close(rmt) end approve(cred_payload) return result end """ branch(repo::GitRepo) Equivalent to `git branch`. Create a new branch from the current HEAD. """ function branch(repo::GitRepo) head_ref = head(repo) try branch(head_ref) finally close(head_ref) end end """ branch!(repo::GitRepo, branch_name::AbstractString, commit::AbstractString=""; kwargs...) Checkout a new git branch in the `repo` repository. `commit` is the [`GitHash`](@ref), in string form, which will be the start of the new branch. If `commit` is an empty string, the current HEAD will be used. The keyword arguments are: * `track::AbstractString=""`: the name of the remote branch this new branch should track, if any. If empty (the default), no remote branch will be tracked. * `force::Bool=false`: if `true`, branch creation will be forced. * `set_head::Bool=true`: if `true`, after the branch creation finishes the branch head will be set as the HEAD of `repo`. Equivalent to `git checkout [-b|-B] <branch_name> [<commit>] [--track <track>]`. # Examples ```julia repo = LibGit2.GitRepo(repo_path) LibGit2.branch!(repo, "new_branch", set_head=false) ``` """ function branch!(repo::GitRepo, branch_name::AbstractString, commit::AbstractString = ""; # start point track::AbstractString = "", # track remote branch force::Bool=false, # force branch creation set_head::Bool=true) # set as head reference on exit # try to lookup branch first branch_ref = force ? nothing : lookup_branch(repo, branch_name) if branch_ref === nothing branch_rmt_ref = isempty(track) ? nothing : lookup_branch(repo, "$track/$branch_name", true) # if commit is empty get head commit oid commit_id = if isempty(commit) if branch_rmt_ref === nothing with(head(repo)) do head_ref with(peel(GitCommit, head_ref)) do hrc GitHash(hrc) end end else tmpcmt = with(peel(GitCommit, branch_rmt_ref)) do hrc GitHash(hrc) end close(branch_rmt_ref) tmpcmt end else GitHash(commit) end iszero(commit_id) && return cmt = GitCommit(repo, commit_id) new_branch_ref = nothing try new_branch_ref = create_branch(repo, branch_name, cmt, force=force) finally close(cmt) new_branch_ref === nothing && throw(GitError(Error.Object, Error.ERROR, "cannot create branch `$branch_name` with `$commit_id`")) branch_ref = new_branch_ref end end try #TODO: what if branch tracks other then "origin" remote if !isempty(track) # setup tracking try with(GitConfig, repo) do cfg set!(cfg, "branch.$branch_name.remote", Consts.REMOTE_ORIGIN) set!(cfg, "branch.$branch_name.merge", name(branch_ref)) end catch @warn "Please provide remote tracking for branch '$branch_name' in '$(path(repo))'" end end if set_head # checkout selected branch with(peel(GitTree, branch_ref)) do btree checkout_tree(repo, btree) end # switch head to the branch head!(repo, branch_ref) end finally close(branch_ref) end return end """ checkout!(repo::GitRepo, commit::AbstractString=""; force::Bool=true) Equivalent to `git checkout [-f] --detach <commit>`. Checkout the git commit `commit` (a [`GitHash`](@ref) in string form) in `repo`. If `force` is `true`, force the checkout and discard any current changes. Note that this detaches the current HEAD. # Examples ```julia repo = LibGit2.GitRepo(repo_path) open(joinpath(LibGit2.path(repo), "file1"), "w") do f write(f, "111\n") end LibGit2.add!(repo, "file1") commit_oid = LibGit2.commit(repo, "add file1") open(joinpath(LibGit2.path(repo), "file1"), "w") do f write(f, "112\n") end # would fail without the force=true # since there are modifications to the file LibGit2.checkout!(repo, string(commit_oid), force=true) ``` """ function checkout!(repo::GitRepo, commit::AbstractString = ""; force::Bool = true) # nothing to do isempty(commit) && return # grab head name head_name = Consts.HEAD_FILE try with(head(repo)) do head_ref head_name = shortname(head_ref) # if it is HEAD use short OID instead if head_name == Consts.HEAD_FILE head_name = string(GitHash(head_ref)) end end catch end # search for commit to get a commit object obj = GitObject(repo, GitHash(commit)) peeled = peel(GitCommit, obj) obj_oid = GitHash(peeled) # checkout commit checkout_tree(repo, peeled, options = force ? CheckoutOptions(checkout_strategy = Consts.CHECKOUT_FORCE) : CheckoutOptions()) GitReference(repo, obj_oid, force=force, msg="libgit2.checkout: moving from $head_name to $(obj_oid))") return nothing end """ clone(repo_url::AbstractString, repo_path::AbstractString; kwargs...) Clone a remote repository located at `repo_url` to the local filesystem location `repo_path`. The keyword arguments are: * `branch::AbstractString=""`: which branch of the remote to clone, if not the default repository branch (usually `master`). * `isbare::Bool=false`: if `true`, clone the remote as a bare repository, which will make `repo_path` itself the git directory instead of `repo_path/.git`. This means that a working tree cannot be checked out. Plays the role of the git CLI argument `--bare`. * `remote_cb::Ptr{Cvoid}=C_NULL`: a callback which will be used to create the remote before it is cloned. If `C_NULL` (the default), no attempt will be made to create the remote - it will be assumed to already exist. * `credentials::Creds=nothing`: provides credentials and/or settings when authenticating against a private repository. * `callbacks::Callbacks=Callbacks()`: user provided callbacks and payloads. Equivalent to `git clone [-b <branch>] [--bare] <repo_url> <repo_path>`. # Examples ```julia repo_url = "https://github.com/JuliaLang/Example.jl" repo1 = LibGit2.clone(repo_url, "test_path") repo2 = LibGit2.clone(repo_url, "test_path", isbare=true) julia_url = "https://github.com/JuliaLang/julia" julia_repo = LibGit2.clone(julia_url, "julia_path", branch="release-0.6") ``` """ function clone(repo_url::AbstractString, repo_path::AbstractString; branch::AbstractString="", isbare::Bool = false, remote_cb::Ptr{Cvoid} = C_NULL, credentials::Creds=nothing, callbacks::Callbacks=Callbacks()) cred_payload = reset!(CredentialPayload(credentials)) if !haskey(callbacks, :credentials) callbacks[:credentials] = (credentials_cb(), cred_payload) elseif haskey(callbacks, :credentials) && credentials !== nothing throw(ArgumentError(string( "Unable to both use the provided `credentials` as a payload when the ", "`callbacks` also contain a credentials payload."))) end # setup clone options lbranch = Base.cconvert(Cstring, branch) GC.@preserve lbranch begin remote_callbacks = RemoteCallbacks(callbacks) fetch_opts = FetchOptions(callbacks=remote_callbacks) clone_opts = CloneOptions( bare = Cint(isbare), checkout_branch = isempty(lbranch) ? Cstring(C_NULL) : Base.unsafe_convert(Cstring, lbranch), fetch_opts = fetch_opts, remote_cb = remote_cb ) repo = try clone(repo_url, repo_path, clone_opts) catch err if isa(err, GitError) && err.code === Error.EAUTH reject(cred_payload) else Base.shred!(cred_payload) end rethrow() end end approve(cred_payload) return repo end """ git reset [<committish>] [--] <pathspecs>... """ function reset!(repo::GitRepo, committish::AbstractString, pathspecs::AbstractString...) obj = GitObject(repo, isempty(committish) ? Consts.HEAD_FILE : committish) # do not remove entries in the index matching the provided pathspecs with empty target commit tree reset!(repo, obj, pathspecs...) end """ reset!(repo::GitRepo, id::GitHash, mode::Cint=Consts.RESET_MIXED) Reset the repository `repo` to its state at `id`, using one of three modes set by `mode`: 1. `Consts.RESET_SOFT` - move HEAD to `id`. 2. `Consts.RESET_MIXED` - default, move HEAD to `id` and reset the index to `id`. 3. `Consts.RESET_HARD` - move HEAD to `id`, reset the index to `id`, and discard all working changes. # Examples ```julia # fetch changes LibGit2.fetch(repo) isfile(joinpath(repo_path, our_file)) # will be false # fastforward merge the changes LibGit2.merge!(repo, fastforward=true) # because there was not any file locally, but there is # a file remotely, we need to reset the branch head_oid = LibGit2.head_oid(repo) new_head = LibGit2.reset!(repo, head_oid, LibGit2.Consts.RESET_HARD) ``` In this example, the remote which is being fetched from *does* have a file called `our_file` in its index, which is why we must reset. Equivalent to `git reset [--soft | --mixed | --hard] <id>`. # Examples ```julia repo = LibGit2.GitRepo(repo_path) head_oid = LibGit2.head_oid(repo) open(joinpath(repo_path, "file1"), "w") do f write(f, "111\n") end LibGit2.add!(repo, "file1") mode = LibGit2.Consts.RESET_HARD # will discard the changes to file1 # and unstage it new_head = LibGit2.reset!(repo, head_oid, mode) ``` """ reset!(repo::GitRepo, id::GitHash, mode::Cint = Consts.RESET_MIXED) = reset!(repo, GitObject(repo, id), mode) """ LibGit2.revcount(repo::GitRepo, commit1::AbstractString, commit2::AbstractString) List the number of revisions between `commit1` and `commit2` (committish OIDs in string form). Since `commit1` and `commit2` may be on different branches, `revcount` performs a "left-right" revision list (and count), returning a tuple of `Int`s - the number of left and right commits, respectively. A left (or right) commit refers to which side of a symmetric difference in a tree the commit is reachable from. Equivalent to `git rev-list --left-right --count <commit1> <commit2>`. # Examples ```julia repo = LibGit2.GitRepo(repo_path) repo_file = open(joinpath(repo_path, test_file), "a") println(repo_file, "hello world") flush(repo_file) LibGit2.add!(repo, test_file) commit_oid1 = LibGit2.commit(repo, "commit 1") println(repo_file, "hello world again") flush(repo_file) LibGit2.add!(repo, test_file) commit_oid2 = LibGit2.commit(repo, "commit 2") LibGit2.revcount(repo, string(commit_oid1), string(commit_oid2)) ``` This will return `(-1, 0)`. """ function revcount(repo::GitRepo, commit1::AbstractString, commit2::AbstractString) commit1_id = revparseid(repo, commit1) commit2_id = revparseid(repo, commit2) base_id = merge_base(repo, string(commit1_id), string(commit2_id)) fc = with(GitRevWalker(repo)) do walker count((i,r)->i!=base_id, walker, oid=commit1_id, by=Consts.SORT_TOPOLOGICAL) end sc = with(GitRevWalker(repo)) do walker count((i,r)->i!=base_id, walker, oid=commit2_id, by=Consts.SORT_TOPOLOGICAL) end return (fc-1, sc-1) end """ merge!(repo::GitRepo; kwargs...) -> Bool Perform a git merge on the repository `repo`, merging commits with diverging history into the current branch. Return `true` if the merge succeeded, `false` if not. The keyword arguments are: * `committish::AbstractString=""`: Merge the named commit(s) in `committish`. * `branch::AbstractString=""`: Merge the branch `branch` and all its commits since it diverged from the current branch. * `fastforward::Bool=false`: If `fastforward` is `true`, only merge if the merge is a fast-forward (the current branch head is an ancestor of the commits to be merged), otherwise refuse to merge and return `false`. This is equivalent to the git CLI option `--ff-only`. * `merge_opts::MergeOptions=MergeOptions()`: `merge_opts` specifies options for the merge, such as merge strategy in case of conflicts. * `checkout_opts::CheckoutOptions=CheckoutOptions()`: `checkout_opts` specifies options for the checkout step. Equivalent to `git merge [--ff-only] [<committish> | <branch>]`. !!! note If you specify a `branch`, this must be done in reference format, since the string will be turned into a `GitReference`. For example, if you wanted to merge branch `branch_a`, you would call `merge!(repo, branch="refs/heads/branch_a")`. """ function merge!(repo::GitRepo; committish::AbstractString = "", branch::AbstractString = "", fastforward::Bool = false, merge_opts::MergeOptions = MergeOptions(), checkout_opts::CheckoutOptions = CheckoutOptions()) # merge into head branch upst_anns = if !isempty(committish) # merge committish into HEAD if committish == Consts.FETCH_HEAD # merge FETCH_HEAD fheads = fetchheads(repo) filter!(fh->fh.ismerge, fheads) if isempty(fheads) throw(GitError(Error.Merge, Error.ERROR, "There is no fetch reference for this branch.")) end Base.map(fh->GitAnnotated(repo,fh), fheads) else # merge committish [GitAnnotated(repo, committish)] end else if !isempty(branch) # merge provided branch into HEAD with(GitReference(repo, branch)) do brn_ref [GitAnnotated(repo, brn_ref)] end else # try to get tracking remote branch for the head if !isattached(repo) throw(GitError(Error.Merge, Error.ERROR, "Repository HEAD is detached. Remote tracking branch cannot be used.")) end if isorphan(repo) # this isn't really a merge, but really moving HEAD # https://github.com/libgit2/libgit2/issues/2135#issuecomment-35997764 # try to figure out remote tracking of orphan head m = with(GitReference(repo, Consts.HEAD_FILE)) do head_sym_ref match(r"refs/heads/(.*)", fullname(head_sym_ref)) end if m === nothing throw(GitError(Error.Merge, Error.ERROR, "Unable to determine name of orphan branch.")) end branchname = m.captures[1] remotename = with(GitConfig, repo) do cfg LibGit2.get(String, cfg, "branch.$branchname.remote") end oid = with(GitReference(repo, "refs/remotes/$remotename/$branchname")) do ref LibGit2.GitHash(ref) end with(GitCommit(repo, oid)) do cmt LibGit2.create_branch(repo, branchname, cmt) end return true else with(head(repo)) do head_ref tr_brn_ref = upstream(head_ref) if tr_brn_ref === nothing throw(GitError(Error.Merge, Error.ERROR, "There is no tracking information for the current branch.")) end try [GitAnnotated(repo, tr_brn_ref)] finally close(tr_brn_ref) end end end end end try merge!(repo, upst_anns, fastforward, merge_opts=merge_opts, checkout_opts=checkout_opts) finally Base.foreach(close, upst_anns) end end """ LibGit2.rebase!(repo::GitRepo, upstream::AbstractString="", newbase::AbstractString="") Attempt an automatic merge rebase of the current branch, from `upstream` if provided, or otherwise from the upstream tracking branch. `newbase` is the branch to rebase onto. By default this is `upstream`. If any conflicts arise which cannot be automatically resolved, the rebase will abort, leaving the repository and working tree in its original state, and the function will throw a `GitError`. This is roughly equivalent to the following command line statement: git rebase --merge [<upstream>] if [ -d ".git/rebase-merge" ]; then git rebase --abort fi """ function rebase!(repo::GitRepo, upstream::AbstractString="", newbase::AbstractString="") with(head(repo)) do head_ref head_ann = GitAnnotated(repo, head_ref) upst_ann = if isempty(upstream) brn_ref = LibGit2.upstream(head_ref) if brn_ref === nothing throw(GitError(Error.Rebase, Error.ERROR, "There is no tracking information for the current branch.")) end try GitAnnotated(repo, brn_ref) finally close(brn_ref) end else GitAnnotated(repo, upstream) end onto_ann = isempty(newbase) ? nothing : GitAnnotated(repo, newbase) try sig = default_signature(repo) try rbs = GitRebase(repo, head_ann, upst_ann, onto=onto_ann) try for rbs_op in rbs commit(rbs, sig) end finish(rbs, sig) catch abort(rbs) rethrow() finally close(rbs) end finally #onto_ann !== nothing && close(onto_ann) close(sig) end finally if !isempty(newbase) close(onto_ann::GitAnnotated) end close(upst_ann) close(head_ann) end end return head_oid(repo) end """ authors(repo::GitRepo) -> Vector{Signature} Return all authors of commits to the `repo` repository. # Examples ```julia repo = LibGit2.GitRepo(repo_path) repo_file = open(joinpath(repo_path, test_file), "a") println(repo_file, commit_msg) flush(repo_file) LibGit2.add!(repo, test_file) sig = LibGit2.Signature("TEST", "TEST@TEST.COM", round(time(), 0), 0) commit_oid1 = LibGit2.commit(repo, "commit1"; author=sig, committer=sig) println(repo_file, randstring(10)) flush(repo_file) LibGit2.add!(repo, test_file) commit_oid2 = LibGit2.commit(repo, "commit2"; author=sig, committer=sig) # will be a Vector of [sig, sig] auths = LibGit2.authors(repo) ``` """ function authors(repo::GitRepo) return with(GitRevWalker(repo)) do walker map((oid,repo)->with(GitCommit(repo, oid)) do cmt author(cmt)::Signature end, walker) #, by = Consts.SORT_TIME) end end """ snapshot(repo::GitRepo) -> State Take a snapshot of the current state of the repository `repo`, storing the current HEAD, index, and any uncommitted work. The output `State` can be used later during a call to [`restore`](@ref) to return the repository to the snapshotted state. """ function snapshot(repo::GitRepo) head = GitHash(repo, Consts.HEAD_FILE) index = with(GitIndex, repo) do idx; write_tree!(idx) end work = try with(GitIndex, repo) do idx if length(readdir(path(repo))) > 1 add!(idx, ".") write!(idx) end write_tree!(idx) end finally # restore index with(GitIndex, repo) do idx read_tree!(idx, index) write!(idx) end end State(head, index, work) end """ restore(s::State, repo::GitRepo) Return a repository `repo` to a previous `State` `s`, for example the HEAD of a branch before a merge attempt. `s` can be generated using the [`snapshot`](@ref) function. """ function restore(s::State, repo::GitRepo) head = reset!(repo, Consts.HEAD_FILE, "*") # unstage everything with(GitIndex, repo) do idx read_tree!(idx, s.work) # move work tree to index opts = CheckoutOptions( checkout_strategy = Consts.CHECKOUT_FORCE | # check the index out to work Consts.CHECKOUT_REMOVE_UNTRACKED) # remove everything else checkout_index(repo, idx, options = opts) read_tree!(idx, s.index) # restore index end reset!(repo, s.head, Consts.RESET_SOFT) # restore head end """ transact(f::Function, repo::GitRepo) Apply function `f` to the git repository `repo`, taking a [`snapshot`](@ref) before applying `f`. If an error occurs within `f`, `repo` will be returned to its snapshot state using [`restore`](@ref). The error which occurred will be rethrown, but the state of `repo` will not be corrupted. """ function transact(f::Function, repo::GitRepo) state = snapshot(repo) try f(repo) catch restore(state, repo) rethrow() finally close(repo) end end ## lazy libgit2 initialization const ENSURE_INITIALIZED_LOCK = ReentrantLock() @noinline function throw_negative_refcount_error(x::Int) error("Negative LibGit2 REFCOUNT $x\nThis shouldn't happen, please file a bug report!") end function ensure_initialized() lock(ENSURE_INITIALIZED_LOCK) do x = Threads.atomic_cas!(REFCOUNT, 0, 1) x > 0 && return x < 0 && throw_negative_refcount_error(x) try initialize() catch Threads.atomic_sub!(REFCOUNT, 1) @assert REFCOUNT[] == 0 rethrow() end end return nothing end @noinline function initialize() @check ccall((:git_libgit2_init, libgit2), Cint, ()) cert_loc = NetworkOptions.ca_roots() cert_loc !== nothing && set_ssl_cert_locations(cert_loc) atexit() do # refcount zero, no objects to be finalized if Threads.atomic_sub!(REFCOUNT, 1) == 1 ccall((:git_libgit2_shutdown, libgit2), Cint, ()) end end end function set_ssl_cert_locations(cert_loc) cert_file = cert_dir = Cstring(C_NULL) if isdir(cert_loc) # directories cert_dir = cert_loc else # files, /dev/null, non-existent paths, etc. cert_file = cert_loc end ret = @ccall libgit2.git_libgit2_opts( Consts.SET_SSL_CERT_LOCATIONS::Cint; cert_file::Cstring, cert_dir::Cstring)::Cint ret >= 0 && return ret err = Error.GitError(ret) err.class == Error.SSL && err.msg == "TLS backend doesn't support certificate locations" || throw(err) var = nothing for v in NetworkOptions.CA_ROOTS_VARS haskey(ENV, v) && (var = v) end @assert var !== nothing # otherwise we shouldn't be here msg = """ Your Julia is built with a SSL/TLS engine that libgit2 doesn't know how to configure to use a file or directory of certificate authority roots, but your environment specifies one via the $var variable. If you believe your system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""` in your environment to use those instead. """ throw(Error.GitError(err.class, err.code, chomp(msg))) end """ trace_set(level::Union{Integer,GIT_TRACE_LEVEL}) Sets the system tracing configuration to the specified level. """ function trace_set(level::Union{Integer,Consts.GIT_TRACE_LEVEL}, cb=trace_cb()) @check @ccall libgit2.git_trace_set(level::Cint, cb::Ptr{Cvoid})::Cint end end # module z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2_jll/src/LibGit2_jll.jl`������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/LibGit2_jll.jl baremodule LibGit2_jll using Base, Libdl, MbedTLS_jll, LibSSH2_jll Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libgit2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libgit2_handle::Ptr{Cvoid} = C_NULL libgit2_path::String = "" if Sys.iswindows() const libgit2 = "libgit2.dll" elseif Sys.isapple() const libgit2 = "@rpath/libgit2.1.6.dylib" else const libgit2 = "libgit2.so.1.6" end function __init__() global libgit2_handle = dlopen(libgit2) global libgit2_path = dlpath(libgit2_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libgit2_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libgit2_path() = libgit2_path end # module LibGit2_jll z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/MbedTLS_jll/src/MbedTLS_jll.jly������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/MbedTLS_jll.jl baremodule MbedTLS_jll using Base, Libdl Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libmbedcrypto, libmbedtls, libmbedx509 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libmbedcrypto_handle::Ptr{Cvoid} = C_NULL libmbedcrypto_path::String = "" libmbedtls_handle::Ptr{Cvoid} = C_NULL libmbedtls_path::String = "" libmbedx509_handle::Ptr{Cvoid} = C_NULL libmbedx509_path::String = "" if Sys.iswindows() const libmbedcrypto = "libmbedcrypto.dll" const libmbedtls = "libmbedtls.dll" const libmbedx509 = "libmbedx509.dll" elseif Sys.isapple() const libmbedcrypto = "@rpath/libmbedcrypto.7.dylib" const libmbedtls = "@rpath/libmbedtls.14.dylib" const libmbedx509 = "@rpath/libmbedx509.1.dylib" else const libmbedcrypto = "libmbedcrypto.so.7" const libmbedtls = "libmbedtls.so.14" const libmbedx509 = "libmbedx509.so.1" end function __init__() global libmbedcrypto_handle = dlopen(libmbedcrypto) global libmbedcrypto_path = dlpath(libmbedcrypto_handle) global libmbedtls_handle = dlopen(libmbedtls) global libmbedtls_path = dlpath(libmbedtls_handle) global libmbedx509_handle = dlopen(libmbedx509) global libmbedx509_path = dlpath(libmbedx509_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libmbedtls_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libmbedcrypto_path() =libmbedcrypto_path get_libmbedtls_path() = libmbedtls_path get_libmbedx509_path() = libmbedx509_path end # module MbedTLS_jll z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibSSH2_jll/src/LibSSH2_jll.jlP������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/LibSSH2_jll.jl baremodule LibSSH2_jll using Base, Libdl, MbedTLS_jll Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libssh2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libssh2_handle::Ptr{Cvoid} = C_NULL libssh2_path::String = "" if Sys.iswindows() const libssh2 = "libssh2.dll" elseif Sys.isapple() const libssh2 = "@rpath/libssh2.1.dylib" else const libssh2 = "libssh2.so.1" end function __init__() global libssh2_handle = dlopen(libssh2) global libssh2_path = dlpath(libssh2_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libssh2_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libssh2_path() = libssh2_path end # module LibSSH2_jll p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/error.jl๙������# This file is a part of Julia. License is MIT: https://julialang.org/license module Error import ..LibGit2: ensure_initialized using LibGit2_jll export GitError @enum(Code, GIT_OK = Cint(0), # no error ERROR = Cint(-01), # generic error ENOTFOUND = Cint(-03), # requested object could not be found EEXISTS = Cint(-04), # object exits preventing op EAMBIGUOUS = Cint(-05), # more than one object matches EBUFS = Cint(-06), # output buffer too small to hold data EUSER = Cint(-07), # user callback generated error EBAREREPO = Cint(-08), # operation not allowed on bare repo EUNBORNBRANCH = Cint(-09), # HEAD refers to branch with 0 commits EUNMERGED = Cint(-10), # merge in progress prevented op ENONFASTFORWARD = Cint(-11), # ref not fast-forwardable EINVALIDSPEC = Cint(-12), # name / ref not in valid format EMERGECONFLICT = Cint(-13), # merge conflict prevented op ELOCKED = Cint(-14), # lock file prevented op EMODIFIED = Cint(-15), # ref value does not match expected EAUTH = Cint(-16), # authentication error ECERTIFICATE = Cint(-17), # server certificate is invalid EAPPLIED = Cint(-18), # patch/merge has already been applied EPEEL = Cint(-19), # the requested peel operation is not possible EEOF = Cint(-20), # unexpected EOF PASSTHROUGH = Cint(-30), # internal only ITEROVER = Cint(-31), # signals end of iteration RETRY = Cint(-32), # internal only EMISMATCH = Cint(-33), # hashsum mismatch in object EINDEXDIRTY = Cint(-34), # unsaved changes in the index would be overwritten EAPPLYFAIL = Cint(-35), # patch application failed EOWNER = Cint(-36)) # the object is not owned by the current user @enum(Class, None, NoMemory, OS, Invalid, Reference, Zlib, Repository, Config, Regex, Odb, Index, Object, Net, Tag, Tree, Indexer, SSL, Submodule, Thread, Stash, Checkout, FetchHead, Merge, SSH, Filter, Revert, Callback, CherryPick, Describe, Rebase, Filesystem, Patch, WorkTree, SHA1, HTTP) struct ErrorStruct message::Ptr{UInt8} class::Cint end struct GitError <: Exception class::Class code::Code msg::String end Base.show(io::IO, err::GitError) = print(io, "GitError(Code:$(err.code), Class:$(err.class), $(err.msg))") function last_error() ensure_initialized() err = ccall((:giterr_last, libgit2), Ptr{ErrorStruct}, ()) if err != C_NULL err_obj = unsafe_load(err) err_class = Class(err_obj.class) err_msg = unsafe_string(err_obj.message) else err_class = Class(0) err_msg = "No errors" end return (err_class, err_msg) end GitError(err_code::Integer) = GitError(Code(err_code)) function GitError(err_code::Code) err_class, err_msg = last_error() return GitError(err_class, err_code, err_msg) end end # Error module macro check(git_func) quote err = Cint($(esc(git_func::Expr))) if err < 0 throw(Error.GitError(err)) end err end end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/utils.jly������# This file is a part of Julia. License is MIT: https://julialang.org/license # Parse "GIT URLs" syntax (URLs and a scp-like syntax). For details see: # https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a # Note that using a Regex like this is inherently insecure with regards to its # handling of passwords; we are unable to deterministically and securely erase # the passwords from memory after use. # TODO: reimplement with a Julian parser instead of leaning on this regex const URL_REGEX = r""" ^(?:(?<scheme>ssh|git|https?)://)?+ (?: (?<user>.*?) (?:\:(?<password>.*?))?@ )? (?<host>[A-Za-z0-9\-\.]+) (?(<scheme>) # Only parse port when not using scp-like syntax (?:\:(?<port>\d+))? /? | :? ) (?<path> # Require path to be preceded by '/'. Alternatively, ':' when using scp-like syntax. (?<=(?(<scheme>)/|:)) .* )? $ """x """ version() -> VersionNumber Return the version of libgit2 in use, as a [`VersionNumber`](@ref man-version-number-literals). """ function version() major = Ref{Cint}(0) minor = Ref{Cint}(0) patch = Ref{Cint}(0) @check ccall((:git_libgit2_version, libgit2), Cint, (Ref{Cint}, Ref{Cint}, Ref{Cint}), major, minor, patch) return VersionNumber(major[], minor[], patch[]) end const VERSION = version() """ isset(val::Integer, flag::Integer) Test whether the bits of `val` indexed by `flag` are set (`1`) or unset (`0`). """ isset(val::Integer, flag::Integer) = (val & flag == flag) """ reset(val::Integer, flag::Integer) Unset the bits of `val` indexed by `flag`, returning them to `0`. """ reset(val::Integer, flag::Integer) = (val &= ~flag) """ toggle(val::Integer, flag::Integer) Flip the bits of `val` indexed by `flag`, so that if a bit is `0` it will be `1` after the toggle, and vice-versa. """ toggle(val::Integer, flag::Integer) = (val |= flag) """ features() Return a list of git features the current version of libgit2 supports, such as threading or using HTTPS or SSH. """ function features() feat = ccall((:git_libgit2_features, libgit2), Cint, ()) res = Consts.GIT_FEATURE[] for f in instances(Consts.GIT_FEATURE) isset(feat, Cuint(f)) && Base.push!(res, f) end return res end """ LibGit2.posixpath(path) Standardise the path string `path` to use POSIX separators. """ function posixpath end if Sys.iswindows() posixpath(path) = replace(path,'\\' => '/') elseif Sys.isunix() posixpath(path) = path end """ LibGit2.git_url(; kwargs...) -> String Create a string based upon the URL components provided. When the `scheme` keyword is not provided the URL produced will use the alternative [scp-like syntax](https://git-scm.com/docs/git-clone#_git_urls_a_id_urls_a). # Keywords * `scheme::AbstractString=""`: the URL scheme which identifies the protocol to be used. For HTTP use "http", SSH use "ssh", etc. When `scheme` is not provided the output format will be "ssh" but using the scp-like syntax. * `username::AbstractString=""`: the username to use in the output if provided. * `password::AbstractString=""`: the password to use in the output if provided. * `host::AbstractString=""`: the hostname to use in the output. A hostname is required to be specified. * `port::Union{AbstractString,Integer}=""`: the port number to use in the output if provided. Cannot be specified when using the scp-like syntax. * `path::AbstractString=""`: the path to use in the output if provided. !!! warning Avoid using passwords in URLs. Unlike the credential objects, Julia is not able to securely zero or destroy the sensitive data after use and the password may remain in memory; possibly to be exposed by an uninitialized memory. # Examples ```jldoctest julia> LibGit2.git_url(username="git", host="github.com", path="JuliaLang/julia.git") "git@github.com:JuliaLang/julia.git" julia> LibGit2.git_url(scheme="https", host="github.com", path="/JuliaLang/julia.git") "https://github.com/JuliaLang/julia.git" julia> LibGit2.git_url(scheme="ssh", username="git", host="github.com", port=2222, path="JuliaLang/julia.git") "ssh://git@github.com:2222/JuliaLang/julia.git" ``` """ function git_url(; scheme::AbstractString="", username::AbstractString="", host::AbstractString="", port::Union{AbstractString, Integer}="", path::AbstractString="") port_str = port isa Integer ? string(port) : port scp_syntax = isempty(scheme) isempty(host) && throw(ArgumentError("A host needs to be specified")) scp_syntax && !isempty(port_str) && throw(ArgumentError("Port cannot be specified when using scp-like syntax")) io = IOBuffer() !isempty(scheme) && write(io, scheme, "://") if !isempty(username) write(io, username) write(io, '@') end write(io, host) !isempty(port_str) && write(io, ':', port_str) if !isempty(path) if scp_syntax write(io, ':') elseif !startswith(path, '/') write(io, '/') end write(io, path) end seekstart(io) return String(take!(io)) end function credential_identifier(scheme::AbstractString, host::AbstractString) string(isempty(scheme) ? "ssh" : scheme, "://", host) end function credential_identifier(url::AbstractString) m = match(URL_REGEX, url) scheme = something(m[:scheme], SubString("")) host = something(m[:host]) credential_identifier(scheme, host) end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/consts.jljT������# This file is a part of Julia. License is MIT: https://julialang.org/license module Consts import ..LibGit2: version, ensure_initialized const HEAD_FILE = "HEAD" const FETCH_HEAD = "FETCH_HEAD" const REMOTE_ORIGIN = "origin" # objs @enum(OBJECT, OBJ_ANY = -2, OBJ_BAD = -1, OBJ_COMMIT = 1, OBJ_TREE = 2, OBJ_BLOB = 3, OBJ_TAG = 4) #revwalk const SORT_NONE = Cint(0) const SORT_TOPOLOGICAL = Cint(1 << 0) const SORT_TIME = Cint(1 << 1) const SORT_REVERSE = Cint(1 << 2) # refs const REF_INVALID = Cint(0) const REF_OID = Cint(1) const REF_SYMBOLIC = Cint(2) const REF_LISTALL = REF_OID | REF_SYMBOLIC # blame const BLAME_NORMAL = Cuint(0) const BLAME_TRACK_COPIES_SAME_FILE = Cuint(1 << 0) const BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = Cuint(1 << 1) const BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = Cuint(1 << 2) const BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = Cuint(1 << 3) const BLAME_FIRST_PARENT = Cuint(1 << 4) # checkout const CHECKOUT_NONE = Cuint(0) const CHECKOUT_SAFE = Cuint(1 << 0) const CHECKOUT_FORCE = Cuint(1 << 1) const CHECKOUT_RECREATE_MISSING = Cuint(1 << 2) const CHECKOUT_ALLOW_CONFLICTS = Cuint(1 << 4) const CHECKOUT_REMOVE_UNTRACKED = Cuint(1 << 5) const CHECKOUT_REMOVE_IGNORED = Cuint(1 << 6) const CHECKOUT_UPDATE_ONLY = Cuint(1 << 7) const CHECKOUT_DONT_UPDATE_INDEX = Cuint(1 << 8) const CHECKOUT_NO_REFRESH = Cuint(1 << 9) const CHECKOUT_SKIP_UNMERGED = Cuint(1 << 10) const CHECKOUT_USE_OURS = Cuint(1 << 11) const CHECKOUT_USE_THEIRS = Cuint(1 << 12) const CHECKOUT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 13) const CHECKOUT_SKIP_LOCKED_DIRECTORIES = Cuint(1 << 18) const CHECKOUT_DONT_OVERWRITE_IGNORED = Cuint(1 << 19) const CHECKOUT_CONFLICT_STYLE_MERGE = Cuint(1 << 20) const CHECKOUT_CONFLICT_STYLE_DIFF3 = Cuint(1 << 21) const CHECKOUT_DONT_REMOVE_EXISTING = Cuint(1 << 22) const CHECKOUT_UPDATE_SUBMODULES = Cuint(1 << 16) const CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = Cuint(1 << 17) const CHECKOUT_NOTIFY_NONE = Cuint(0) const CHECKOUT_NOTIFY_CONFLICT = Cuint(1 << 0) const CHECKOUT_NOTIFY_DIRTY = Cuint(1 << 1) const CHECKOUT_NOTIFY_UPDATED = Cuint(1 << 2) const CHECKOUT_NOTIFY_UNTRACKED = Cuint(1 << 3) const CHECKOUT_NOTIFY_IGNORED = Cuint(1 << 4) const CHECKOUT_NOTIFY_ALL = 0x0FFFF # diff const DIFF_OPTIONS_VERSION = Cuint(1) const DIFF_NORMAL = Cuint(0) const DIFF_REVERSE = Cuint(1 << 0) const DIFF_INCLUDE_IGNORED = Cuint(1 << 1) const DIFF_RECURSE_IGNORED_DIRS = Cuint(1 << 2) const DIFF_INCLUDE_UNTRACKED = Cuint(1 << 3) const DIFF_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) const DIFF_INCLUDE_UNMODIFIED = Cuint(1 << 5) const DIFF_INCLUDE_TYPECHANGE = Cuint(1 << 6) const DIFF_INCLUDE_TYPECHANGE_TREES = Cuint(1 << 7) const DIFF_IGNORE_FILEMODE = Cuint(1 << 8) const DIFF_IGNORE_SUBMODULES = Cuint(1 << 9) const DIFF_IGNORE_CASE = Cuint(1 << 10) const DIFF_DISABLE_PATHSPEC_MATCH = Cuint(1 << 12) const DIFF_SKIP_BINARY_CHECK = Cuint(1 << 13) const DIFF_ENABLE_FAST_UNTRACKED_DIRS = Cuint(1 << 14) const DIFF_FORCE_TEXT = Cuint(1 << 20) const DIFF_FORCE_BINARY = Cuint(1 << 21) const DIFF_IGNORE_WHITESPACE = Cuint(1 << 22) const DIFF_IGNORE_WHITESPACE_CHANGE = Cuint(1 << 23) const DIFF_IGNORE_WHITESPACE_EOL = Cuint(1 << 24) const DIFF_SHOW_UNTRACKED_CONTENT = Cuint(1 << 25) const DIFF_SHOW_UNMODIFIED = Cuint(1 << 26) const DIFF_PATIENCE = Cuint(1 << 28) const DIFF_MINIMAL = Cuint(1 << 29) const DIFF_FLAG_BINARY = Cuint(1 << 0) const DIFF_FLAG_NOT_BINARY = Cuint(1 << 1) const DIFF_FLAG_VALID_OID = Cuint(1 << 2) const DIFF_FORMAT_PATCH = Cuint(1) const DIFF_FORMAT_PATCH_HEADER = Cuint(2) const DIFF_FORMAT_RAW = Cuint(3) const DIFF_FORMAT_NAME_ONLY = Cuint(4) const DIFF_FORMAT_NAME_STATUS = Cuint(5) @enum(DELTA_STATUS, DELTA_UNMODIFIED = Cint(0), DELTA_ADDED = Cint(1), DELTA_DELETED = Cint(2), DELTA_MODIFIED = Cint(3), DELTA_RENAMED = Cint(4), DELTA_COPIED = Cint(5), DELTA_IGNORED = Cint(6), DELTA_UNTRACKED = Cint(7), DELTA_TYPECHANGE = Cint(8)) # index const IDXENTRY_NAMEMASK = (0x0fff) const IDXENTRY_STAGEMASK = (0x3000) const IDXENTRY_EXTENDED = (0x4000) const IDXENTRY_VALID = (0x8000) const IDXENTRY_STAGESHIFT = Cint(12) const IDXENTRY_UPDATE = Cint(1 << 0) const IDXENTRY_REMOVE = Cint(1 << 1) const IDXENTRY_UPTODATE = Cint(1 << 2) const IDXENTRY_ADDED = Cint(1 << 3) const IDXENTRY_HASHED = Cint(1 << 4) const IDXENTRY_UNHASHED = Cint(1 << 5) const IDXENTRY_WT_REMOVE = Cint(1 << 6) const IDXENTRY_CONFLICTED = Cint(1 << 7) const IDXENTRY_UNPACKED = Cint(1 << 8) const IDXENTRY_NEW_SKIP_WORKTREE = Cint(1 << 9) const INDEXCAP_IGNORE_CASE = Cuint(1) const INDEXCAP_NO_FILEMODE = Cuint(2) const INDEXCAP_NO_SYMLINKS = Cuint(4) const INDEXCAP_FROM_OWNER = ~Cuint(0) const INDEX_ADD_DEFAULT = Cuint(0) const INDEX_ADD_FORCE = Cuint(1 << 0) const INDEX_ADD_DISABLE_PATHSPEC_MATCH = Cuint(1 << 1) const INDEX_ADD_CHECK_PATHSPEC = Cuint(1 << 2) const INDEX_STAGE_ANY = Cint(-1) # merge """ Option flags for git merge. * `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common ancestor and the "ours" or "theirs" side of the merge. Allows merges where a file has been renamed. * `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather than trying to resolve it. * `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting from the merge. * `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, use the first one, rather than trying to recursively merge the bases. """ @enum(GIT_MERGE, MERGE_FIND_RENAMES = 1 << 0, MERGE_FAIL_ON_CONFLICT = 1 << 1, MERGE_SKIP_REUC = 1 << 2, MERGE_NO_RECURSIVE = 1 << 3) @enum(GIT_MERGE_FILE, MERGE_FILE_DEFAULT = 0, # Defaults MERGE_FILE_STYLE_MERGE = 1 << 0, # Create standard conflicted merge files MERGE_FILE_STYLE_DIFF3 = 1 << 1, # Create diff3-style files MERGE_FILE_SIMPLIFY_ALNUM = 1 << 2, # Condense non-alphanumeric regions for simplified diff file MERGE_FILE_IGNORE_WHITESPACE = 1 << 3, # Ignore all whitespace MERGE_FILE_IGNORE_WHITESPACE_CHANGE = 1 << 4, # Ignore changes in amount of whitespace MERGE_FILE_IGNORE_WHITESPACE_EOL = 1 << 5, # Ignore whitespace at end of line MERGE_FILE_DIFF_PATIENCE = 1 << 6, # Use the "patience diff" algorithm MERGE_FILE_DIFF_MINIMAL = 1 << 7) # Take extra time to find minimal diff """ Option flags for git merge file favoritism. * `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, make a note of the conflict in the index which `git checkout` will use to create a merge file, which the user can then reference to resolve the conflicts. This is the default. * `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, use the version in the "ours" side of the merge in the index. * `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, use the version in the "theirs" side of the merge in the index. * `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, include each unique line from both sides in the file which is put into the index. """ @enum(GIT_MERGE_FILE_FAVOR, MERGE_FILE_FAVOR_NORMAL = 0, MERGE_FILE_FAVOR_OURS = 1, MERGE_FILE_FAVOR_THEIRS = 2, MERGE_FILE_FAVOR_UNION = 3) """ The user's instructions for how to perform a possible merge. * `MERGE_PREFERENCE_NONE`: the user has no preference. * `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. * `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no other type (which may introduce conflicts). """ @enum(GIT_MERGE_PREFERENCE, MERGE_PREFERENCE_NONE = 0, MERGE_PREFERENCE_NO_FASTFORWARD = 1, MERGE_PREFERENCE_FASTFORWARD_ONLY = 2) """ Result of analysis on merge possibilities. * `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of the input commits. * `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the user wishes to merge have all diverged from a common ancestor. In this case the changes have to be resolved and conflicts may occur. * `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can be reached from HEAD, so no merge needs to be performed. * `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no merge needs to be performed - instead, the user can simply checkout the input commit(s). * `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not exist. It is not possible to merge, but it may be possible to checkout the input commits. """ @enum(GIT_MERGE_ANALYSIS, MERGE_ANALYSIS_NONE = 0, MERGE_ANALYSIS_NORMAL = 1 << 0, MERGE_ANALYSIS_UP_TO_DATE = 1 << 1, MERGE_ANALYSIS_FASTFORWARD = 1 << 2, MERGE_ANALYSIS_UNBORN = 1 << 3) # reset const RESET_SOFT = Cint(1) # Move the head to the given commit const RESET_MIXED = Cint(2) # SOFT plus reset index to the commit const RESET_HARD = Cint(3) # MIXED plus changes in working tree discarded # rebase """ Options for what rebase operation is currently being performed on a commit. * `REBASE_OPERATION_PICK`: cherry-pick the commit in question. * `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its message using the prompt. * `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user to edit the commit's contents and its message. * `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. The commit messages of the two commits will be merged. * `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. Only the commit message of the previous commit will be used. * `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if the command exits successfully. """ @enum(GIT_REBASE_OPERATION, REBASE_OPERATION_PICK = Cint(0), REBASE_OPERATION_REWORD = Cint(1), REBASE_OPERATION_EDIT = Cint(2), REBASE_OPERATION_SQUASH = Cint(3), REBASE_OPERATION_FIXUP = Cint(4), REBASE_OPERATION_EXEC = Cint(5)) # fetch_prune const FETCH_PRUNE_UNSPECIFIED = Cint(0) const FETCH_PRUNE = Cint(1) const FETCH_NO_PRUNE = Cint(2) # remote_autotag const REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = Cint(0) const REMOTE_DOWNLOAD_TAGS_AUTO = Cint(1) const REMOTE_DOWNLOAD_TAGS_NONE = Cint(2) const REMOTE_DOWNLOAD_TAGS_ALL = Cint(3) # remote_redirect const REMOTE_REDIRECT_NONE = Cuint(1 << 0) const REMOTE_REDIRECT_INITIAL = Cuint(1 << 1) const REMOTE_REDIRECT_ALL = Cuint(1 << 2) # clone const CLONE_LOCAL_AUTO = Cint(0) const CLONE_LOCAL = Cint(1) const CLONE_NO_LOCAL = Cint(2) const CLONE_LOCAL_NO_LINKS = Cint(3) # describe const DESCRIBE_DEFAULT = Cuint(0) const DESCRIBE_TAGS = Cuint(1 << 0) const DESCRIBE_ALL = Cuint(1 << 1) # status const STATUS_CURRENT = Cuint(0) const STATUS_INDEX_NEW = Cuint(1 << 0) const STATUS_INDEX_MODIFIED = Cuint(1 << 1) const STATUS_INDEX_DELETED = Cuint(1 << 2) const STATUS_INDEX_RENAMED = Cuint(1 << 3) const STATUS_INDEX_TYPECHANGE = Cuint(1 << 4) const STATUS_WT_NEW = Cuint(1 << 7) const STATUS_WT_MODIFIED = Cuint(1 << 8) const STATUS_WT_DELETED = Cuint(1 << 9) const STATUS_WT_TYPECHANGE = Cuint(1 << 10) const STATUS_WT_RENAMED = Cuint(1 << 11) const STATUS_WT_UNREADABLE = Cuint(1 << 12) const STATUS_IGNORED = Cuint(1 << 14) const STATUS_CONFLICTED = Cuint(1 << 15) # status show const STATUS_SHOW_INDEX_AND_WORKDIR = Cint(0) const STATUS_SHOW_INDEX_ONLY = Cint(1) const STATUS_SHOW_WORKDIR_ONLY = Cint(2) # status options const STATUS_OPT_INCLUDE_UNTRACKED = Cuint(1 << 0) const STATUS_OPT_INCLUDE_IGNORED = Cuint(1 << 1) const STATUS_OPT_INCLUDE_UNMODIFIED = Cuint(1 << 2) const STATUS_OPT_EXCLUDE_SUBMODULES = Cuint(1 << 3) const STATUS_OPT_RECURSE_UNTRACKED_DIRS = Cuint(1 << 4) const STATUS_OPT_DISABLE_PATHSPEC_MATCH = Cuint(1 << 5) const STATUS_OPT_RECURSE_IGNORED_DIRS = Cuint(1 << 6) const STATUS_OPT_RENAMES_HEAD_TO_INDEX = Cuint(1 << 7) const STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = Cuint(1 << 8) const STATUS_OPT_SORT_CASE_SENSITIVELY = Cuint(1 << 9) const STATUS_OPT_SORT_CASE_INSENSITIVELY = Cuint(1 << 10) const STATUS_OPT_RENAMES_FROM_REWRITES = Cuint(1 << 11) const STATUS_OPT_NO_REFRESH = Cuint(1 << 12) const STATUS_OPT_UPDATE_INDEX = Cuint(1 << 13) const STATUS_OPT_INCLUDE_UNREADABLE = Cuint(1 << 14) const STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = Cuint(1 << 15) # certificate types from `enum git_cert_t` in `cert.h`. const CERT_TYPE_TLS = 1 # GIT_CERT_X509 const CERT_TYPE_SSH = 2 # GIT_CERT_HOSTKEY_LIBSSH2 # certificate callback return values const PASSTHROUGH = -30 const CERT_REJECT = -1 const CERT_ACCEPT = 0 # certificate hash flags const CERT_SSH_MD5 = 1 << 0 const CERT_SSH_SHA1 = 1 << 1 const CERT_SSH_SHA256 = 1 << 2 # libssh2 known host constants const LIBSSH2_KNOWNHOST_TYPE_PLAIN = 1 const LIBSSH2_KNOWNHOST_TYPE_SHA1 = 2 const LIBSSH2_KNOWNHOST_TYPE_CUSTOM = 3 const LIBSSH2_KNOWNHOST_KEYENC_RAW = 1 << 16 const LIBSSH2_KNOWNHOST_KEYENC_BASE64 = 2 << 16 # libssh2 host check return values const LIBSSH2_KNOWNHOST_CHECK_MATCH = 0 const LIBSSH2_KNOWNHOST_CHECK_MISMATCH = 1 const LIBSSH2_KNOWNHOST_CHECK_NOTFOUND = 2 const LIBSSH2_KNOWNHOST_CHECK_FAILURE = 3 @enum(GIT_SUBMODULE_IGNORE, SUBMODULE_IGNORE_UNSPECIFIED = -1, # use the submodule's configuration SUBMODULE_IGNORE_NONE = 1, # any change or untracked == dirty SUBMODULE_IGNORE_UNTRACKED = 2, # dirty if tracked files change SUBMODULE_IGNORE_DIRTY = 3, # only dirty if HEAD moved SUBMODULE_IGNORE_ALL = 4) # never dirty """ Option flags for `GitRepo`. * `REPOSITORY_OPEN_NO_SEARCH` - Only open the repository if it can be immediately found in the `path`. Do not walk up from the `path` looking at parent directories. * `REPOSITORY_OPEN_CROSS_FS` - Unless this flag is set, open will not continue searching across filesystem boundaries. (E.g. Searching in a user's home directory `/home/user/source/` will not return `/.git/` as the found repo if `/` is a different filesystem than `/home`.) * `REPOSITORY_OPEN_BARE` - Open repository as a bare repo regardless of core.bare config, and defer loading config file for faster setup. """ @enum(GIT_REPOSITORY_OPEN, REPOSITORY_OPEN_DEFAULT = 0, REPOSITORY_OPEN_NO_SEARCH = 1<<0, REPOSITORY_OPEN_CROSS_FS = 1<<1, REPOSITORY_OPEN_BARE = 1<<2) @enum(GIT_BRANCH, BRANCH_LOCAL = 1, BRANCH_REMOTE = 2) @enum(GIT_FILEMODE, FILEMODE_UNREADABLE = 0o000000, FILEMODE_TREE = 0o040000, FILEMODE_BLOB = 0o100644, FILEMODE_BLOB_EXECUTABLE = 0o100755, FILEMODE_LINK = 0o120000, FILEMODE_COMMIT = 0o160000) @enum(GIT_CREDTYPE, CREDTYPE_USERPASS_PLAINTEXT = Cuint(1 << 0), CREDTYPE_SSH_KEY = Cuint(1 << 1), CREDTYPE_SSH_CUSTOM = Cuint(1 << 2), CREDTYPE_DEFAULT = Cuint(1 << 3), CREDTYPE_SSH_INTERACTIVE = Cuint(1 << 4), CREDTYPE_USERNAME = Cuint(1 << 5), CREDTYPE_SSH_MEMORY = Cuint(1 << 6)) @enum(GIT_FEATURE, FEATURE_THREADS = Cuint(1 << 0), FEATURE_HTTPS = Cuint(1 << 1), FEATURE_SSH = Cuint(1 << 2), FEATURE_NSEC = Cuint(1 << 3)) if version() >= v"0.24.0" @doc """ Priority level of a config file. These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. * `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. * `CONFIG_LEVEL_PROGRAMDATA` - System-wide on Windows, for compatibility with portable git * `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems * `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` * `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) """ @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_PROGRAMDATA = 1, CONFIG_LEVEL_SYSTEM = 2, CONFIG_LEVEL_XDG = 3, CONFIG_LEVEL_GLOBAL = 4, CONFIG_LEVEL_LOCAL = 5, CONFIG_LEVEL_APP = 6, CONFIG_HIGHEST_LEVEL =-1) else @doc """ Priority level of a config file. These priority levels correspond to the natural escalation logic (from higher to lower) when searching for config entries in git. * `CONFIG_LEVEL_DEFAULT` - Open the global, XDG and system configuration files if any available. * `CONFIG_LEVEL_SYSTEM` - System-wide configuration file; `/etc/gitconfig` on Linux systems * `CONFIG_LEVEL_XDG` - XDG compatible configuration file; typically `~/.config/git/config` * `CONFIG_LEVEL_GLOBAL` - User-specific configuration file (also called Global configuration file); typically `~/.gitconfig` * `CONFIG_LEVEL_LOCAL` - Repository specific configuration file; `\$WORK_DIR/.git/config` on non-bare repos * `CONFIG_LEVEL_APP` - Application specific configuration file; freely defined by applications * `CONFIG_HIGHEST_LEVEL` - Represents the highest level available config file (i.e. the most specific config file available that actually is loaded) """ @enum(GIT_CONFIG, CONFIG_LEVEL_DEFAULT = 0, CONFIG_LEVEL_SYSTEM = 1, CONFIG_LEVEL_XDG = 2, CONFIG_LEVEL_GLOBAL = 3, CONFIG_LEVEL_LOCAL = 4, CONFIG_LEVEL_APP = 5, CONFIG_HIGHEST_LEVEL =-1) end """ Global library options. These are used to select which global option to set or get and are used in `git_libgit2_opts()`. """ @enum(GIT_OPT, GET_MWINDOW_SIZE = 0, SET_MWINDOW_SIZE = 1, GET_MWINDOW_MAPPED_LIMIT = 2, SET_MWINDOW_MAPPED_LIMIT = 3, GET_SEARCH_PATH = 4, SET_SEARCH_PATH = 5, SET_CACHE_OBJECT_LIMIT = 6, SET_CACHE_MAX_SIZE = 7, ENABLE_CACHING = 8, GET_CACHED_MEMORY = 9, GET_TEMPLATE_PATH = 10, SET_TEMPLATE_PATH = 11, SET_SSL_CERT_LOCATIONS = 12) """ Option flags for `GitProxy`. * `PROXY_NONE`: do not attempt the connection through a proxy. * `PROXY_AUTO`: attempt to figure out the proxy configuration from the git configuration. * `PROXY_SPECIFIED`: connect using the URL given in the `url` field of this struct. """ @enum(GIT_PROXY, PROXY_NONE, PROXY_AUTO, PROXY_SPECIFIED) # Available tracing levels. @enum GIT_TRACE_LEVEL begin TRACE_NONE TRACE_FATAL TRACE_ERROR TRACE_WARN TRACE_INFO TRACE_DEBUG TRACE_TRACE end end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/types.jl_ใ������# This file is a part of Julia. License is MIT: https://julialang.org/license using Base: something import Base.@kwdef import .Consts: GIT_SUBMODULE_IGNORE, GIT_MERGE_FILE_FAVOR, GIT_MERGE_FILE, GIT_CONFIG const OID_RAWSZ = 20 const OID_HEXSZ = OID_RAWSZ * 2 const OID_MINPREFIXLEN = 4 abstract type AbstractGitHash end """ GitHash A git object identifier, based on the sha-1 hash. It is a $OID_RAWSZ byte string ($OID_HEXSZ hex digits) used to identify a `GitObject` in a repository. """ struct GitHash <: AbstractGitHash val::NTuple{OID_RAWSZ, UInt8} GitHash(val::NTuple{OID_RAWSZ, UInt8}) = new(val) end GitHash() = GitHash(ntuple(i->zero(UInt8), OID_RAWSZ)) GitHash(h::GitHash) = h """ GitShortHash(hash::GitHash, len::Integer) A shortened git object identifier, which can be used to identify a git object when it is unique, consisting of the initial `len` hexadecimal digits of `hash` (the remaining digits are ignored). """ struct GitShortHash <: AbstractGitHash hash::GitHash # underlying hash: unused digits are ignored len::Csize_t # length in hex digits end """ LibGit2.TimeStruct Time in a signature. Matches the [`git_time`](https://libgit2.org/libgit2/#HEAD/type/git_time) struct. """ struct TimeStruct time::Int64 # time in seconds from epoch offset::Cint # timezone offset in minutes @static if LibGit2.VERSION >= v"0.27.0" sign::Cchar end end """ LibGit2.SignatureStruct An action signature (e.g. for committers, taggers, etc). Matches the [`git_signature`](https://libgit2.org/libgit2/#HEAD/type/git_signature) struct. The fields represent: * `name`: The full name of the committer or author of the commit. * `email`: The email at which the committer/author can be contacted. * `when`: a [`TimeStruct`](@ref) indicating when the commit was authored/committed into the repository. """ struct SignatureStruct name::Ptr{UInt8} # full name of the author email::Ptr{UInt8} # email of the author when::TimeStruct # time when the action happened end """ LibGit2.StrArrayStruct A LibGit2 representation of an array of strings. Matches the [`git_strarray`](https://libgit2.org/libgit2/#HEAD/type/git_strarray) struct. When fetching data from LibGit2, a typical usage would look like: ```julia sa_ref = Ref(StrArrayStruct()) @check ccall(..., (Ptr{StrArrayStruct},), sa_ref) res = convert(Vector{String}, sa_ref[]) free(sa_ref) ``` In particular, note that `LibGit2.free` should be called afterward on the `Ref` object. Conversely, when passing a vector of strings to LibGit2, it is generally simplest to rely on implicit conversion: ```julia strs = String[...] @check ccall(..., (Ptr{StrArrayStruct},), strs) ``` Note that no call to `free` is required as the data is allocated by Julia. """ struct StrArrayStruct strings::Ptr{Cstring} count::Csize_t end StrArrayStruct() = StrArrayStruct(C_NULL, 0) function free(sa_ref::Base.Ref{StrArrayStruct}) ensure_initialized() ccall((:git_strarray_free, libgit2), Cvoid, (Ptr{StrArrayStruct},), sa_ref) end """ LibGit2.Buffer A data buffer for exporting data from libgit2. Matches the [`git_buf`](https://libgit2.org/libgit2/#HEAD/type/git_buf) struct. When fetching data from LibGit2, a typical usage would look like: ```julia buf_ref = Ref(Buffer()) @check ccall(..., (Ptr{Buffer},), buf_ref) # operation on buf_ref free(buf_ref) ``` In particular, note that `LibGit2.free` should be called afterward on the `Ref` object. """ struct Buffer ptr::Ptr{Cchar} asize::Csize_t size::Csize_t end Buffer() = Buffer(C_NULL, 0, 0) function free(buf_ref::Base.Ref{Buffer}) ensure_initialized() ccall((:git_buf_free, libgit2), Cvoid, (Ptr{Buffer},), buf_ref) end """ LibGit2.CheckoutOptions Matches the [`git_checkout_options`](https://libgit2.org/libgit2/#HEAD/type/git_checkout_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `checkout_strategy`: determine how to handle conflicts and whether to force the checkout/recreate missing files. * `disable_filters`: if nonzero, do not apply filters like CLRF (to convert file newlines between UNIX and DOS). * `dir_mode`: read/write/access mode for any directories involved in the checkout. Default is `0755`. * `file_mode`: read/write/access mode for any files involved in the checkout. Default is `0755` or `0644`, depending on the blob. * `file_open_flags`: bitflags used to open any files during the checkout. * `notify_flags`: Flags for what sort of conflicts the user should be notified about. * `notify_cb`: An optional callback function to notify the user if a checkout conflict occurs. If this function returns a non-zero value, the checkout will be cancelled. * `notify_payload`: Payload for the notify callback function. * `progress_cb`: An optional callback function to display checkout progress. * `progress_payload`: Payload for the progress callback. * `paths`: If not empty, describes which paths to search during the checkout. If empty, the checkout will occur over all files in the repository. * `baseline`: Expected content of the [`workdir`](@ref), captured in a (pointer to a) [`GitTree`](@ref). Defaults to the state of the tree at HEAD. * `baseline_index`: Expected content of the [`workdir`](@ref), captured in a (pointer to a) `GitIndex`. Defaults to the state of the index at HEAD. * `target_directory`: If not empty, checkout to this directory instead of the `workdir`. * `ancestor_label`: In case of conflicts, the name of the common ancestor side. * `our_label`: In case of conflicts, the name of "our" side. * `their_label`: In case of conflicts, the name of "their" side. * `perfdata_cb`: An optional callback function to display performance data. * `perfdata_payload`: Payload for the performance callback. """ @kwdef struct CheckoutOptions version::Cuint = Cuint(1) checkout_strategy::Cuint = Consts.CHECKOUT_SAFE disable_filters::Cint = Cint(0) dir_mode::Cuint = Cuint(0) file_mode::Cuint = Cuint(0) file_open_flags::Cint = Cint(0) notify_flags::Cuint = Consts.CHECKOUT_NOTIFY_NONE notify_cb::Ptr{Cvoid} = C_NULL notify_payload::Any = nothing progress_cb::Ptr{Cvoid} = C_NULL progress_payload::Any = nothing paths::StrArrayStruct = StrArrayStruct() baseline::Ptr{Cvoid} = C_NULL baseline_index::Ptr{Cvoid} = C_NULL target_directory::Cstring = Cstring(C_NULL) ancestor_label::Cstring = Cstring(C_NULL) our_label::Cstring = Cstring(C_NULL) their_label::Cstring = Cstring(C_NULL) perfdata_cb::Ptr{Cvoid} = C_NULL perfdata_payload::Any = Nothing end @assert Base.allocatedinline(CheckoutOptions) """ LibGit2.TransferProgress Transfer progress information used by the `transfer_progress` remote callback. Matches the [`git_indexer_progress`](https://libgit2.org/libgit2/#HEAD/type/git_indexer_progress) struct. """ @kwdef struct TransferProgress total_objects::Cuint = Cuint(0) indexed_objects::Cuint = Cuint(0) received_objects::Cuint = Cuint(0) local_objects::Cuint = Cuint(0) total_deltas::Cuint = Cuint(0) indexed_deltas::Cuint = Cuint(0) received_bytes::Csize_t = Csize_t(0) end @assert Base.allocatedinline(TransferProgress) """ LibGit2.RemoteCallbacks Callback settings. Matches the [`git_remote_callbacks`](https://libgit2.org/libgit2/#HEAD/type/git_remote_callbacks) struct. """ @kwdef struct RemoteCallbacks version::Cuint = Cuint(1) sideband_progress::Ptr{Cvoid} = C_NULL completion::Ptr{Cvoid} = C_NULL credentials::Ptr{Cvoid} = C_NULL certificate_check::Ptr{Cvoid} = certificate_cb() transfer_progress::Ptr{Cvoid} = C_NULL update_tips::Ptr{Cvoid} = C_NULL pack_progress::Ptr{Cvoid} = C_NULL push_transfer_progress::Ptr{Cvoid} = C_NULL push_update_reference::Ptr{Cvoid} = C_NULL push_negotiation::Ptr{Cvoid} = C_NULL transport::Ptr{Cvoid} = C_NULL @static if LibGit2.VERSION >= v"1.2.0" remote_ready::Ptr{Cvoid} = C_NULL end payload::Any = nothing @static if LibGit2.VERSION >= v"0.99.0" resolve_url::Ptr{Cvoid} = C_NULL end end @assert Base.allocatedinline(RemoteCallbacks) """ LibGit2.Callbacks A dictionary which containing the callback name as the key and the value as a tuple of the callback function and payload. The `Callback` dictionary to construct `RemoteCallbacks` allows each callback to use a distinct payload. Each callback, when called, will receive `Dict` which will hold the callback's custom payload which can be accessed using the callback name. # Examples ```julia-repl julia> c = LibGit2.Callbacks(:credentials => (LibGit2.credentials_cb(), LibGit2.CredentialPayload())); julia> LibGit2.clone(url, callbacks=c); ``` See [`git_remote_callbacks`](https://libgit2.org/libgit2/#HEAD/type/git_remote_callbacks) for details on supported callbacks. """ const Callbacks = Dict{Symbol, Tuple{Ptr{Cvoid}, Any}} function RemoteCallbacks(c::Callbacks) callbacks = Dict{Symbol, Ptr{Cvoid}}() payloads = Dict{Symbol, Any}() for (name, (callback, payload)) in c callbacks[name] = callback payloads[name] = payload end RemoteCallbacks(; payload=payloads, callbacks...) end """ LibGit2.ProxyOptions Options for connecting through a proxy. Matches the [`git_proxy_options`](https://libgit2.org/libgit2/#HEAD/type/git_proxy_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `proxytype`: an `enum` for the type of proxy to use. Defined in [`git_proxy_t`](https://libgit2.org/libgit2/#HEAD/type/git_proxy_t). The corresponding Julia enum is `GIT_PROXY` and has values: - `PROXY_NONE`: do not attempt the connection through a proxy. - `PROXY_AUTO`: attempt to figure out the proxy configuration from the git configuration. - `PROXY_SPECIFIED`: connect using the URL given in the `url` field of this struct. Default is to auto-detect the proxy type. * `url`: the URL of the proxy. * `credential_cb`: a pointer to a callback function which will be called if the remote requires authentication to connect. * `certificate_cb`: a pointer to a callback function which will be called if certificate verification fails. This lets the user decide whether or not to keep connecting. If the function returns `1`, connecting will be allowed. If it returns `0`, the connection will not be allowed. A negative value can be used to return errors. * `payload`: the payload to be provided to the two callback functions. # Examples ```julia-repl julia> fo = LibGit2.FetchOptions( proxy_opts = LibGit2.ProxyOptions(url = Cstring("https://my_proxy_url.com"))) julia> fetch(remote, "master", options=fo) ``` """ @kwdef struct ProxyOptions version::Cuint = Cuint(1) proxytype::Consts.GIT_PROXY = Consts.PROXY_AUTO url::Cstring = Cstring(C_NULL) credential_cb::Ptr{Cvoid} = C_NULL certificate_cb::Ptr{Cvoid} = certificate_cb() payload::Any = nothing end @assert Base.allocatedinline(ProxyOptions) """ LibGit2.FetchOptions Matches the [`git_fetch_options`](https://libgit2.org/libgit2/#HEAD/type/git_fetch_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `callbacks`: remote callbacks to use during the fetch. * `prune`: whether to perform a prune after the fetch or not. The default is to use the setting from the `GitConfig`. * `update_fetchhead`: whether to update the [`FetchHead`](@ref) after the fetch. The default is to perform the update, which is the normal git behavior. * `download_tags`: whether to download tags present at the remote or not. The default is to request the tags for objects which are being downloaded anyway from the server. * `proxy_opts`: options for connecting to the remote through a proxy. See [`ProxyOptions`](@ref). Only present on libgit2 versions newer than or equal to 0.25.0. * `custom_headers`: any extra headers needed for the fetch. Only present on libgit2 versions newer than or equal to 0.24.0. """ @kwdef struct FetchOptions version::Cuint = Cuint(1) callbacks::RemoteCallbacks = RemoteCallbacks() prune::Cint = Consts.FETCH_PRUNE_UNSPECIFIED update_fetchhead::Cint = Cint(1) download_tags::Cint = Consts.REMOTE_DOWNLOAD_TAGS_AUTO @static if LibGit2.VERSION >= v"0.25.0" proxy_opts::ProxyOptions = ProxyOptions() end @static if LibGit2.VERSION >= v"1.4.0" follow_redirects::Cuint = Cuint(0) end @static if LibGit2.VERSION >= v"0.24.0" custom_headers::StrArrayStruct = StrArrayStruct() end end @assert Base.allocatedinline(FetchOptions) """ LibGit2.CloneOptions Matches the [`git_clone_options`](https://libgit2.org/libgit2/#HEAD/type/git_clone_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `checkout_opts`: The options for performing the checkout of the remote as part of the clone. * `fetch_opts`: The options for performing the pre-checkout fetch of the remote as part of the clone. * `bare`: If `0`, clone the full remote repository. If non-zero, perform a bare clone, in which there is no local copy of the source files in the repository and the [`gitdir`](@ref) and [`workdir`](@ref) are the same. * `localclone`: Flag whether to clone a local object database or do a fetch. The default is to let git decide. It will not use the git-aware transport for a local clone, but will use it for URLs which begin with `file://`. * `checkout_branch`: The name of the branch to checkout. If an empty string, the default branch of the remote will be checked out. * `repository_cb`: An optional callback which will be used to create the *new* repository into which the clone is made. * `repository_cb_payload`: The payload for the repository callback. * `remote_cb`: An optional callback used to create the [`GitRemote`](@ref) before making the clone from it. * `remote_cb_payload`: The payload for the remote callback. """ @kwdef struct CloneOptions version::Cuint = Cuint(1) checkout_opts::CheckoutOptions = CheckoutOptions() fetch_opts::FetchOptions = FetchOptions() bare::Cint = Cint(0) localclone::Cint = Consts.CLONE_LOCAL_AUTO checkout_branch::Cstring = Cstring(C_NULL) repository_cb::Ptr{Cvoid} = C_NULL repository_cb_payload::Any = nothing remote_cb::Ptr{Cvoid} = C_NULL remote_cb_payload::Any = nothing end @assert Base.allocatedinline(CloneOptions) """ LibGit2.DiffOptionsStruct Matches the [`git_diff_options`](https://libgit2.org/libgit2/#HEAD/type/git_diff_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `flags`: flags controlling which files will appear in the diff. Defaults to `DIFF_NORMAL`. * `ignore_submodules`: whether to look at files in submodules or not. Defaults to `SUBMODULE_IGNORE_UNSPECIFIED`, which means the submodule's configuration will control whether it appears in the diff or not. * `pathspec`: path to files to include in the diff. Default is to use all files in the repository. * `notify_cb`: optional callback which will notify the user of changes to the diff as file deltas are added to it. * `progress_cb`: optional callback which will display diff progress. Only relevant on libgit2 versions at least as new as 0.24.0. * `payload`: the payload to pass to `notify_cb` and `progress_cb`. * `context_lines`: the number of *unchanged* lines used to define the edges of a hunk. This is also the number of lines which will be shown before/after a hunk to provide context. Default is 3. * `interhunk_lines`: the maximum number of *unchanged* lines *between* two separate hunks allowed before the hunks will be combined. Default is 0. * `id_abbrev`: sets the length of the abbreviated [`GitHash`](@ref) to print. Default is `7`. * `max_size`: the maximum file size of a blob. Above this size, it will be treated as a binary blob. The default is 512 MB. * `old_prefix`: the virtual file directory in which to place old files on one side of the diff. Default is `"a"`. * `new_prefix`: the virtual file directory in which to place new files on one side of the diff. Default is `"b"`. """ @kwdef struct DiffOptionsStruct version::Cuint = Consts.DIFF_OPTIONS_VERSION flags::UInt32 = Consts.DIFF_NORMAL # options controlling which files are in the diff ignore_submodules::GIT_SUBMODULE_IGNORE = Consts.SUBMODULE_IGNORE_UNSPECIFIED pathspec::StrArrayStruct = StrArrayStruct() notify_cb::Ptr{Cvoid} = C_NULL @static if LibGit2.VERSION >= v"0.24.0" progress_cb::Ptr{Cvoid} = C_NULL end payload::Any = nothing # options controlling how the diff text is generated context_lines::UInt32 = UInt32(3) interhunk_lines::UInt32 = UInt32(0) id_abbrev::UInt16 = UInt16(7) max_size::Int64 = Int64(512*1024*1024) #512Mb old_prefix::Cstring = Cstring(C_NULL) new_prefix::Cstring = Cstring(C_NULL) end @assert Base.allocatedinline(DiffOptionsStruct) """ LibGit2.DescribeOptions Matches the [`git_describe_options`](https://libgit2.org/libgit2/#HEAD/type/git_describe_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `max_candidates_tags`: consider this many most recent tags in `refs/tags` to describe a commit. Defaults to 10 (so that the 10 most recent tags would be examined to see if they describe a commit). * `describe_strategy`: whether to consider all entries in `refs/tags` (equivalent to `git-describe --tags`) or all entries in `refs/` (equivalent to `git-describe --all`). The default is to only show annotated tags. If `Consts.DESCRIBE_TAGS` is passed, all tags, annotated or not, will be considered. If `Consts.DESCRIBE_ALL` is passed, any ref in `refs/` will be considered. * `pattern`: only consider tags which match `pattern`. Supports glob expansion. * `only_follow_first_parent`: when finding the distance from a matching reference to the described object, only consider the distance from the first parent. * `show_commit_oid_as_fallback`: if no matching reference can be found which describes a commit, show the commit's [`GitHash`](@ref) instead of throwing an error (the default behavior). """ @kwdef struct DescribeOptions version::Cuint = Cuint(1) max_candidates_tags::Cuint = Cuint(10) describe_strategy::Cuint = Consts.DESCRIBE_DEFAULT pattern::Cstring = Cstring(C_NULL) only_follow_first_parent::Cint = Cint(0) show_commit_oid_as_fallback::Cint = Cint(0) end @assert Base.allocatedinline(DescribeOptions) """ LibGit2.DescribeFormatOptions Matches the [`git_describe_format_options`](https://libgit2.org/libgit2/#HEAD/type/git_describe_format_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `abbreviated_size`: lower bound on the size of the abbreviated `GitHash` to use, defaulting to `7`. * `always_use_long_format`: set to `1` to use the long format for strings even if a short format can be used. * `dirty_suffix`: if set, this will be appended to the end of the description string if the [`workdir`](@ref) is dirty. """ @kwdef struct DescribeFormatOptions version::Cuint = Cuint(1) abbreviated_size::Cuint = Cuint(7) always_use_long_format::Cint = Cint(0) dirty_suffix::Cstring = Cstring(C_NULL) end @assert Base.allocatedinline(DescribeFormatOptions) """ LibGit2.DiffFile Description of one side of a delta. Matches the [`git_diff_file`](https://libgit2.org/libgit2/#HEAD/type/git_diff_file) struct. The fields represent: * `id`: the [`GitHash`](@ref) of the item in the diff. If the item is empty on this side of the diff (for instance, if the diff is of the removal of a file), this will be `GitHash(0)`. * `path`: a `NULL` terminated path to the item relative to the working directory of the repository. * `size`: the size of the item in bytes. * `flags`: a combination of the [`git_diff_flag_t`](https://libgit2.org/libgit2/#HEAD/type/git_diff_flag_t) flags. The `i`th bit of this integer sets the `i`th flag. * `mode`: the [`stat`](@ref) mode for the item. * `id_abbrev`: only present in LibGit2 versions newer than or equal to `0.25.0`. The length of the `id` field when converted using [`string`](@ref). Usually equal to `OID_HEXSZ` ($OID_HEXSZ). """ struct DiffFile id::GitHash path::Cstring size::Int64 flags::UInt32 mode::UInt16 @static if LibGit2.VERSION >= v"0.25.0" id_abbrev::UInt16 end end function Base.show(io::IO, df::DiffFile) println(io, "DiffFile:") println(io, "Oid: $(df.id)") println(io, "Path: $(df.path)") println(io, "Size: $(df.size)") end """ LibGit2.DiffDelta Description of changes to one entry. Matches the [`git_diff_delta`](https://libgit2.org/libgit2/#HEAD/type/git_diff_delta) struct. The fields represent: * `status`: One of `Consts.DELTA_STATUS`, indicating whether the file has been added/modified/deleted. * `flags`: Flags for the delta and the objects on each side. Determines whether to treat the file(s) as binary/text, whether they exist on each side of the diff, and whether the object ids are known to be correct. * `similarity`: Used to indicate if a file has been renamed or copied. * `nfiles`: The number of files in the delta (for instance, if the delta was run on a submodule commit id, it may contain more than one file). * `old_file`: A [`DiffFile`](@ref) containing information about the file(s) before the changes. * `new_file`: A [`DiffFile`](@ref) containing information about the file(s) after the changes. """ struct DiffDelta status::Cint flags::UInt32 similarity::UInt16 nfiles::UInt16 old_file::DiffFile new_file::DiffFile end function Base.show(io::IO, dd::DiffDelta) println(io, "DiffDelta:") println(io, "Status: $(Consts.DELTA_STATUS(dd.status))") println(io, "Number of files: $(dd.nfiles)") println(io, "Old file:\n$(dd.old_file)") println(io, "New file:\n$(dd.new_file)") end """ LibGit2.MergeOptions Matches the [`git_merge_options`](https://libgit2.org/libgit2/#HEAD/type/git_merge_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `flags`: an `enum` for flags describing merge behavior. Defined in [`git_merge_flag_t`](https://github.com/libgit2/libgit2/blob/HEAD/include/git2/merge.h#L95). The corresponding Julia enum is `GIT_MERGE` and has values: - `MERGE_FIND_RENAMES`: detect if a file has been renamed between the common ancestor and the "ours" or "theirs" side of the merge. Allows merges where a file has been renamed. - `MERGE_FAIL_ON_CONFLICT`: exit immediately if a conflict is found rather than trying to resolve it. - `MERGE_SKIP_REUC`: do not write the REUC extension on the index resulting from the merge. - `MERGE_NO_RECURSIVE`: if the commits being merged have multiple merge bases, use the first one, rather than trying to recursively merge the bases. * `rename_threshold`: how similar two files must to consider one a rename of the other. This is an integer that sets the percentage similarity. The default is 50. * `target_limit`: the maximum number of files to compare with to look for renames. The default is 200. * `metric`: optional custom function to use to determine the similarity between two files for rename detection. * `recursion_limit`: the upper limit on the number of merges of common ancestors to perform to try to build a new virtual merge base for the merge. The default is no limit. This field is only present on libgit2 versions newer than 0.24.0. * `default_driver`: the merge driver to use if both sides have changed. This field is only present on libgit2 versions newer than 0.25.0. * `file_favor`: how to handle conflicting file contents for the `text` driver. - `MERGE_FILE_FAVOR_NORMAL`: if both sides of the merge have changes to a section, make a note of the conflict in the index which `git checkout` will use to create a merge file, which the user can then reference to resolve the conflicts. This is the default. - `MERGE_FILE_FAVOR_OURS`: if both sides of the merge have changes to a section, use the version in the "ours" side of the merge in the index. - `MERGE_FILE_FAVOR_THEIRS`: if both sides of the merge have changes to a section, use the version in the "theirs" side of the merge in the index. - `MERGE_FILE_FAVOR_UNION`: if both sides of the merge have changes to a section, include each unique line from both sides in the file which is put into the index. * `file_flags`: guidelines for merging files. """ @kwdef struct MergeOptions version::Cuint = Cuint(1) flags::Cint = Cint(0) rename_threshold::Cuint = Cuint(50) target_limit::Cuint = Cuint(200) metric::Ptr{Cvoid} = C_NULL @static if LibGit2.VERSION >= v"0.24.0" recursion_limit::Cuint = Cuint(0) end @static if LibGit2.VERSION >= v"0.25.0" default_driver::Cstring = Cstring(C_NULL) end file_favor::GIT_MERGE_FILE_FAVOR = Consts.MERGE_FILE_FAVOR_NORMAL file_flags::GIT_MERGE_FILE = Consts.MERGE_FILE_DEFAULT end @assert Base.allocatedinline(MergeOptions) """ LibGit2.BlameOptions Matches the [`git_blame_options`](https://libgit2.org/libgit2/#HEAD/type/git_blame_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `flags`: one of `Consts.BLAME_NORMAL` or `Consts.BLAME_FIRST_PARENT` (the other blame flags are not yet implemented by libgit2). * `min_match_characters`: the minimum number of *alphanumeric* characters which much change in a commit in order for the change to be associated with that commit. The default is 20. Only takes effect if one of the `Consts.BLAME_*_COPIES` flags are used, which libgit2 does not implement yet. * `newest_commit`: the [`GitHash`](@ref) of the newest commit from which to look at changes. * `oldest_commit`: the [`GitHash`](@ref) of the oldest commit from which to look at changes. * `min_line`: the first line of the file from which to starting blaming. The default is `1`. * `max_line`: the last line of the file to which to blame. The default is `0`, meaning the last line of the file. """ @kwdef struct BlameOptions version::Cuint = Cuint(1) flags::UInt32 = UInt32(0) min_match_characters::UInt16 = UInt16(20) newest_commit::GitHash = GitHash() oldest_commit::GitHash = GitHash() min_line::Csize_t = Csize_t(1) max_line::Csize_t = Csize_t(0) end @assert Base.allocatedinline(BlameOptions) """ LibGit2.PushOptions Matches the [`git_push_options`](https://libgit2.org/libgit2/#HEAD/type/git_push_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `parallelism`: if a pack file must be created, this variable sets the number of worker threads which will be spawned by the packbuilder. If `0`, the packbuilder will auto-set the number of threads to use. The default is `1`. * `callbacks`: the callbacks (e.g. for authentication with the remote) to use for the push. * `proxy_opts`: only relevant if the LibGit2 version is greater than or equal to `0.25.0`. Sets options for using a proxy to communicate with a remote. See [`ProxyOptions`](@ref) for more information. * `custom_headers`: only relevant if the LibGit2 version is greater than or equal to `0.24.0`. Extra headers needed for the push operation. """ @kwdef struct PushOptions version::Cuint = Cuint(1) parallelism::Cint = Cint(1) callbacks::RemoteCallbacks = RemoteCallbacks() @static if LibGit2.VERSION >= v"0.25.0" proxy_opts::ProxyOptions = ProxyOptions() end @static if LibGit2.VERSION >= v"1.4.0" follow_redirects::Cuint = Cuint(0) end @static if LibGit2.VERSION >= v"0.24.0" custom_headers::StrArrayStruct = StrArrayStruct() end end @assert Base.allocatedinline(PushOptions) """ LibGit2.CherrypickOptions Matches the [`git_cherrypick_options`](https://libgit2.org/libgit2/#HEAD/type/git_cherrypick_options) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `mainline`: if cherrypicking a merge commit, specifies the parent number (starting at `1`) which will allow cherrypick to apply the changes relative to that parent. Only relevant if cherrypicking a merge commit. Default is `0`. * `merge_opts`: options for merging the changes in. See [`MergeOptions`](@ref) for more information. * `checkout_opts`: options for the checkout of the commit being cherrypicked. See [`CheckoutOptions`](@ref) for more information. """ @kwdef struct CherrypickOptions version::Cuint = Cuint(1) mainline::Cuint = Cuint(0) merge_opts::MergeOptions = MergeOptions() checkout_opts::CheckoutOptions = CheckoutOptions() end @assert Base.allocatedinline(CherrypickOptions) """ LibGit2.IndexTime Matches the [`git_index_time`](https://libgit2.org/libgit2/#HEAD/type/git_index_time) struct. """ struct IndexTime seconds::Int64 nanoseconds::Cuint end """ LibGit2.IndexEntry In-memory representation of a file entry in the index. Matches the [`git_index_entry`](https://libgit2.org/libgit2/#HEAD/type/git_index_entry) struct. """ struct IndexEntry ctime::IndexTime mtime::IndexTime dev::UInt32 ino::UInt32 mode::UInt32 uid::UInt32 gid::UInt32 file_size::Int64 id::GitHash flags::UInt16 flags_extended::UInt16 path::Ptr{UInt8} end Base.show(io::IO, ie::IndexEntry) = print(io, "IndexEntry($(string(ie.id)))") """ LibGit2.RebaseOptions Matches the `git_rebase_options` struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `quiet`: inform other git clients helping with/working on the rebase that the rebase should be done "quietly". Used for interoperability. The default is `1`. * `inmemory`: start an in-memory rebase. Callers working on the rebase can go through its steps and commit any changes, but cannot rewind HEAD or update the repository. The [`workdir`](@ref) will not be modified. Only present on libgit2 versions newer than or equal to 0.24.0. * `rewrite_notes_ref`: name of the reference to notes to use to rewrite the commit notes as the rebase is finished. * `merge_opts`: merge options controlling how the trees will be merged at each rebase step. Only present on libgit2 versions newer than or equal to 0.24.0. * `checkout_opts`: checkout options for writing files when initializing the rebase, stepping through it, and aborting it. See [`CheckoutOptions`](@ref) for more information. """ @kwdef struct RebaseOptions version::Cuint = Cuint(1) quiet::Cint = Cint(1) @static if LibGit2.VERSION >= v"0.24.0" inmemory::Cint = Cint(0) end rewrite_notes_ref::Cstring = Cstring(C_NULL) @static if LibGit2.VERSION >= v"0.24.0" merge_opts::MergeOptions = MergeOptions() end checkout_opts::CheckoutOptions = CheckoutOptions() end @assert Base.allocatedinline(RebaseOptions) """ LibGit2.RebaseOperation Describes a single instruction/operation to be performed during the rebase. Matches the [`git_rebase_operation`](https://libgit2.org/libgit2/#HEAD/type/git_rebase_operation_t) struct. The fields represent: * `optype`: the type of rebase operation currently being performed. The options are: - `REBASE_OPERATION_PICK`: cherry-pick the commit in question. - `REBASE_OPERATION_REWORD`: cherry-pick the commit in question, but rewrite its message using the prompt. - `REBASE_OPERATION_EDIT`: cherry-pick the commit in question, but allow the user to edit the commit's contents and its message. - `REBASE_OPERATION_SQUASH`: squash the commit in question into the previous commit. The commit messages of the two commits will be merged. - `REBASE_OPERATION_FIXUP`: squash the commit in question into the previous commit. Only the commit message of the previous commit will be used. - `REBASE_OPERATION_EXEC`: do not cherry-pick a commit. Run a command and continue if the command exits successfully. * `id`: the [`GitHash`](@ref) of the commit being worked on during this rebase step. * `exec`: in case `REBASE_OPERATION_EXEC` is used, the command to run during this step (for instance, running the test suite after each commit). """ struct RebaseOperation optype::Cint id::GitHash exec::Cstring end function Base.show(io::IO, rbo::RebaseOperation) println(io, "RebaseOperation($(string(rbo.id)))") println(io, "Operation type: $(Consts.GIT_REBASE_OPERATION(rbo.optype))") end """ LibGit2.StatusOptions Options to control how `git_status_foreach_ext()` will issue callbacks. Matches the [`git_status_opt_t`](https://libgit2.org/libgit2/#HEAD/type/git_status_opt_t) struct. The fields represent: * `version`: version of the struct in use, in case this changes later. For now, always `1`. * `show`: a flag for which files to examine and in which order. The default is `Consts.STATUS_SHOW_INDEX_AND_WORKDIR`. * `flags`: flags for controlling any callbacks used in a status call. * `pathspec`: an array of paths to use for path-matching. The behavior of the path-matching will vary depending on the values of `show` and `flags`. * The `baseline` is the tree to be used for comparison to the working directory and index; defaults to HEAD. """ @kwdef struct StatusOptions version::Cuint = Cuint(1) show::Cint = Consts.STATUS_SHOW_INDEX_AND_WORKDIR flags::Cuint = Consts.STATUS_OPT_INCLUDE_UNTRACKED | Consts.STATUS_OPT_RECURSE_UNTRACKED_DIRS | Consts.STATUS_OPT_RENAMES_HEAD_TO_INDEX | Consts.STATUS_OPT_SORT_CASE_SENSITIVELY pathspec::StrArrayStruct = StrArrayStruct() @static if LibGit2.VERSION >= v"0.27.0" baseline::Ptr{Cvoid} = C_NULL end end @assert Base.allocatedinline(StatusOptions) """ LibGit2.StatusEntry Providing the differences between the file as it exists in HEAD and the index, and providing the differences between the index and the working directory. Matches the `git_status_entry` struct. The fields represent: * `status`: contains the status flags for the file, indicating if it is current, or has been changed in some way in the index or work tree. * `head_to_index`: a pointer to a [`DiffDelta`](@ref) which encapsulates the difference(s) between the file as it exists in HEAD and in the index. * `index_to_workdir`: a pointer to a `DiffDelta` which encapsulates the difference(s) between the file as it exists in the index and in the [`workdir`](@ref). """ struct StatusEntry status::Cuint head_to_index::Ptr{DiffDelta} index_to_workdir::Ptr{DiffDelta} end """ LibGit2.FetchHead Contains the information about HEAD during a fetch, including the name and URL of the branch fetched from, the oid of the HEAD, and whether the fetched HEAD has been merged locally. The fields represent: * `name`: The name in the local reference database of the fetch head, for example, `"refs/heads/master"`. * `url`: The URL of the fetch head. * `oid`: The [`GitHash`](@ref) of the tip of the fetch head. * `ismerge`: Boolean flag indicating whether the changes at the remote have been merged into the local copy yet or not. If `true`, the local copy is up to date with the remote fetch head. """ struct FetchHead name::String url::String oid::GitHash ismerge::Bool end function Base.show(io::IO, fh::FetchHead) println(io, "FetchHead:") println(io, "Name: $(fh.name)") println(io, "URL: $(fh.url)") print(io, "OID: ") show(io, fh.oid) println(io) println(io, "Merged: $(fh.ismerge)") end """ LibGit2.ConfigEntry Matches the [`git_config_entry`](https://libgit2.org/libgit2/#HEAD/type/git_config_entry) struct. """ struct ConfigEntry name::Cstring value::Cstring include_depth::Cuint level::GIT_CONFIG free::Ptr{Cvoid} payload::Ptr{Cvoid} # User is not permitted to read or write this field end @assert Base.allocatedinline(ConfigEntry) function Base.show(io::IO, ce::ConfigEntry) print(io, "ConfigEntry(\"", unsafe_string(ce.name), "\", \"", unsafe_string(ce.value), "\")") end """ LibGit2.split_cfg_entry(ce::LibGit2.ConfigEntry) -> Tuple{String,String,String,String} Break the `ConfigEntry` up to the following pieces: section, subsection, name, and value. # Examples Given the git configuration file containing: ``` [credential "https://example.com"] username = me ``` The `ConfigEntry` would look like the following: ```julia-repl julia> entry ConfigEntry("credential.https://example.com.username", "me") julia> LibGit2.split_cfg_entry(entry) ("credential", "https://example.com", "username", "me") ``` Refer to the [git config syntax documentation](https://git-scm.com/docs/git-config#_syntax) for more details. """ function split_cfg_entry(ce::ConfigEntry) key = unsafe_string(ce.name) # Determine the positions of the delimiters subsection_delim = something(findfirst(isequal('.'), key), 0) name_delim = something(findlast(isequal('.'), key), 0) section = SubString(key, 1, subsection_delim - 1) subsection = SubString(key, subsection_delim + 1, name_delim - 1) name = SubString(key, name_delim + 1) value = unsafe_string(ce.value) return (section, subsection, name, value) end # Abstract object types """ AbstractGitObject `AbstractGitObject`s must obey the following interface: - `obj.owner`, if present, must be a `Union{Nothing,GitRepo,GitTree}` - `obj.ptr`, if present, must be a `Union{Ptr{Cvoid},Ptr{SignatureStruct}}` """ abstract type AbstractGitObject end function Base.getproperty(obj::AbstractGitObject, name::Symbol) # These type-assertions enforce the interface requirements above. # They assist type-inference in cases where the compiler only knows that it # has an `AbstractGitObject` without being certain about the concrete type. # See detailed explanation in https://github.com/JuliaLang/julia/pull/36452. if name === :owner return getfield(obj, :owner)::Union{Nothing,GitRepo,GitTree} elseif name === :ptr return getfield(obj, :ptr)::Union{Ptr{Cvoid},Ptr{SignatureStruct}} else return getfield(obj, name) end end Base.isempty(obj::AbstractGitObject) = (obj.ptr == C_NULL) # `GitObject`s must obey the following interface: # - `obj.owner` must be a `GitRepo` # - `obj.ptr` must be a Ptr{Cvoid} abstract type GitObject <: AbstractGitObject end function Base.getproperty(obj::GitObject, name::Symbol) if name === :owner return getfield(obj, :owner)::GitRepo elseif name === :ptr return getfield(obj, :ptr)::Ptr{Cvoid} else return getfield(obj, name) end end for (typ, owntyp, sup, cname) in Tuple{Symbol,Any,Symbol,Symbol}[ (:GitRepo, nothing, :AbstractGitObject, :git_repository), (:GitConfig, :(Union{GitRepo, Nothing}), :AbstractGitObject, :git_config), (:GitIndex, :(Union{GitRepo, Nothing}), :AbstractGitObject, :git_index), (:GitRemote, :GitRepo, :AbstractGitObject, :git_remote), (:GitRevWalker, :GitRepo, :AbstractGitObject, :git_revwalk), (:GitReference, :GitRepo, :AbstractGitObject, :git_reference), (:GitDescribeResult, :GitRepo, :AbstractGitObject, :git_describe_result), (:GitDiff, :GitRepo, :AbstractGitObject, :git_diff), (:GitDiffStats, :GitRepo, :AbstractGitObject, :git_diff_stats), (:GitAnnotated, :GitRepo, :AbstractGitObject, :git_annotated_commit), (:GitRebase, :GitRepo, :AbstractGitObject, :git_rebase), (:GitBlame, :GitRepo, :AbstractGitObject, :git_blame), (:GitStatus, :GitRepo, :AbstractGitObject, :git_status_list), (:GitBranchIter, :GitRepo, :AbstractGitObject, :git_branch_iterator), (:GitConfigIter, nothing, :AbstractGitObject, :git_config_iterator), (:GitUnknownObject, :GitRepo, :GitObject, :git_object), (:GitCommit, :GitRepo, :GitObject, :git_commit), (:GitBlob, :GitRepo, :GitObject, :git_blob), (:GitTree, :GitRepo, :GitObject, :git_tree), (:GitTag, :GitRepo, :GitObject, :git_tag), (:GitTreeEntry, :GitTree, :AbstractGitObject, :git_tree_entry), ] if owntyp === nothing @eval mutable struct $typ <: $sup ptr::Ptr{Cvoid} function $typ(ptr::Ptr{Cvoid}, fin::Bool=true) # fin=false should only be used when the pointer should not be free'd # e.g. from within callback functions which are passed a pointer @assert ptr != C_NULL obj = new(ptr) if fin Threads.atomic_add!(REFCOUNT, 1) finalizer(Base.close, obj) end return obj end end @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr else @eval mutable struct $typ <: $sup owner::$owntyp ptr::Ptr{Cvoid} function $typ(owner::$owntyp, ptr::Ptr{Cvoid}, fin::Bool=true) @assert ptr != C_NULL obj = new(owner, ptr) if fin Threads.atomic_add!(REFCOUNT, 1) finalizer(Base.close, obj) end return obj end end @eval Base.unsafe_convert(::Type{Ptr{Cvoid}}, x::$typ) = x.ptr if isa(owntyp, Expr) && owntyp.args[1] === :Union && owntyp.args[3] === :Nothing @eval begin $typ(ptr::Ptr{Cvoid}, fin::Bool=true) = $typ(nothing, ptr, fin) end end end @eval function Base.close(obj::$typ) if obj.ptr != C_NULL ensure_initialized() ccall(($(string(cname, :_free)), libgit2), Cvoid, (Ptr{Cvoid},), obj.ptr) obj.ptr = C_NULL if Threads.atomic_sub!(REFCOUNT, 1) == 1 # will the last finalizer please turn out the lights? ccall((:git_libgit2_shutdown, libgit2), Cint, ()) end end end end ## Calling `GitObject(repo, ...)` will automatically resolve to the appropriate type. function GitObject(repo::GitRepo, ptr::Ptr{Cvoid}) T = objtype(Consts.OBJECT(ptr)) T(repo, ptr) end """ LibGit2.GitSignature This is a Julia wrapper around a pointer to a [`git_signature`](https://libgit2.org/libgit2/#HEAD/type/git_signature) object. """ mutable struct GitSignature <: AbstractGitObject ptr::Ptr{SignatureStruct} function GitSignature(ptr::Ptr{SignatureStruct}) @assert ptr != C_NULL obj = new(ptr) finalizer(Base.close, obj) return obj end end function Base.close(obj::GitSignature) if obj.ptr != C_NULL ensure_initialized() ccall((:git_signature_free, libgit2), Cvoid, (Ptr{SignatureStruct},), obj.ptr) obj.ptr = C_NULL end end # Structure has the same layout as SignatureStruct mutable struct Signature name::String email::String time::Int64 time_offset::Cint end """ LibGit2.BlameHunk Matches the [`git_blame_hunk`](https://libgit2.org/libgit2/#HEAD/type/git_blame_hunk) struct. The fields represent: * `lines_in_hunk`: the number of lines in this hunk of the blame. * `final_commit_id`: the [`GitHash`](@ref) of the commit where this section was last changed. * `final_start_line_number`: the *one based* line number in the file where the hunk starts, in the *final* version of the file. * `final_signature`: the signature of the person who last modified this hunk. You will need to pass this to `Signature` to access its fields. * `orig_commit_id`: the [`GitHash`](@ref) of the commit where this hunk was first found. * `orig_path`: the path to the file where the hunk originated. This may be different than the current/final path, for instance if the file has been moved. * `orig_start_line_number`: the *one based* line number in the file where the hunk starts, in the *original* version of the file at `orig_path`. * `orig_signature`: the signature of the person who introduced this hunk. You will need to pass this to `Signature` to access its fields. * `boundary`: `'1'` if the original commit is a "boundary" commit (for instance, if it's equal to an oldest commit set in `options`). """ @kwdef struct BlameHunk lines_in_hunk::Csize_t = Csize_t(0) final_commit_id::GitHash = GitHash() final_start_line_number::Csize_t = Csize_t(0) final_signature::Ptr{SignatureStruct} = Ptr{SignatureStruct}(C_NULL) orig_commit_id::GitHash = GitHash() orig_path::Cstring = Cstring(C_NULL) orig_start_line_number::Csize_t = Csize_t(0) orig_signature::Ptr{SignatureStruct} = Ptr{SignatureStruct}(C_NULL) boundary::Char = '\0' end @assert Base.allocatedinline(BlameHunk) """ with(f::Function, obj) Resource management helper function. Applies `f` to `obj`, making sure to call `close` on `obj` after `f` successfully returns or throws an error. Ensures that allocated git resources are finalized as soon as they are no longer needed. """ function with(f::Function, obj) try f(obj) finally close(obj) end end with(f::Function, ::Type{T}, args...) where {T} = with(f, T(args...)) """ with_warn(f::Function, ::Type{T}, args...) Resource management helper function. Apply `f` to `args`, first constructing an instance of type `T` from `args`. Makes sure to call `close` on the resulting object after `f` successfully returns or throws an error. Ensures that allocated git resources are finalized as soon as they are no longer needed. If an error is thrown by `f`, a warning is shown containing the error. """ function with_warn(f::Function, ::Type{T}, args...) where T obj = T(args...) try with(f, obj) catch err @warn "$(string(T)) thrown exception:" exception=err end end """ LibGit2.Consts.OBJECT(::Type{T}) where T<:GitObject The `OBJECT` enum value corresponding to type `T`. """ Consts.OBJECT(::Type{GitCommit}) = Consts.OBJ_COMMIT Consts.OBJECT(::Type{GitTree}) = Consts.OBJ_TREE Consts.OBJECT(::Type{GitBlob}) = Consts.OBJ_BLOB Consts.OBJECT(::Type{GitTag}) = Consts.OBJ_TAG Consts.OBJECT(::Type{GitUnknownObject}) = Consts.OBJ_ANY Consts.OBJECT(::Type{GitObject}) = Consts.OBJ_ANY function Consts.OBJECT(ptr::Ptr{Cvoid}) ensure_initialized() ccall((:git_object_type, libgit2), Consts.OBJECT, (Ptr{Cvoid},), ptr) end """ objtype(obj_type::Consts.OBJECT) Return the type corresponding to the enum value. """ function objtype(obj_type::Consts.OBJECT) if obj_type == Consts.OBJ_COMMIT GitCommit elseif obj_type == Consts.OBJ_TREE GitTree elseif obj_type == Consts.OBJ_BLOB GitBlob elseif obj_type == Consts.OBJ_TAG GitTag elseif obj_type == Consts.OBJ_ANY #this name comes from the header GitUnknownObject else throw(GitError(Error.Object, Error.ENOTFOUND, "Object type $obj_type is not supported")) end end abstract type AbstractCredential end """ isfilled(cred::AbstractCredential) -> Bool Verifies that a credential is ready for use in authentication. """ isfilled(::AbstractCredential) "Credential that support only `user` and `password` parameters" mutable struct UserPasswordCredential <: AbstractCredential user::String pass::Base.SecretBuffer function UserPasswordCredential(user::AbstractString="", pass::Union{AbstractString, Base.SecretBuffer}="") new(user, pass) end end function Base.setproperty!(cred::UserPasswordCredential, name::Symbol, value) if name === :pass field = getfield(cred, name) Base.shred!(field) end setfield!(cred, name, convert(fieldtype(typeof(cred), name), value)) end function Base.shred!(cred::UserPasswordCredential) cred.user = "" Base.shred!(cred.pass) return cred end function Base.:(==)(a::UserPasswordCredential, b::UserPasswordCredential) a.user == b.user && a.pass == b.pass end function isfilled(cred::UserPasswordCredential) !isempty(cred.user) && !isempty(cred.pass) end "SSH credential type" mutable struct SSHCredential <: AbstractCredential user::String pass::Base.SecretBuffer # Paths to private keys prvkey::String pubkey::String function SSHCredential(user="", pass="", prvkey="", pubkey="") new(user, pass, prvkey, pubkey) end end function Base.setproperty!(cred::SSHCredential, name::Symbol, value) if name === :pass field = getfield(cred, name) Base.shred!(field) end setfield!(cred, name, convert(fieldtype(typeof(cred), name), value)) end function Base.shred!(cred::SSHCredential) cred.user = "" Base.shred!(cred.pass) cred.prvkey = "" cred.pubkey = "" return cred end function Base.:(==)(a::SSHCredential, b::SSHCredential) a.user == b.user && a.pass == b.pass && a.prvkey == b.prvkey && a.pubkey == b.pubkey end function isfilled(cred::SSHCredential) !isempty(cred.user) && isfile(cred.prvkey) && isfile(cred.pubkey) && (!isempty(cred.pass) || !is_passphrase_required(cred.prvkey)) end "Caches credential information for re-use" struct CachedCredentials cred::Dict{String,AbstractCredential} CachedCredentials() = new(Dict{String,AbstractCredential}()) end Base.haskey(cache::CachedCredentials, cred_id) = Base.haskey(cache.cred, cred_id) Base.getindex(cache::CachedCredentials, cred_id) = Base.getindex(cache.cred, cred_id) Base.get!(cache::CachedCredentials, cred_id, default) = Base.get!(cache.cred, cred_id, default) function Base.shred!(p::CachedCredentials) foreach(Base.shred!, values(p.cred)) return p end function approve(cache::CachedCredentials, cred::AbstractCredential, url::AbstractString) cred_id = credential_identifier(url) if haskey(cache.cred, cred_id) # Shred the cached credential we'll be overwriting if it isn't identical cred !== cache.cred[cred_id] && Base.shred!(cache.cred[cred_id]) end cache.cred[cred_id] = cred nothing end function reject(cache::CachedCredentials, cred::AbstractCredential, url::AbstractString) cred_id = credential_identifier(url) if haskey(cache.cred, cred_id) # Shred the cached credential if it isn't the `cred` passed in cred !== cache.cred[cred_id] && Base.shred!(cache.cred[cred_id]) delete!(cache.cred, cred_id) end nothing end """ LibGit2.CredentialPayload Retains the state between multiple calls to the credential callback for the same URL. A `CredentialPayload` instance is expected to be `reset!` whenever it will be used with a different URL. """ mutable struct CredentialPayload explicit::Union{AbstractCredential, Nothing} cache::Union{CachedCredentials, Nothing} allow_ssh_agent::Bool # Allow the use of the SSH agent to get credentials allow_git_helpers::Bool # Allow the use of git credential helpers allow_prompt::Bool # Allow prompting the user for credentials config::GitConfig # Ephemeral state fields credential::Union{AbstractCredential, Nothing} first_pass::Bool use_ssh_agent::Bool use_env::Bool use_git_helpers::Bool remaining_prompts::Int url::String scheme::String username::String host::String function CredentialPayload( credential::Union{AbstractCredential, Nothing}=nothing, cache::Union{CachedCredentials, Nothing}=nothing, config::GitConfig=GitConfig(); allow_ssh_agent::Bool=true, allow_git_helpers::Bool=true, allow_prompt::Bool=true) payload = new(credential, cache, allow_ssh_agent, allow_git_helpers, allow_prompt, config) return reset!(payload) end end function CredentialPayload(credential::AbstractCredential; kwargs...) CredentialPayload(credential, nothing; kwargs...) end function CredentialPayload(cache::CachedCredentials; kwargs...) CredentialPayload(nothing, cache; kwargs...) end CredentialPayload(p::CredentialPayload) = p function Base.shred!(p::CredentialPayload) # Note: Avoid shredding the `explicit` or `cache` fields as these are just references # and it is not our responsibility to shred them. credential = p.credential credential !== nothing && Base.shred!(credential) p.credential = nothing end """ reset!(payload, [config]) -> CredentialPayload Reset the `payload` state back to the initial values so that it can be used again within the credential callback. If a `config` is provided the configuration will also be updated. """ function reset!(p::CredentialPayload, config::GitConfig=p.config) p.config = config p.credential = nothing p.first_pass = true p.use_ssh_agent = p.allow_ssh_agent p.use_env = true p.use_git_helpers = p.allow_git_helpers p.remaining_prompts = p.allow_prompt ? 3 : 0 p.url = "" p.scheme = "" p.username = "" p.host = "" return p end """ approve(payload::CredentialPayload; shred::Bool=true) -> Nothing Store the `payload` credential for re-use in a future authentication. Should only be called when authentication was successful. The `shred` keyword controls whether sensitive information in the payload credential field should be destroyed. Should only be set to `false` during testing. """ function approve(p::CredentialPayload; shred::Bool=true) cred = p.credential cred === nothing && return # No credential was used # Each `approve` call needs to avoid shredding the passed in credential as we need # the credential information intact for subsequent approve calls. cache = p.cache if cache !== nothing approve(cache, cred, p.url) shred = false # Avoid wiping `cred` as this would also wipe the cached copy end if p.allow_git_helpers approve(p.config, cred, p.url) end if shred Base.shred!(cred) p.credential = nothing end nothing end """ reject(payload::CredentialPayload; shred::Bool=true) -> Nothing Discard the `payload` credential from begin re-used in future authentication. Should only be called when authentication was unsuccessful. The `shred` keyword controls whether sensitive information in the payload credential field should be destroyed. Should only be set to `false` during testing. """ function reject(p::CredentialPayload; shred::Bool=true) cred = p.credential cred === nothing && return # No credential was used # Note: each `reject` call needs to avoid shredding the passed in credential as we need # the credential information intact for subsequent reject calls. cache = p.cache if cache !== nothing reject(cache, cred, p.url) end if p.allow_git_helpers reject(p.config, cred, p.url) end if shred Base.shred!(cred) p.credential = nothing end nothing end # Useful for functions which can handle various kinds of credentials const Creds = Union{CredentialPayload, AbstractCredential, CachedCredentials, Nothing} t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/signature.jl ������# This file is a part of Julia. License is MIT: https://julialang.org/license function Signature(ptr::Ptr{SignatureStruct}) sig = unsafe_load(ptr)::SignatureStruct name = unsafe_string(sig.name) email = unsafe_string(sig.email) time = sig.when.time offset = sig.when.offset return Signature(name, email, time, offset) end Signature(sig::GitSignature) = Signature(sig.ptr) function Signature(name::AbstractString, email::AbstractString) ensure_initialized() sig_ptr_ptr = Ref{Ptr{SignatureStruct}}(C_NULL) @check ccall((:git_signature_now, libgit2), Cint, (Ptr{Ptr{SignatureStruct}}, Cstring, Cstring), sig_ptr_ptr, name, email) sig = GitSignature(sig_ptr_ptr[]) s = Signature(sig.ptr) close(sig) return s end function Signature(repo::GitRepo) sig = default_signature(repo) s = Signature(sig.ptr) close(sig) return s end function Base.convert(::Type{GitSignature}, sig::Signature) ensure_initialized() sig_ptr_ptr = Ref{Ptr{SignatureStruct}}(C_NULL) @check ccall((:git_signature_new, libgit2), Cint, (Ptr{Ptr{SignatureStruct}}, Cstring, Cstring, Int64, Cint), sig_ptr_ptr, sig.name, sig.email, sig.time, sig.time_offset) return GitSignature(sig_ptr_ptr[]) end function yearmonthday(days) z = days + 306; h = 100z - 25; a = fld(h, 3652425); b = a - fld(a, 4) y = fld(100b + h, 36525); c = b + z - 365y - fld(y, 4); m = div(5c + 456, 153) d = c - div(153m - 457, 5); return m > 12 ? (y + 1, m - 12, d) : (y, m, d) end lpad0(x) = lpad(x, 2, '0') function unix2date(t) UNIXEPOCH = 62135683200000 rata = UNIXEPOCH + round(Int64, Int64(1000) * t) year, month, day = yearmonthday(fld(rata, 86400000)) secs = t % (24 * 60 * 60) mins = div(secs, 60) m, d = lpad0(month), lpad0(day) h, mi, s = lpad0.(round.(Int, (div(mins, 60), mins % 60, secs % 60))) return "$year-$m-$d $h:$mi:$s" end function Base.show(io::IO, sig::Signature) print(io, "Name: ", sig.name, ", ") print(io, "Email: ", sig.email, ", ") print(io, "Time: ", unix2date(sig.time + 60*sig.time_offset)) @printf(io, "%+03i:%02i", divrem(sig.time_offset, 60)...) end """Return signature object. Free it after use.""" function default_signature(repo::GitRepo) ensure_initialized() sig_ptr_ptr = Ref{Ptr{SignatureStruct}}(C_NULL) @check ccall((:git_signature_default, libgit2), Cint, (Ptr{Ptr{SignatureStruct}}, Ptr{Cvoid}), sig_ptr_ptr, repo.ptr) return GitSignature(sig_ptr_ptr[]) end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/oid.jl€������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitHash(ptr::Ptr{UInt8}) Construct a `GitHash` from a pointer to `UInt8` data containing the bytes of the SHA-1 hash. The constructor throws an error if the pointer is null, i.e. equal to `C_NULL`. """ function GitHash(ptr::Ptr{UInt8}) if ptr == C_NULL throw(ArgumentError("NULL pointer passed to GitHash() constructor")) end ensure_initialized() oid_ptr = Ref(GitHash()) @check ccall((:git_oid_fromraw, libgit2), Cint, (Ptr{GitHash}, Ptr{UInt8}), oid_ptr, ptr) return oid_ptr[] end """ GitHash(id::Vector{UInt8}) Construct a `GitHash` from a vector of $OID_RAWSZ bytes. """ function GitHash(id::Array{UInt8,1}) if length(id) != OID_RAWSZ throw(ArgumentError("invalid raw buffer size")) end return GitHash(pointer(id)) end """ GitHash(id::AbstractString) Construct a `GitHash` from a string of $OID_HEXSZ hexadecimal digits. """ function GitHash(id::AbstractString) bstr = String(id) len = sizeof(bstr) if len < OID_HEXSZ throw(ArgumentError("Input string is too short, use `GitShortHash` for partial hashes")) end ensure_initialized() oid_ptr = Ref{GitHash}() @check ccall((:git_oid_fromstrn, libgit2), Cint, (Ptr{GitHash}, Ptr{UInt8}, Csize_t), oid_ptr, bstr, len) return oid_ptr[] end """ GitShortHash(buf::Buffer) Construct a `GitShortHash` from the data stored in the given [`Buffer`](@ref). """ function GitShortHash(buf::Buffer) ensure_initialized() oid_ptr = Ref{GitHash}() @check ccall((:git_oid_fromstrn, libgit2), Cint, (Ptr{GitHash}, Ptr{UInt8}, Csize_t), oid_ptr, buf.ptr, buf.size) GitShortHash(oid_ptr[], buf.size) end """ GitShortHash(id::AbstractString) Construct a `GitShortHash` from a string of at most $OID_HEXSZ hexadecimal digits. """ function GitShortHash(id::AbstractString) ensure_initialized() bstr = String(id) len = sizeof(bstr) oid_ptr = Ref{GitHash}() @check ccall((:git_oid_fromstrn, libgit2), Cint, (Ptr{GitHash}, Ptr{UInt8}, Csize_t), oid_ptr, bstr, len) GitShortHash(oid_ptr[], len) end """ @githash_str -> AbstractGitHash Construct a git hash object from the given string, returning a `GitShortHash` if the string is shorter than $OID_HEXSZ hexadecimal digits, otherwise a `GitHash`. # Examples ```jldoctest julia> LibGit2.githash"d114feb74ce633" GitShortHash("d114feb74ce633") julia> LibGit2.githash"d114feb74ce63307afe878a5228ad014e0289a85" GitHash("d114feb74ce63307afe878a5228ad014e0289a85") ``` """ macro githash_str(id) bstr = String(id) if sizeof(bstr) < OID_HEXSZ GitShortHash(id) else GitHash(id) end end """ GitHash(ref::GitReference) Get the identifier (`GitHash`) of the object referred to by the direct reference `ref`. Note: this does not work for symbolic references; in such cases use `GitHash(repo::GitRepo, ref_name::AbstractString)` instead. """ function GitHash(ref::GitReference) isempty(ref) && return GitHash() reftype(ref) != Consts.REF_OID && return GitHash() ensure_initialized() GC.@preserve ref begin oid_ptr = ccall((:git_reference_target, libgit2), Ptr{UInt8}, (Ptr{Cvoid},), ref.ptr) oid_ptr == C_NULL && return GitHash() oid = GitHash(oid_ptr) end return oid end """ GitHash(repo::GitRepo, ref_name::AbstractString) Get the identifier (`GitHash`) of the object referred to by reference specified by `ref_name`. """ function GitHash(repo::GitRepo, ref_name::AbstractString) isempty(repo) && return GitHash() ensure_initialized() oid_ptr = Ref(GitHash()) @check ccall((:git_reference_name_to_id, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}, Cstring), oid_ptr, repo.ptr, ref_name) return oid_ptr[] end """ GitHash(obj::GitObject) Get the identifier (`GitHash`) of `obj`. """ function GitHash(obj::GitObject) ensure_initialized() GitHash(ccall((:git_object_id, libgit2), Ptr{UInt8}, (Ptr{Cvoid},), obj.ptr)) end ==(obj1::GitObject, obj2::GitObject) = GitHash(obj1) == GitHash(obj2) """ GitShortHash(obj::GitObject) Get a shortened identifier (`GitShortHash`) of `obj`. The minimum length (in characters) is determined by the `core.abbrev` config option, and will be of sufficient length to unambiguously identify the object in the repository. """ function GitShortHash(obj::GitObject) ensure_initialized() buf_ref = Ref(Buffer()) @check ccall((:git_object_short_id, libgit2), Cint, (Ptr{Buffer},Ptr{Cvoid}), buf_ref, obj.ptr) sid = GitShortHash(buf_ref[]) free(buf_ref) return sid end """ raw(id::GitHash) -> Vector{UInt8} Obtain the raw bytes of the [`GitHash`](@ref) as a vector of length $OID_RAWSZ. """ raw(id::GitHash) = collect(id.val) function Base.print(io::IO, id::GitHash) for i in id.val print(io, string(i, base = 16, pad = 2)) end end Base.string(id::GitShortHash) = string(id.hash)[1:id.len] Base.show(io::IO, id::GitHash) = print(io, "GitHash(\"$(string(id))\")") Base.show(io::IO, id::GitShortHash) = print(io, "GitShortHash(\"$(string(id))\")") Base.hash(id::GitHash, h::UInt) = hash(id.val, h) function Base.cmp(id1::GitHash, id2::GitHash) ensure_initialized() Int(ccall((:git_oid_cmp, libgit2), Cint, (Ptr{GitHash}, Ptr{GitHash}), Ref(id1), Ref(id2))) end function Base.cmp(id1::GitShortHash, id2::GitShortHash) ensure_initialized() # shortened hashes appear at the beginning of the order, i.e. # 000 < 01 < 010 < 011 < 0112 c = Int(ccall((:git_oid_ncmp, libgit2), Cint, (Ptr{GitHash}, Ptr{GitHash}, Csize_t), Ref(id1.hash), Ref(id2.hash), min(id1.len, id2.len))) return c == 0 ? cmp(id1.len, id2.len) : c end Base.cmp(id1::GitHash, id2::GitShortHash) = cmp(GitShortHash(id1, OID_HEXSZ), id2) Base.cmp(id1::GitShortHash, id2::GitHash) = cmp(id1, GitShortHash(id2, OID_HEXSZ)) ==(id1::GitHash, id2::GitHash) = cmp(id1, id2) == 0 Base.isless(id1::AbstractGitHash, id2::AbstractGitHash) = cmp(id1, id2) < 0 """ iszero(id::GitHash) -> Bool Determine whether all hexadecimal digits of the given [`GitHash`](@ref) are zero. """ function iszero(id::GitHash) for i in 1:OID_RAWSZ id.val[i] != zero(UInt8) && return false end return true end Base.zero(::Type{GitHash}) = GitHash() t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/reference.jlu-������# This file is a part of Julia. License is MIT: https://julialang.org/license function GitReference(repo::GitRepo, refname::AbstractString) ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_reference_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), ref_ptr_ptr, repo.ptr, refname) return GitReference(repo, ref_ptr_ptr[]) end function GitReference(repo::GitRepo, obj_oid::GitHash, refname::AbstractString = Consts.HEAD_FILE; force::Bool=false, msg::AbstractString="") ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_reference_create, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{UInt8}, Ptr{GitHash}, Cint, Cstring), ref_ptr_ptr, repo.ptr, refname, Ref(obj_oid), Cint(force), isempty(msg) ? C_NULL : msg) return GitReference(repo, ref_ptr_ptr[]) end """ LibGit2.isorphan(repo::GitRepo) Check if the current branch is an "orphan" branch, i.e. has no commits. The first commit to this branch will have no parents. """ function isorphan(repo::GitRepo) ensure_initialized() r = @check ccall((:git_repository_head_unborn, libgit2), Cint, (Ptr{Cvoid},), repo.ptr) r != 0 end """ LibGit2.head(repo::GitRepo) -> GitReference Return a `GitReference` to the current HEAD of `repo`. """ function head(repo::GitRepo) ensure_initialized() head_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_head, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), head_ptr_ptr, repo.ptr) return GitReference(repo, head_ptr_ptr[]) end """ LibGit2.shortname(ref::GitReference) Return a shortened version of the name of `ref` that's "human-readable". ```julia-repl julia> repo = GitRepo(path_to_repo); julia> branch_ref = LibGit2.head(repo); julia> LibGit2.name(branch_ref) "refs/heads/master" julia> LibGit2.shortname(branch_ref) "master" ``` """ function shortname(ref::GitReference) isempty(ref) && return "" ensure_initialized() GC.@preserve ref begin name_ptr = ccall((:git_reference_shorthand, libgit2), Cstring, (Ptr{Cvoid},), ref.ptr) name_ptr == C_NULL && return "" name = unsafe_string(name_ptr) end return name end """ LibGit2.reftype(ref::GitReference) -> Cint Return a `Cint` corresponding to the type of `ref`: * `0` if the reference is invalid * `1` if the reference is an object id * `2` if the reference is symbolic """ function reftype(ref::GitReference) ensure_initialized() return ccall((:git_reference_type, libgit2), Cint, (Ptr{Cvoid},), ref.ptr) end """ LibGit2.fullname(ref::GitReference) Return the name of the reference pointed to by the symbolic reference `ref`. If `ref` is not a symbolic reference, return an empty string. """ function fullname(ref::GitReference) isempty(ref) && return "" reftype(ref) == Consts.REF_OID && return "" ensure_initialized() GC.@preserve ref begin rname = ccall((:git_reference_symbolic_target, libgit2), Cstring, (Ptr{Cvoid},), ref.ptr) rname == C_NULL && return "" name = unsafe_string(rname) end return name end """ LibGit2.name(ref::GitReference) Return the full name of `ref`. """ function name(ref::GitReference) isempty(ref) && return "" ensure_initialized() GC.@preserve ref begin name_ptr = ccall((:git_reference_name, libgit2), Cstring, (Ptr{Cvoid},), ref.ptr) name_ptr == C_NULL && return "" name = unsafe_string(name_ptr) end return name end function branch(ref::GitReference) isempty(ref) && return "" ensure_initialized() str_ptr_ptr = Ref{Cstring}() GC.@preserve ref begin @check ccall((:git_branch_name, libgit2), Cint, (Ptr{Cstring}, Ptr{Cvoid},), str_ptr_ptr, ref.ptr) str = unsafe_string(str_ptr_ptr[]) end return str end function ishead(ref::GitReference) isempty(ref) && return false ensure_initialized() err = ccall((:git_branch_is_head, libgit2), Cint, (Ptr{Cvoid},), ref.ptr) return err == 1 end function isbranch(ref::GitReference) isempty(ref) && return false ensure_initialized() err = ccall((:git_reference_is_branch, libgit2), Cint, (Ptr{Cvoid},), ref.ptr) return err == 1 end function istag(ref::GitReference) isempty(ref) && return false ensure_initialized() err = ccall((:git_reference_is_tag, libgit2), Cint, (Ptr{Cvoid},), ref.ptr) return err == 1 end function isremote(ref::GitReference) isempty(ref) && return false ensure_initialized() err = ccall((:git_reference_is_remote, libgit2), Cint, (Ptr{Cvoid},), ref.ptr) return err == 1 end function Base.show(io::IO, ref::GitReference) println(io, "GitReference:") if isremote(ref) println(io, "Remote with name ", name(ref)) elseif isbranch(ref) println(io, "Branch with name ", name(ref)) if ishead(ref) println(io, "Branch is HEAD.") else println(io, "Branch is not HEAD.") end elseif istag(ref) println(io, "Tag with name ", name(ref)) end end """ peel([T,] ref::GitReference) Recursively peel `ref` until an object of type `T` is obtained. If no `T` is provided, then `ref` will be peeled until an object other than a [`GitTag`](@ref) is obtained. - A `GitTag` will be peeled to the object it references. - A [`GitCommit`](@ref) will be peeled to a [`GitTree`](@ref). !!! note Only annotated tags can be peeled to `GitTag` objects. Lightweight tags (the default) are references under `refs/tags/` which point directly to `GitCommit` objects. """ function peel(::Type{T}, ref::GitReference) where T<:GitObject ensure_initialized() obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_reference_peel, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cint), obj_ptr_ptr, ref.ptr, Consts.OBJECT(T)) return T(ref.owner, obj_ptr_ptr[]) end peel(ref::GitReference) = peel(GitObject, ref) """ LibGit2.ref_list(repo::GitRepo) -> Vector{String} Get a list of all reference names in the `repo` repository. """ function ref_list(repo::GitRepo) ensure_initialized() sa_ref = Ref(StrArrayStruct()) @check ccall((:git_reference_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo.ptr) res = convert(Vector{String}, sa_ref[]) free(sa_ref) res end """ LibGit2.create_branch(repo::GitRepo, bname::AbstractString, commit_obj::GitCommit; force::Bool=false) Create a new branch in the repository `repo` with name `bname`, which points to commit `commit_obj` (which has to be part of `repo`). If `force` is `true`, overwrite an existing branch named `bname` if it exists. If `force` is `false` and a branch already exists named `bname`, this function will throw an error. """ function create_branch(repo::GitRepo, bname::AbstractString, commit_obj::GitCommit; force::Bool=false) ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_branch_create, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Cint), ref_ptr_ptr, repo.ptr, bname, commit_obj.ptr, Cint(force)) return GitReference(repo, ref_ptr_ptr[]) end """ LibGit2.delete_branch(branch::GitReference) Delete the branch pointed to by `branch`. """ function delete_branch(branch::GitReference) ensure_initialized() @check ccall((:git_branch_delete, libgit2), Cint, (Ptr{Cvoid},), branch.ptr) end """ LibGit2.head!(repo::GitRepo, ref::GitReference) -> GitReference Set the HEAD of `repo` to the object pointed to by `ref`. """ function head!(repo::GitRepo, ref::GitReference) ensure_initialized() ref_name = name(ref) @check ccall((:git_repository_set_head, libgit2), Cint, (Ptr{Cvoid}, Cstring), repo.ptr, ref_name) return ref end """ lookup_branch(repo::GitRepo, branch_name::AbstractString, remote::Bool=false) -> Union{GitReference, Nothing} Determine if the branch specified by `branch_name` exists in the repository `repo`. If `remote` is `true`, `repo` is assumed to be a remote git repository. Otherwise, it is part of the local filesystem. Return either a `GitReference` to the requested branch if it exists, or [`nothing`](@ref) if not. """ function lookup_branch(repo::GitRepo, branch_name::AbstractString, remote::Bool=false) ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) branch_type = remote ? Consts.BRANCH_REMOTE : Consts.BRANCH_LOCAL err = ccall((:git_branch_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{UInt8}, Cint), ref_ptr_ptr, repo.ptr, branch_name, branch_type) if err != Int(Error.GIT_OK) if err == Int(Error.ENOTFOUND) return nothing end if ref_ptr_ptr[] != C_NULL close(GitReference(repo, ref_ptr_ptr[])) end throw(Error.GitError(err)) end return GitReference(repo, ref_ptr_ptr[]) end """ upstream(ref::GitReference) -> Union{GitReference, Nothing} Determine if the branch containing `ref` has a specified upstream branch. Return either a `GitReference` to the upstream branch if it exists, or [`nothing`](@ref) if the requested branch does not have an upstream counterpart. """ function upstream(ref::GitReference) isempty(ref) && return nothing ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) err = ccall((:git_branch_upstream, libgit2), Cint, (Ref{Ptr{Cvoid}}, Ptr{Cvoid},), ref_ptr_ptr, ref.ptr) if err != Int(Error.GIT_OK) if err == Int(Error.ENOTFOUND) return nothing end if ref_ptr_ptr[] != C_NULL close(GitReference(ref.owner, ref_ptr_ptr[])) end throw(Error.GitError(err)) end return GitReference(ref.owner, ref_ptr_ptr[]) end repository(ref::GitReference) = ref.owner function target!(ref::GitReference, new_oid::GitHash; msg::AbstractString="") ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_reference_set_target, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{GitHash}, Cstring), ref_ptr_ptr, ref.ptr, Ref(new_oid), isempty(msg) ? C_NULL : msg) return GitReference(ref.owner, ref_ptr_ptr[]) end function GitBranchIter(repo::GitRepo, flags::Cint=Cint(Consts.BRANCH_LOCAL)) ensure_initialized() bi_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_branch_iterator_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cint), bi_ptr, repo.ptr, flags) return GitBranchIter(repo, bi_ptr[]) end function Base.iterate(bi::GitBranchIter, state=nothing) ensure_initialized() ref_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) btype = Ref{Cint}() err = ccall((:git_branch_next, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cint}, Ptr{Cvoid}), ref_ptr_ptr, btype, bi.ptr) if err == Cint(Error.GIT_OK) return ((GitReference(bi.owner, ref_ptr_ptr[]), btype[]), nothing) elseif err == Cint(Error.ITEROVER) return nothing else throw(GitError(err)) end end Base.IteratorSize(::Type{GitBranchIter}) = Base.SizeUnknown() q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/commit.jl(������# This file is a part of Julia. License is MIT: https://julialang.org/license repository(c::GitCommit) = c.owner """ message(c::GitCommit, raw::Bool=false) Return the commit message describing the changes made in commit `c`. If `raw` is `false`, return a slightly "cleaned up" message (which has any leading newlines removed). If `raw` is `true`, the message is not stripped of any such newlines. """ function message(c::GitCommit, raw::Bool=false) ensure_initialized() GC.@preserve c begin local msg_ptr::Cstring msg_ptr = raw ? ccall((:git_commit_message_raw, libgit2), Cstring, (Ptr{Cvoid},), c.ptr) : ccall((:git_commit_message, libgit2), Cstring, (Ptr{Cvoid},), c.ptr) if msg_ptr == C_NULL return nothing end msg_str = unsafe_string(msg_ptr) end return msg_str end """ author(c::GitCommit) Return the `Signature` of the author of the commit `c`. The author is the person who made changes to the relevant file(s). See also [`committer`](@ref). """ function author(c::GitCommit) ensure_initialized() GC.@preserve c begin ptr = ccall((:git_commit_author, libgit2), Ptr{SignatureStruct}, (Ptr{Cvoid},), c.ptr) @assert ptr != C_NULL sig = Signature(ptr) end return sig end """ committer(c::GitCommit) Return the `Signature` of the committer of the commit `c`. The committer is the person who committed the changes originally authored by the [`author`](@ref), but need not be the same as the `author`, for example, if the `author` emailed a patch to a `committer` who committed it. """ function committer(c::GitCommit) ensure_initialized() GC.@preserve c begin ptr = ccall((:git_commit_committer, libgit2), Ptr{SignatureStruct}, (Ptr{Cvoid},), c.ptr) sig = Signature(ptr) end return sig end function Base.show(io::IO, c::GitCommit) authstr = sprint(show, author(c)) cmtrstr = sprint(show, committer(c)) print(io, "Git Commit:\nCommit Author: $authstr\nCommitter: $cmtrstr\nSHA: $(GitHash(c))\nMessage:\n$(message(c))") end function commit(repo::GitRepo, refname::AbstractString, msg::AbstractString, author::GitSignature, committer::GitSignature, tree::GitTree, parents::GitCommit...) ensure_initialized() commit_id_ptr = Ref(GitHash()) nparents = length(parents) parentptrs = Ptr{Cvoid}[c.ptr for c in parents] @check ccall((:git_commit_create, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}, Ptr{UInt8}, Ptr{SignatureStruct}, Ptr{SignatureStruct}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Cvoid}, Csize_t, Ptr{Ptr{Cvoid}}), commit_id_ptr, repo.ptr, isempty(refname) ? C_NULL : refname, author.ptr, committer.ptr, C_NULL, msg, tree.ptr, nparents, nparents > 0 ? parentptrs : C_NULL) return commit_id_ptr[] end """ commit(repo::GitRepo, msg::AbstractString; kwargs...) -> GitHash Wrapper around [`git_commit_create`](https://libgit2.org/libgit2/#HEAD/group/commit/git_commit_create). Create a commit in the repository `repo`. `msg` is the commit message. Return the OID of the new commit. The keyword arguments are: * `refname::AbstractString=Consts.HEAD_FILE`: if not NULL, the name of the reference to update to point to the new commit. For example, `"HEAD"` will update the HEAD of the current branch. If the reference does not yet exist, it will be created. * `author::Signature = Signature(repo)` is a `Signature` containing information about the person who authored the commit. * `committer::Signature = Signature(repo)` is a `Signature` containing information about the person who committed the commit to the repository. Not necessarily the same as `author`, for instance if `author` emailed a patch to `committer` who committed it. * `tree_id::GitHash = GitHash()` is a git tree to use to create the commit, showing its ancestry and relationship with any other history. `tree` must belong to `repo`. * `parent_ids::Vector{GitHash}=GitHash[]` is a list of commits by [`GitHash`](@ref) to use as parent commits for the new one, and may be empty. A commit might have multiple parents if it is a merge commit, for example. """ function commit(repo::GitRepo, msg::AbstractString; refname::AbstractString=Consts.HEAD_FILE, author::Signature = Signature(repo), committer::Signature = Signature(repo), tree_id::GitHash = GitHash(), parent_ids::Vector{GitHash}=GitHash[]) # Retrieve tree identifier if iszero(tree_id) tree_id = with(GitIndex, repo) do idx; write_tree!(idx) end end # Retrieve parents from HEAD if isempty(parent_ids) try # if throws then HEAD not found -> empty repo Base.push!(parent_ids, GitHash(repo, refname)) catch end end # return commit id commit_id = GitHash() # get necessary objects tree = GitTree(repo, tree_id) auth_sig = convert(GitSignature, author) comm_sig = convert(GitSignature, committer) parents = GitCommit[] try for id in parent_ids Base.push!(parents, GitCommit(repo, id)) end commit_id = commit(repo, refname, msg, auth_sig, comm_sig, tree, parents...) finally for parent in parents close(parent) end close(tree) close(auth_sig) close(comm_sig) end return commit_id end u���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/repository.jlชG������# This file is a part of Julia. License is MIT: https://julialang.org/license """ LibGit2.GitRepo(path::AbstractString) Open a git repository at `path`. """ function GitRepo(path::AbstractString) ensure_initialized() repo_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_open, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring), repo_ptr_ptr, path) return GitRepo(repo_ptr_ptr[]) end """ LibGit2.GitRepoExt(path::AbstractString, flags::Cuint = Cuint(Consts.REPOSITORY_OPEN_DEFAULT)) Open a git repository at `path` with extended controls (for instance, if the current user must be a member of a special access group to read `path`). """ function GitRepoExt(path::AbstractString, flags::Cuint = Cuint(Consts.REPOSITORY_OPEN_DEFAULT)) ensure_initialized() separator = @static Sys.iswindows() ? ";" : ":" repo_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_open_ext, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cuint, Cstring), repo_ptr_ptr, path, flags, separator) return GitRepo(repo_ptr_ptr[]) end function cleanup(r::GitRepo) if r.ptr != C_NULL ensure_initialized() @check ccall((:git_repository__cleanup, libgit2), Cint, (Ptr{Cvoid},), r.ptr) end end """ LibGit2.init(path::AbstractString, bare::Bool=false) -> GitRepo Open a new git repository at `path`. If `bare` is `false`, the working tree will be created in `path/.git`. If `bare` is `true`, no working directory will be created. """ function init(path::AbstractString, bare::Bool=false) ensure_initialized() repo_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_init, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cuint), repo_ptr_ptr, path, bare) return GitRepo(repo_ptr_ptr[]) end """ LibGit2.head_oid(repo::GitRepo) -> GitHash Lookup the object id of the current HEAD of git repository `repo`. """ function head_oid(repo::GitRepo) head_ref = head(repo) try return GitHash(head_ref) finally close(head_ref) end end """ LibGit2.headname(repo::GitRepo) Lookup the name of the current HEAD of git repository `repo`. If `repo` is currently detached, return the name of the HEAD it's detached from. """ function headname(repo::GitRepo) with(head(repo)) do href if isattached(repo) shortname(href) else "(detached from $(string(GitHash(href))[1:7]))" end end end """ isbare(repo::GitRepo) -> Bool Determine if `repo` is bare. Suppose the top level directory of `repo` is `DIR`. A non-bare repository is one in which the git directory (see [`gitdir`](@ref)) is `DIR/.git`, and the working tree can be checked out. A bare repository is one in which all of git's administrative files are simply in `DIR`, rather than "hidden" in the `.git` subdirectory. This means that there is nowhere to check out the working tree, and no tracking information for remote branches or configurations is present. """ function isbare(repo::GitRepo) ensure_initialized() @assert repo.ptr != C_NULL return ccall((:git_repository_is_bare, libgit2), Cint, (Ptr{Cvoid},), repo.ptr) == 1 end """ isattached(repo::GitRepo) -> Bool Determine if `repo` is detached - that is, whether its HEAD points to a commit (detached) or whether HEAD points to a branch tip (attached). """ function isattached(repo::GitRepo) ensure_initialized() @assert repo.ptr != C_NULL ccall((:git_repository_head_detached, libgit2), Cint, (Ptr{Cvoid},), repo.ptr) != 1 end @doc """ GitObject(repo::GitRepo, hash::AbstractGitHash) GitObject(repo::GitRepo, spec::AbstractString) Return the specified object ([`GitCommit`](@ref), [`GitBlob`](@ref), [`GitTree`](@ref) or [`GitTag`](@ref)) from `repo` specified by `hash`/`spec`. - `hash` is a full (`GitHash`) or partial (`GitShortHash`) hash. - `spec` is a textual specification: see [the git docs](https://git-scm.com/docs/git-rev-parse.html#_specifying_revisions) for a full list. """ GitObject for T in (:GitCommit, :GitBlob, :GitTree, :GitTag) @eval @doc $""" $T(repo::GitRepo, hash::AbstractGitHash) $T(repo::GitRepo, spec::AbstractString) Return a `$T` object from `repo` specified by `hash`/`spec`. - `hash` is a full (`GitHash`) or partial (`GitShortHash`) hash. - `spec` is a textual specification: see [the git docs](https://git-scm.com/docs/git-rev-parse.html#_specifying_revisions) for a full list. """ $T end function (::Type{T})(repo::GitRepo, spec::AbstractString) where T<:GitObject ensure_initialized() obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @assert repo.ptr != C_NULL @check ccall((:git_revparse_single, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), obj_ptr_ptr, repo.ptr, spec) # check object is of correct type if T != GitObject && T != GitUnknownObject t = Consts.OBJECT(obj_ptr_ptr[]) t == Consts.OBJECT(T) || throw(GitError(Error.Object, Error.ERROR, "Expected object of type $T, received object of type $(objtype(t))")) end return T(repo, obj_ptr_ptr[]) end function (::Type{T})(repo::GitRepo, oid::GitHash) where T<:GitObject ensure_initialized() oid_ptr = Ref(oid) obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @assert repo.ptr != C_NULL @check ccall((:git_object_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{GitHash}, Consts.OBJECT), obj_ptr_ptr, repo.ptr, oid_ptr, Consts.OBJECT(T)) return T(repo, obj_ptr_ptr[]) end function (::Type{T})(repo::GitRepo, oid::GitShortHash) where T<:GitObject ensure_initialized() oid_ptr = Ref(oid.hash) obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @assert repo.ptr != C_NULL @check ccall((:git_object_lookup_prefix, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{GitHash}, Csize_t, Consts.OBJECT), obj_ptr_ptr, repo.ptr, oid_ptr, oid.len, Consts.OBJECT(T)) return T(repo, obj_ptr_ptr[]) end # TODO: deprecate this function revparseid(repo::GitRepo, spec) = GitHash(GitUnknownObject(repo, spec)) """ LibGit2.gitdir(repo::GitRepo) Return the location of the "git" files of `repo`: - for normal repositories, this is the location of the `.git` folder. - for bare repositories, this is the location of the repository itself. See also [`workdir`](@ref), [`path`](@ref). """ function gitdir(repo::GitRepo) ensure_initialized() @assert repo.ptr != C_NULL return unsafe_string(ccall((:git_repository_path, libgit2), Cstring, (Ptr{Cvoid},), repo.ptr)) end """ LibGit2.workdir(repo::GitRepo) Return the location of the working directory of `repo`. This will throw an error for bare repositories. !!! note This will typically be the parent directory of `gitdir(repo)`, but can be different in some cases: e.g. if either the `core.worktree` configuration variable or the `GIT_WORK_TREE` environment variable is set. See also [`gitdir`](@ref), [`path`](@ref). """ function workdir(repo::GitRepo) ensure_initialized() @assert repo.ptr != C_NULL sptr = ccall((:git_repository_workdir, libgit2), Cstring, (Ptr{Cvoid},), repo.ptr) sptr == C_NULL && throw(GitError(Error.Object, Error.ERROR, "No working directory found.")) return unsafe_string(sptr) end """ LibGit2.path(repo::GitRepo) Return the base file path of the repository `repo`. - for normal repositories, this will typically be the parent directory of the ".git" directory (note: this may be different than the working directory, see `workdir` for more details). - for bare repositories, this is the location of the "git" files. See also [`gitdir`](@ref), [`workdir`](@ref). """ function path(repo::GitRepo) d = gitdir(repo) if isdirpath(d) d = dirname(d) # strip trailing separator end if isbare(repo) return d else parent, base = splitdir(d) return base == ".git" ? parent : d end end """ peel([T,] obj::GitObject) Recursively peel `obj` until an object of type `T` is obtained. If no `T` is provided, then `obj` will be peeled until the type changes. - A `GitTag` will be peeled to the object it references. - A `GitCommit` will be peeled to a `GitTree`. """ function peel(::Type{T}, obj::GitObject) where T<:GitObject ensure_initialized() new_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_object_peel, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cint), new_ptr_ptr, obj.ptr, Consts.OBJECT(T)) return T(obj.owner, new_ptr_ptr[]) end peel(obj::GitObject) = peel(GitObject, obj) """ LibGit2.GitDescribeResult(committish::GitObject; kwarg...) Produce a `GitDescribeResult` of the `committish` `GitObject`, which contains detailed information about it based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` A git description of a `committish` object looks for the tag (by default, annotated, although a search of all tags can be performed) which can be reached from `committish` which is most recent. If the tag is pointing to `committish`, then only the tag is included in the description. Otherwise, a suffix is included which contains the number of commits between `committish` and the most recent tag. If there is no such tag, the default behavior is for the description to fail, although this can be changed through `options`. Equivalent to `git describe <committish>`. See [`DescribeOptions`](@ref) for more information. """ function GitDescribeResult(committish::GitObject; options::DescribeOptions=DescribeOptions()) ensure_initialized() result_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_describe_commit, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{DescribeOptions}), result_ptr_ptr, committish.ptr, Ref(options)) return GitDescribeResult(committish.owner, result_ptr_ptr[]) end """ LibGit2.GitDescribeResult(repo::GitRepo; kwarg...) Produce a `GitDescribeResult` of the repository `repo`'s working directory. The `GitDescribeResult` contains detailed information about the workdir based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` In this case, the description is run on HEAD, producing the most recent tag which is an ancestor of HEAD. Afterwards, a status check on the [`workdir`](@ref) is performed and if the `workdir` is dirty (see [`isdirty`](@ref)) the description is also considered dirty. Equivalent to `git describe`. See [`DescribeOptions`](@ref) for more information. """ function GitDescribeResult(repo::GitRepo; options::DescribeOptions=DescribeOptions()) ensure_initialized() result_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @assert repo.ptr != C_NULL @check ccall((:git_describe_workdir, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{DescribeOptions}), result_ptr_ptr, repo.ptr, Ref(options)) return GitDescribeResult(repo, result_ptr_ptr[]) end """ LibGit2.format(result::GitDescribeResult; kwarg...) -> String Produce a formatted string based on a `GitDescribeResult`. Formatting options are controlled by the keyword argument: * `options::DescribeFormatOptions=DescribeFormatOptions()` """ function format(result::GitDescribeResult; options::DescribeFormatOptions=DescribeFormatOptions()) ensure_initialized() buf_ref = Ref(Buffer()) @check ccall((:git_describe_format, libgit2), Cint, (Ptr{Buffer}, Ptr{Cvoid}, Ptr{DescribeFormatOptions}), buf_ref, result.ptr, Ref(options)) buf = buf_ref[] str = unsafe_string(buf.ptr, buf.size) free(buf_ref) return str end function Base.show(io::IO, result::GitDescribeResult) fmt_desc = format(result) println(io, "GitDescribeResult:") println(io, fmt_desc) end """ checkout_tree(repo::GitRepo, obj::GitObject; options::CheckoutOptions = CheckoutOptions()) Update the working tree and index of `repo` to match the tree pointed to by `obj`. `obj` can be a commit, a tag, or a tree. `options` controls how the checkout will be performed. See [`CheckoutOptions`](@ref) for more information. """ function checkout_tree(repo::GitRepo, obj::GitObject; options::CheckoutOptions = CheckoutOptions()) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_checkout_tree, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{CheckoutOptions}), repo.ptr, obj.ptr, Ref(options)) end """ checkout_index(repo::GitRepo, idx::Union{GitIndex, Nothing} = nothing; options::CheckoutOptions = CheckoutOptions()) Update the working tree of `repo` to match the index `idx`. If `idx` is `nothing`, the index of `repo` will be used. `options` controls how the checkout will be performed. See [`CheckoutOptions`](@ref) for more information. """ function checkout_index(repo::GitRepo, idx::Union{GitIndex, Nothing} = nothing; options::CheckoutOptions = CheckoutOptions()) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_checkout_index, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{CheckoutOptions}), repo.ptr, idx === nothing ? C_NULL : idx.ptr, Ref(options)) end """ checkout_head(repo::GitRepo; options::CheckoutOptions = CheckoutOptions()) Update the index and working tree of `repo` to match the commit pointed to by HEAD. `options` controls how the checkout will be performed. See [`CheckoutOptions`](@ref) for more information. !!! warning *Do not* use this function to switch branches! Doing so will cause checkout conflicts. """ function checkout_head(repo::GitRepo; options::CheckoutOptions = CheckoutOptions()) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_checkout_head, libgit2), Cint, (Ptr{Cvoid}, Ptr{CheckoutOptions}), repo.ptr, Ref(options)) end """ LibGit2.cherrypick(repo::GitRepo, commit::GitCommit; options::CherrypickOptions = CherrypickOptions()) Cherrypick the commit `commit` and apply the changes in it to the current state of `repo`. The keyword argument `options` sets checkout and merge options for the cherrypick. !!! note `cherrypick` will *apply* the changes in `commit` but not *commit* them, so `repo` will be left in a dirty state. If you want to also commit the changes in `commit` you must call [`commit`](@ref) yourself. """ function cherrypick(repo::GitRepo, commit::GitCommit; options::CherrypickOptions = CherrypickOptions()) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_cherrypick, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{CherrypickOptions}), repo.ptr, commit.ptr, Ref(options)) end """Updates some entries, determined by the `pathspecs`, in the index from the target commit tree.""" function reset!(repo::GitRepo, obj::Union{GitObject, Nothing}, pathspecs::AbstractString...) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_reset_default, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{StrArrayStruct}), repo.ptr, obj === nothing ? C_NULL : obj.ptr, collect(pathspecs)) return head_oid(repo) end """Sets the current head to the specified commit oid and optionally resets the index and working tree to match.""" function reset!(repo::GitRepo, obj::GitObject, mode::Cint; checkout_opts::CheckoutOptions = CheckoutOptions()) ensure_initialized() @assert repo.ptr != C_NULL @check ccall((:git_reset, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint, Ptr{CheckoutOptions}), repo.ptr, obj.ptr, mode, Ref(checkout_opts)) return head_oid(repo) end """ clone(repo_url::AbstractString, repo_path::AbstractString, clone_opts::CloneOptions) Clone the remote repository at `repo_url` (which can be a remote URL or a path on the local filesystem) to `repo_path` (which must be a path on the local filesystem). Options for the clone, such as whether to perform a bare clone or not, are set by [`CloneOptions`](@ref). # Examples ```julia repo_url = "https://github.com/JuliaLang/Example.jl" repo = LibGit2.clone(repo_url, "/home/me/projects/Example") ``` """ function clone(repo_url::AbstractString, repo_path::AbstractString, clone_opts::CloneOptions) ensure_initialized() clone_opts_ref = Ref(clone_opts) repo_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_clone, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cstring, Ref{CloneOptions}), repo_ptr_ptr, repo_url, repo_path, clone_opts_ref) return GitRepo(repo_ptr_ptr[]) end """ fetchheads(repo::GitRepo) -> Vector{FetchHead} Return the list of all the fetch heads for `repo`, each represented as a [`FetchHead`](@ref), including their names, URLs, and merge statuses. # Examples ```julia-repl julia> fetch_heads = LibGit2.fetchheads(repo); julia> fetch_heads[1].name "refs/heads/master" julia> fetch_heads[1].ismerge true julia> fetch_heads[2].name "refs/heads/test_branch" julia> fetch_heads[2].ismerge false ``` """ function fetchheads(repo::GitRepo) ensure_initialized() fh = FetchHead[] ffcb = fetchhead_foreach_cb() @assert repo.ptr != C_NULL @check ccall((:git_repository_fetchhead_foreach, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Any), repo.ptr, ffcb, fh) return fh end """ LibGit2.remotes(repo::GitRepo) Return a vector of the names of the remotes of `repo`. """ function remotes(repo::GitRepo) ensure_initialized() sa_ref = Ref(StrArrayStruct()) @assert repo.ptr != C_NULL @check ccall((:git_remote_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo.ptr) res = convert(Vector{String}, sa_ref[]) free(sa_ref) return res end function Base.show(io::IO, repo::GitRepo) print(io, "LibGit2.GitRepo(") if repo.ptr == C_NULL print(io, "<closed>") else show(io, path(repo)) end print(io, ")") end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/config.jlI!������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitConfig(path::AbstractString, level::Consts.GIT_CONFIG=Consts.CONFIG_LEVEL_APP, force::Bool=false) Create a new `GitConfig` by loading configuration information from the file at `path`. See [`addfile`](@ref) for more information about the `level`, `repo` and `force` options. """ function GitConfig(path::AbstractString, level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_APP, repo::Union{GitRepo, Nothing}=nothing, force::Bool=false) ensure_initialized() # create new config object cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_new, libgit2), Cint, (Ptr{Ptr{Cvoid}},), cfg_ptr_ptr) cfg = GitConfig(cfg_ptr_ptr[]) try addfile(cfg, path, level, repo, force) catch close(cfg) rethrow() end return cfg end """ GitConfig(repo::GitRepo) Get the stored configuration for the git repository `repo`. If `repo` does not have a specific configuration file set, the default git configuration will be used. """ function GitConfig(repo::GitRepo) ensure_initialized() cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_config, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), cfg_ptr_ptr, repo.ptr) return GitConfig(repo, cfg_ptr_ptr[]) end """ GitConfig(level::Consts.GIT_CONFIG=Consts.CONFIG_LEVEL_DEFAULT) Get the default git configuration by loading the global and system configuration files into a prioritized configuration. This can be used to access default configuration options outside a specific git repository. """ function GitConfig(level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_DEFAULT) ensure_initialized() cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_open_default, libgit2), Cint, (Ptr{Ptr{Cvoid}},), cfg_ptr_ptr) cfg = GitConfig(cfg_ptr_ptr[]) if level != Consts.CONFIG_LEVEL_DEFAULT glb_cfg_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) tmpcfg = cfg try @check ccall((:git_config_open_level, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cint), glb_cfg_ptr_ptr, cfg.ptr, Cint(level)) cfg = GitConfig(glb_cfg_ptr_ptr[]) finally close(tmpcfg) end end return cfg end """ addfile(cfg::GitConfig, path::AbstractString, level::Consts.GIT_CONFIG=Consts.CONFIG_LEVEL_APP, repo::Union{GitRepo, Nothing} = nothing, force::Bool=false) Add an existing git configuration file located at `path` to the current `GitConfig` `cfg`. If the file does not exist, it will be created. - `level` sets the git configuration priority level and is determined by [`Consts.GIT_CONFIG`](@ref). - `repo` is an optional repository to allow parsing of conditional includes. - If `force` is `false` and a configuration for the given priority level already exists, `addfile` will error. If `force` is `true`, the existing configuration will be replaced by the one in the file at `path`. """ function addfile(cfg::GitConfig, path::AbstractString, level::Consts.GIT_CONFIG = Consts.CONFIG_LEVEL_APP, repo::Union{GitRepo, Nothing} = nothing, force::Bool=false) ensure_initialized() @static if LibGit2.VERSION >= v"0.27.0" @check ccall((:git_config_add_file_ondisk, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cint, Ptr{Cvoid}, Cint), cfg.ptr, path, Cint(level), isa(repo, GitRepo) ? repo.ptr : C_NULL, Cint(force)) else repo === nothing || error("repo argument is not supported in this version of LibGit2") @check ccall((:git_config_add_file_ondisk, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cint, Cint), cfg.ptr, path, Cint(level), Cint(force)) end end function get(::Type{<:AbstractString}, c::GitConfig, name::AbstractString) ensure_initialized() buf_ref = Ref(Buffer()) @check ccall((:git_config_get_string_buf, libgit2), Cint, (Ptr{Buffer}, Ptr{Cvoid}, Cstring), buf_ref, c.ptr, name) buf = buf_ref[] str = unsafe_string(buf.ptr, buf.size) free(buf_ref) str end function get(::Type{Bool}, c::GitConfig, name::AbstractString) ensure_initialized() val_ptr = Ref(Cint(0)) @check ccall((:git_config_get_bool, libgit2), Cint, (Ptr{Cint}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name) return Bool(val_ptr[]) end function get(::Type{Int32}, c::GitConfig, name::AbstractString) ensure_initialized() val_ptr = Ref(Cint(0)) @check ccall((:git_config_get_int32, libgit2), Cint, (Ptr{Cint}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name) return val_ptr[] end function get(::Type{Int64}, c::GitConfig, name::AbstractString) ensure_initialized() val_ptr = Ref(Cintmax_t(0)) @check ccall((:git_config_get_int64, libgit2), Cint, (Ptr{Cintmax_t}, Ptr{Cvoid}, Cstring), val_ptr, c.ptr, name) return val_ptr[] end function get(c::GitConfig, name::AbstractString, default::T) where T res = default try res = get(T,c,name); catch; end return res end function getconfig(r::GitRepo, name::AbstractString, default) with(GitConfig, r) do cfg get(cfg, name, default) end end function getconfig(rname::AbstractString, name::AbstractString, default) with(GitRepo, rname) do r with(GitConfig, r) do cfg get(cfg, name, default) end end end function getconfig(name::AbstractString, default) with(GitConfig) do cfg get(cfg, name, default) end end function set!(c::GitConfig, name::AbstractString, value::AbstractString) ensure_initialized() @check ccall((:git_config_set_string, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cstring), c.ptr, name, value) end function set!(c::GitConfig, name::AbstractString, value::Bool) ensure_initialized() bval = Int32(value) @check ccall((:git_config_set_bool, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cint), c.ptr, name, bval) end function set!(c::GitConfig, name::AbstractString, value::Int32) ensure_initialized() @check ccall((:git_config_set_int32, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cint), c.ptr, name, value) end function set!(c::GitConfig, name::AbstractString, value::Int64) ensure_initialized() @check ccall((:git_config_set_int64, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cintmax_t), c.ptr, name, value) end function GitConfigIter(cfg::GitConfig) ensure_initialized() ci_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_iterator_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), ci_ptr, cfg.ptr) return GitConfigIter(ci_ptr[]) end function GitConfigIter(cfg::GitConfig, name::AbstractString) ensure_initialized() ci_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_multivar_iterator_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring), ci_ptr, cfg.ptr, name, C_NULL) return GitConfigIter(ci_ptr[]) end function GitConfigIter(cfg::GitConfig, name::AbstractString, value::Regex) ensure_initialized() ci_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_multivar_iterator_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring), ci_ptr, cfg.ptr, name, value.pattern) return GitConfigIter(ci_ptr[]) end function GitConfigIter(cfg::GitConfig, name::Regex) ensure_initialized() ci_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_config_iterator_glob_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), ci_ptr, cfg.ptr, name.pattern) return GitConfigIter(ci_ptr[]) end function Base.iterate(ci::GitConfigIter, state=nothing) ensure_initialized() entry_ptr_ptr = Ref{Ptr{ConfigEntry}}(C_NULL) err = ccall((:git_config_next, libgit2), Cint, (Ptr{Ptr{ConfigEntry}}, Ptr{Cvoid}), entry_ptr_ptr, ci.ptr) if err == Cint(Error.GIT_OK) return (unsafe_load(entry_ptr_ptr[]), nothing) elseif err == Cint(Error.ITEROVER) return nothing else throw(GitError(err)) end end Base.IteratorSize(::Type{GitConfigIter}) = Base.SizeUnknown() q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/walker.jlŽ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitRevWalker(repo::GitRepo) A `GitRevWalker` *walks* through the *revisions* (i.e. commits) of a git repository `repo`. It is a collection of the commits in the repository, and supports iteration and calls to [`LibGit2.map`](@ref) and [`LibGit2.count`](@ref) (for instance, `LibGit2.count` could be used to determine what percentage of commits in a repository were made by a certain author). ```julia cnt = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker LibGit2.count((oid,repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) end ``` Here, `LibGit2.count` finds the number of commits along the walk with a certain `GitHash`. Since the `GitHash` is unique to a commit, `cnt` will be `1`. """ function GitRevWalker(repo::GitRepo) ensure_initialized() w_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_revwalk_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), w_ptr, repo.ptr) return GitRevWalker(repo, w_ptr[]) end function Base.iterate(w::GitRevWalker, state=nothing) ensure_initialized() id_ptr = Ref(GitHash()) err = ccall((:git_revwalk_next, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}), id_ptr, w.ptr) if err == Cint(Error.GIT_OK) return (id_ptr[], nothing) elseif err == Cint(Error.ITEROVER) return nothing else throw(GitError(err)) end end Base.IteratorSize(::Type{GitRevWalker}) = Base.SizeUnknown() """ LibGit2.push_head!(w::GitRevWalker) Push the HEAD commit and its ancestors onto the [`GitRevWalker`](@ref) `w`. This ensures that HEAD and all its ancestor commits will be encountered during the walk. """ function push_head!(w::GitRevWalker) ensure_initialized() @check ccall((:git_revwalk_push_head, libgit2), Cint, (Ptr{Cvoid},), w.ptr) return w end """ LibGit2.push!(w::GitRevWalker, cid::GitHash) Start the [`GitRevWalker`](@ref) `walker` at commit `cid`. This function can be used to apply a function to all commits since a certain year, by passing the first commit of that year as `cid` and then passing the resulting `w` to [`LibGit2.map`](@ref). """ function push!(w::GitRevWalker, cid::GitHash) ensure_initialized() @check ccall((:git_revwalk_push, libgit2), Cint, (Ptr{Cvoid}, Ptr{GitHash}), w.ptr, Ref(cid)) return w end function push!(w::GitRevWalker, range::AbstractString) ensure_initialized() @check ccall((:git_revwalk_push_range, libgit2), Cint, (Ptr{Cvoid}, Ptr{UInt8}), w.ptr, range) return w end function Base.sort!(w::GitRevWalker; by::Cint = Consts.SORT_NONE, rev::Bool=false) ensure_initialized() rev && (by |= Consts.SORT_REVERSE) @check ccall((:git_revwalk_sorting, libgit2), Cint, (Ptr{Cvoid}, Cint), w.ptr, by) return w end repository(w::GitRevWalker) = w.owner """ LibGit2.map(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), range::AbstractString="", by::Cint=Consts.SORT_NONE, rev::Bool=false) Using the [`GitRevWalker`](@ref) `walker` to "walk" over every commit in the repository's history, apply `f` to each commit in the walk. The keyword arguments are: * `oid`: The [`GitHash`](@ref) of the commit to begin the walk from. The default is to use [`push_head!`](@ref) and therefore the HEAD commit and all its ancestors. * `range`: A range of `GitHash`s in the format `oid1..oid2`. `f` will be applied to all commits between the two. * `by`: The sorting method. The default is not to sort. Other options are to sort by topology (`LibGit2.Consts.SORT_TOPOLOGICAL`), to sort forwards in time (`LibGit2.Consts.SORT_TIME`, most ancient first) or to sort backwards in time (`LibGit2.Consts.SORT_REVERSE`, most recent first). * `rev`: Whether to reverse the sorted order (for instance, if topological sorting is used). # Examples ```julia oids = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker LibGit2.map((oid, repo)->string(oid), walker, by=LibGit2.Consts.SORT_TIME) end ``` Here, `LibGit2.map` visits each commit using the `GitRevWalker` and finds its `GitHash`. """ function map(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), range::AbstractString="", by::Cint = Consts.SORT_NONE, rev::Bool=false, count::Int=0) res = [] sort!(walker, by=by, rev=rev) if !iszero(oid) push!(walker, oid) elseif !isempty(range) push!(walker, range) else push_head!(walker) end repo = repository(walker) for val in (count == 0 ? walker : Iterators.take(walker, count)) Base.push!(res, f(val, repo)) end return res end """ LibGit2.count(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint=Consts.SORT_NONE, rev::Bool=false) Using the [`GitRevWalker`](@ref) `walker` to "walk" over every commit in the repository's history, find the number of commits which return `true` when `f` is applied to them. The keyword arguments are: * `oid`: The [`GitHash`](@ref) of the commit to begin the walk from. The default is to use [`push_head!`](@ref) and therefore the HEAD commit and all its ancestors. * `by`: The sorting method. The default is not to sort. Other options are to sort by topology (`LibGit2.Consts.SORT_TOPOLOGICAL`), to sort forwards in time (`LibGit2.Consts.SORT_TIME`, most ancient first) or to sort backwards in time (`LibGit2.Consts.SORT_REVERSE`, most recent first). * `rev`: Whether to reverse the sorted order (for instance, if topological sorting is used). # Examples ```julia cnt = LibGit2.with(LibGit2.GitRevWalker(repo)) do walker LibGit2.count((oid, repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) end ``` `LibGit2.count` finds the number of commits along the walk with a certain `GitHash` `commit_oid1`, starting the walk from that commit and moving forwards in time from it. Since the `GitHash` is unique to a commit, `cnt` will be `1`. """ function count(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint = Consts.SORT_NONE, rev::Bool=false) c = 0 sort!(walker, by=by, rev=rev) if !iszero(oid) push!(walker, oid) else push_head!(walker) end repo = repository(walker) for val in walker c += f(val, repo) == true end return c end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/remote.jlก4������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitRemote(repo::GitRepo, rmt_name::AbstractString, rmt_url::AbstractString) -> GitRemote Look up a remote git repository using its name and URL. Uses the default fetch refspec. # Examples ```julia repo = LibGit2.init(repo_path) remote = LibGit2.GitRemote(repo, "upstream", repo_url) ``` """ function GitRemote(repo::GitRepo, rmt_name::AbstractString, rmt_url::AbstractString) ensure_initialized() rmt_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_remote_create, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring), rmt_ptr_ptr, repo.ptr, rmt_name, rmt_url) return GitRemote(repo, rmt_ptr_ptr[]) end """ GitRemote(repo::GitRepo, rmt_name::AbstractString, rmt_url::AbstractString, fetch_spec::AbstractString) -> GitRemote Look up a remote git repository using the repository's name and URL, as well as specifications for how to fetch from the remote (e.g. which remote branch to fetch from). # Examples ```julia repo = LibGit2.init(repo_path) refspec = "+refs/heads/mybranch:refs/remotes/origin/mybranch" remote = LibGit2.GitRemote(repo, "upstream", repo_url, refspec) ``` """ function GitRemote(repo::GitRepo, rmt_name::AbstractString, rmt_url::AbstractString, fetch_spec::AbstractString) ensure_initialized() rmt_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_remote_create_with_fetchspec, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring, Cstring), rmt_ptr_ptr, repo.ptr, rmt_name, rmt_url, fetch_spec) return GitRemote(repo, rmt_ptr_ptr[]) end """ GitRemoteAnon(repo::GitRepo, url::AbstractString) -> GitRemote Look up a remote git repository using only its URL, not its name. # Examples ```julia repo = LibGit2.init(repo_path) remote = LibGit2.GitRemoteAnon(repo, repo_url) ``` """ function GitRemoteAnon(repo::GitRepo, url::AbstractString) ensure_initialized() rmt_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_remote_create_anonymous, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), rmt_ptr_ptr, repo.ptr, url) return GitRemote(repo, rmt_ptr_ptr[]) end """ lookup_remote(repo::GitRepo, remote_name::AbstractString) -> Union{GitRemote, Nothing} Determine if the `remote_name` specified exists within the `repo`. Return either a [`GitRemote`](@ref) to the remote name if it exists, or [`nothing`](@ref) if not. # Examples ```julia repo = LibGit2.GitRepo(path) remote_name = "test" LibGit2.lookup_remote(repo, remote_name) # will return nothing ``` """ function lookup_remote(repo::GitRepo, remote_name::AbstractString) ensure_initialized() rmt_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) err = ccall((:git_remote_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), rmt_ptr_ptr, repo.ptr, remote_name) if err == Int(Error.GIT_OK) return GitRemote(repo, rmt_ptr_ptr[]) elseif err == Int(Error.ENOTFOUND) return nothing else throw(Error.GitError(err)) end end function get(::Type{GitRemote}, repo::GitRepo, rmt_name::AbstractString) ensure_initialized() rmt_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_remote_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), rmt_ptr_ptr, repo.ptr, rmt_name) return GitRemote(repo, rmt_ptr_ptr[]) end """ url(rmt::GitRemote) Get the fetch URL of a remote git repository. # Examples ```julia-repl julia> repo_url = "https://github.com/JuliaLang/Example.jl"; julia> repo = LibGit2.init(mktempdir()); julia> remote = LibGit2.GitRemote(repo, "origin", repo_url); julia> LibGit2.url(remote) "https://github.com/JuliaLang/Example.jl" ``` """ function url(rmt::GitRemote) ensure_initialized() url_ptr = ccall((:git_remote_url, libgit2), Cstring, (Ptr{Cvoid},), rmt.ptr) url_ptr == C_NULL && return "" return unsafe_string(url_ptr) end """ push_url(rmt::GitRemote) Get the push URL of a remote git repository. # Examples ```julia-repl julia> repo_url = "https://github.com/JuliaLang/Example.jl"; julia> repo = LibGit2.init(mktempdir()); julia> LibGit2.set_remote_push_url(repo, "origin", repo_url); julia> LibGit2.push_url(LibGit2.get(LibGit2.GitRemote, repo, "origin")) "https://github.com/JuliaLang/Example.jl" ``` """ function push_url(rmt::GitRemote) ensure_initialized() url_ptr = ccall((:git_remote_pushurl, libgit2), Cstring, (Ptr{Cvoid},), rmt.ptr) url_ptr == C_NULL && return "" return unsafe_string(url_ptr) end """ name(rmt::GitRemote) Get the name of a remote repository, for instance `"origin"`. If the remote is anonymous (see [`GitRemoteAnon`](@ref)) the name will be an empty string `""`. # Examples ```julia-repl julia> repo_url = "https://github.com/JuliaLang/Example.jl"; julia> repo = LibGit2.clone(cache_repo, "test_directory"); julia> remote = LibGit2.GitRemote(repo, "origin", repo_url); julia> name(remote) "origin" ``` """ function name(rmt::GitRemote) ensure_initialized() name_ptr = ccall((:git_remote_name, libgit2), Cstring, (Ptr{Cvoid},), rmt.ptr) name_ptr == C_NULL && return "" return unsafe_string(name_ptr) end """ fetch_refspecs(rmt::GitRemote) -> Vector{String} Get the *fetch* refspecs for the specified `rmt`. These refspecs contain information about which branch(es) to fetch from. # Examples ```julia-repl julia> remote = LibGit2.get(LibGit2.GitRemote, repo, "upstream"); julia> LibGit2.add_fetch!(repo, remote, "upstream"); julia> LibGit2.fetch_refspecs(remote) String["+refs/heads/*:refs/remotes/upstream/*"] ``` """ function fetch_refspecs(rmt::GitRemote) ensure_initialized() sa_ref = Ref(StrArrayStruct()) @check ccall((:git_remote_get_fetch_refspecs, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, rmt.ptr) res = convert(Vector{String}, sa_ref[]) free(sa_ref) res end """ push_refspecs(rmt::GitRemote) -> Vector{String} Get the *push* refspecs for the specified `rmt`. These refspecs contain information about which branch(es) to push to. # Examples ```julia-repl julia> remote = LibGit2.get(LibGit2.GitRemote, repo, "upstream"); julia> LibGit2.add_push!(repo, remote, "refs/heads/master"); julia> close(remote); julia> remote = LibGit2.get(LibGit2.GitRemote, repo, "upstream"); julia> LibGit2.push_refspecs(remote) String["refs/heads/master"] ``` """ function push_refspecs(rmt::GitRemote) ensure_initialized() sa_ref = Ref(StrArrayStruct()) @check ccall((:git_remote_get_push_refspecs, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, rmt.ptr) res = convert(Vector{String}, sa_ref[]) free(sa_ref) res end """ add_fetch!(repo::GitRepo, rmt::GitRemote, fetch_spec::String) Add a *fetch* refspec for the specified `rmt`. This refspec will contain information about which branch(es) to fetch from. # Examples ```julia-repl julia> LibGit2.add_fetch!(repo, remote, "upstream"); julia> LibGit2.fetch_refspecs(remote) String["+refs/heads/*:refs/remotes/upstream/*"] ``` """ function add_fetch!(repo::GitRepo, rmt::GitRemote, fetch_spec::String) ensure_initialized() @check ccall((:git_remote_add_fetch, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cstring), repo.ptr, name(rmt), fetch_spec) end """ add_push!(repo::GitRepo, rmt::GitRemote, push_spec::String) Add a *push* refspec for the specified `rmt`. This refspec will contain information about which branch(es) to push to. # Examples ```julia-repl julia> LibGit2.add_push!(repo, remote, "refs/heads/master"); julia> remote = LibGit2.get(LibGit2.GitRemote, repo, branch); julia> LibGit2.push_refspecs(remote) String["refs/heads/master"] ``` !!! note You may need to [`close`](@ref) and reopen the `GitRemote` in question after updating its push refspecs in order for the change to take effect and for calls to [`push`](@ref) to work. """ function add_push!(repo::GitRepo, rmt::GitRemote, push_spec::String) ensure_initialized() @check ccall((:git_remote_add_push, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cstring), repo.ptr, name(rmt), push_spec) end """ fetch(rmt::GitRemote, refspecs; options::FetchOptions=FetchOptions(), msg="") Fetch from the specified `rmt` remote git repository, using `refspecs` to determine which remote branch(es) to fetch. The keyword arguments are: * `options`: determines the options for the fetch, e.g. whether to prune afterwards. See [`FetchOptions`](@ref) for more information. * `msg`: a message to insert into the reflogs. """ function fetch(rmt::GitRemote, refspecs::Vector{<:AbstractString}; options::FetchOptions = FetchOptions(), msg::AbstractString="") ensure_initialized() msg = "libgit2.fetch: $msg" @check ccall((:git_remote_fetch, libgit2), Cint, (Ptr{Cvoid}, Ptr{StrArrayStruct}, Ptr{FetchOptions}, Cstring), rmt.ptr, isempty(refspecs) ? C_NULL : refspecs, Ref(options), msg) end """ push(rmt::GitRemote, refspecs; force::Bool=false, options::PushOptions=PushOptions()) Push to the specified `rmt` remote git repository, using `refspecs` to determine which remote branch(es) to push to. The keyword arguments are: * `force`: if `true`, a force-push will occur, disregarding conflicts. * `options`: determines the options for the push, e.g. which proxy headers to use. See [`PushOptions`](@ref) for more information. !!! note You can add information about the push refspecs in two other ways: by setting an option in the repository's `GitConfig` (with `push.default` as the key) or by calling [`add_push!`](@ref). Otherwise you will need to explicitly specify a push refspec in the call to `push` for it to have any effect, like so: `LibGit2.push(repo, refspecs=["refs/heads/master"])`. """ function push(rmt::GitRemote, refspecs::Vector{<:AbstractString}; force::Bool = false, options::PushOptions = PushOptions()) ensure_initialized() @check ccall((:git_remote_push, libgit2), Cint, (Ptr{Cvoid}, Ptr{StrArrayStruct}, Ptr{PushOptions}), rmt.ptr, isempty(refspecs) ? C_NULL : refspecs, Ref(options)) end """ remote_delete(repo::GitRepo, remote_name::AbstractString) -> Nothing Delete the `remote_name` from the git `repo`. """ function remote_delete(repo::GitRepo, remote_name::AbstractString) ensure_initialized() @check ccall((:git_remote_delete, libgit2), Cint, (Ptr{Cvoid}, Cstring), repo.ptr, remote_name) end Base.show(io::IO, rmt::GitRemote) = print(io, "GitRemote:\nRemote name: ", name(rmt), " url: ", url(rmt)) """ set_remote_fetch_url(repo::GitRepo, remote_name, url) set_remote_fetch_url(path::String, remote_name, url) Set the fetch `url` for the specified `remote_name` for the [`GitRepo`](@ref) or the git repository located at `path`. Typically git repos use `"origin"` as the remote name. """ function set_remote_fetch_url end function set_remote_fetch_url(repo::GitRepo, remote_name::AbstractString, url::AbstractString) ensure_initialized() @check ccall((:git_remote_set_url, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cstring), repo.ptr, remote_name, url) end function set_remote_fetch_url(path::AbstractString, remote_name::AbstractString, url::AbstractString) with(GitRepo, path) do repo set_remote_fetch_url(repo, remote_name, url) end end """ set_remote_push_url(repo::GitRepo, remote_name, url) set_remote_push_url(path::String, remote_name, url) Set the push `url` for the specified `remote_name` for the [`GitRepo`](@ref) or the git repository located at `path`. Typically git repos use `"origin"` as the remote name. """ function set_remote_push_url end function set_remote_push_url(repo::GitRepo, remote_name::AbstractString, url::AbstractString) ensure_initialized() @check ccall((:git_remote_set_pushurl, libgit2), Cint, (Ptr{Cvoid}, Cstring, Cstring), repo.ptr, remote_name, url) end function set_remote_push_url(path::AbstractString, remote_name::AbstractString, url::AbstractString) with(GitRepo, path) do repo set_remote_push_url(repo, remote_name, url) end end """ set_remote_url(repo::GitRepo, remote_name, url) set_remote_url(repo::String, remote_name, url) Set both the fetch and push `url` for `remote_name` for the [`GitRepo`](@ref) or the git repository located at `path`. Typically git repos use `"origin"` as the remote name. # Examples ```julia repo_path = joinpath(tempdir(), "Example") repo = LibGit2.init(repo_path) LibGit2.set_remote_url(repo, "upstream", "https://github.com/JuliaLang/Example.jl") LibGit2.set_remote_url(repo_path, "upstream2", "https://github.com/JuliaLang/Example2.jl") ``` """ function set_remote_url end function set_remote_url(repo::GitRepo, remote_name::AbstractString, url::AbstractString) set_remote_fetch_url(repo, remote_name, url) set_remote_push_url(repo, remote_name, url) end function set_remote_url(path::AbstractString, remote_name::AbstractString, url::AbstractString) with(GitRepo, path) do repo set_remote_url(repo, remote_name, url) end end s���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/strarray.jlZ������# This file is a part of Julia. License is MIT: https://julialang.org/license function Base.cconvert(::Type{Ptr{StrArrayStruct}}, x::Vector) str_ref = Base.cconvert(Ref{Cstring}, x) sa_ref = Ref(StrArrayStruct(Base.unsafe_convert(Ref{Cstring}, str_ref), length(x))) sa_ref, str_ref end function Base.unsafe_convert(::Type{Ptr{StrArrayStruct}}, rr::Tuple{Ref{StrArrayStruct}, Ref{Cstring}}) Base.unsafe_convert(Ptr{StrArrayStruct}, first(rr)) end function Base.convert(::Type{Vector{String}}, sa::StrArrayStruct) [unsafe_string(unsafe_load(sa.strings, i)) for i = 1:sa.count] end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/index.jl# ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitIndex(repo::GitRepo) Load the index file for the repository `repo`. """ function GitIndex(repo::GitRepo) ensure_initialized() idx_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_repository_index, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), idx_ptr_ptr, repo.ptr) return GitIndex(repo, idx_ptr_ptr[]) end """ read!(idx::GitIndex, force::Bool = false) -> GitIndex Update the contents of `idx` by reading changes made on disk. For example, `idx` might be updated if a file has been added to the repository since it was created. If `force` is `true`, any changes in memory (any changes in `idx` since its last [`write!`](@ref), or since its creation if no writes have occurred) are discarded. If `force` is `false`, the index data is only updated from disk if the data on disk has changed since the last time it was loaded into `idx`. """ function read!(idx::GitIndex, force::Bool = false) ensure_initialized() @check ccall((:git_index_read, libgit2), Cint, (Ptr{Cvoid}, Cint), idx.ptr, Cint(force)) return idx end """ write!(idx::GitIndex) -> GitIndex Write the state of index `idx` to disk using a file lock. """ function write!(idx::GitIndex) ensure_initialized() @check ccall((:git_index_write, libgit2), Cint, (Ptr{Cvoid},), idx.ptr) return idx end """ write_tree!(idx::GitIndex) -> GitHash Write the index `idx` as a [`GitTree`](@ref) on disk. Trees will be recursively created for each subtree in `idx`. The returned [`GitHash`](@ref) can be used to create a [`GitCommit`](@ref). `idx` must have a parent repository and this repository cannot be bare. `idx` must not contain any files with conflicts. """ function write_tree!(idx::GitIndex) ensure_initialized() oid_ptr = Ref(GitHash()) @check ccall((:git_index_write_tree, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}), oid_ptr, idx.ptr) return oid_ptr[] end function repository(idx::GitIndex) if idx.owner === nothing throw(GitError(Error.Index, Error.ENOTFOUND, "Index does not have an owning repository.")) else return idx.owner end end """ LibGit2.read_tree!(idx::GitIndex, tree::GitTree) LibGit2.read_tree!(idx::GitIndex, treehash::AbstractGitHash) Read the tree `tree` (or the tree pointed to by `treehash` in the repository owned by `idx`) into the index `idx`. The current index contents will be replaced. """ function read_tree!(idx::GitIndex, tree::GitTree) ensure_initialized() @check ccall((:git_index_read_tree, libgit2), Cint, (Ptr{Cvoid}, Ptr{Cvoid}), idx.ptr, tree.ptr) end read_tree!(idx::GitIndex, hash::AbstractGitHash) = read_tree!(idx, GitTree(repository(idx), hash)) """ add!(repo::GitRepo, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) add!(idx::GitIndex, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) Add all the files with paths specified by `files` to the index `idx` (or the index of the `repo`). If the file already exists, the index entry will be updated. If the file does not exist already, it will be newly added into the index. `files` may contain glob patterns which will be expanded and any matching files will be added (unless `INDEX_ADD_DISABLE_PATHSPEC_MATCH` is set, see below). If a file has been ignored (in `.gitignore` or in the config), it *will not* be added, *unless* it is already being tracked in the index, in which case it *will* be updated. The keyword argument `flags` is a set of bit-flags which control the behavior with respect to ignored files: * `Consts.INDEX_ADD_DEFAULT` - default, described above. * `Consts.INDEX_ADD_FORCE` - disregard the existing ignore rules and force addition of the file to the index even if it is already ignored. * `Consts.INDEX_ADD_CHECK_PATHSPEC` - cannot be used at the same time as `INDEX_ADD_FORCE`. Check that each file in `files` which exists on disk is not in the ignore list. If one of the files *is* ignored, the function will return `EINVALIDSPEC`. * `Consts.INDEX_ADD_DISABLE_PATHSPEC_MATCH` - turn off glob matching, and only add files to the index which exactly match the paths specified in `files`. """ function add!(idx::GitIndex, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) ensure_initialized() @check ccall((:git_index_add_all, libgit2), Cint, (Ptr{Cvoid}, Ptr{StrArrayStruct}, Cuint, Ptr{Cvoid}, Ptr{Cvoid}), idx.ptr, collect(files), flags, C_NULL, C_NULL) end """ update!(repo::GitRepo, files::AbstractString...) update!(idx::GitIndex, files::AbstractString...) Update all the files with paths specified by `files` in the index `idx` (or the index of the `repo`). Match the state of each file in the index with the current state on disk, removing it if it has been removed on disk, or updating its entry in the object database. """ function update!(idx::GitIndex, files::AbstractString...) ensure_initialized() @check ccall((:git_index_update_all, libgit2), Cint, (Ptr{Cvoid}, Ptr{StrArrayStruct}, Ptr{Cvoid}, Ptr{Cvoid}), idx.ptr, collect(files), C_NULL, C_NULL) end """ remove!(repo::GitRepo, files::AbstractString...) remove!(idx::GitIndex, files::AbstractString...) Remove all the files with paths specified by `files` in the index `idx` (or the index of the `repo`). """ function remove!(idx::GitIndex, files::AbstractString...) ensure_initialized() @check ccall((:git_index_remove_all, libgit2), Cint, (Ptr{Cvoid}, Ptr{StrArrayStruct}, Ptr{Cvoid}, Ptr{Cvoid}), idx.ptr, collect(files), C_NULL, C_NULL) end function add!(repo::GitRepo, files::AbstractString...; flags::Cuint = Consts.INDEX_ADD_DEFAULT) with(GitIndex, repo) do idx add!(idx, files..., flags = flags) write!(idx) end return end function update!(repo::GitRepo, files::AbstractString...) with(GitIndex, repo) do idx update!(idx, files...) write!(idx) end return end function remove!(repo::GitRepo, files::AbstractString...) with(GitIndex, repo) do idx remove!(idx, files...) write!(idx) end return end function read!(repo::GitRepo, force::Bool = false) with(GitIndex, repo) do idx read!(idx, force) end return end function count(idx::GitIndex) ensure_initialized() return ccall((:git_index_entrycount, libgit2), Csize_t, (Ptr{Cvoid},), idx.ptr) end function Base.getindex(idx::GitIndex, i::Integer) ensure_initialized() GC.@preserve idx begin ie_ptr = ccall((:git_index_get_byindex, libgit2), Ptr{IndexEntry}, (Ptr{Cvoid}, Csize_t), idx.ptr, i-1) ie_ptr == C_NULL && return nothing elem = unsafe_load(ie_ptr) end return elem end function Base.findall(path::String, idx::GitIndex) ensure_initialized() pos_ref = Ref{Csize_t}(0) ret = ccall((:git_index_find, libgit2), Cint, (Ref{Csize_t}, Ptr{Cvoid}, Cstring), pos_ref, idx.ptr, path) ret == Cint(Error.ENOTFOUND) && return nothing return pos_ref[]+1 end """ stage(ie::IndexEntry) -> Cint Get the stage number of `ie`. The stage number `0` represents the current state of the working tree, but other numbers can be used in the case of a merge conflict. In such a case, the various stage numbers on an `IndexEntry` describe which side(s) of the conflict the current state of the file belongs to. Stage `0` is the state before the attempted merge, stage `1` is the changes which have been made locally, stages `2` and larger are for changes from other branches (for instance, in the case of a multi-branch "octopus" merge, stages `2`, `3`, and `4` might be used). """ function stage(ie::IndexEntry) ensure_initialized() return ccall((:git_index_entry_stage, libgit2), Cint, (Ptr{IndexEntry},), Ref(ie)) end function Base.show(io::IO, idx::GitIndex) println(io, "GitIndex:\nRepository: ", repository(idx), "\nNumber of elements: ", count(idx)) end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/merge.jl ,������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitAnnotated(repo::GitRepo, commit_id::GitHash) GitAnnotated(repo::GitRepo, ref::GitReference) GitAnnotated(repo::GitRepo, fh::FetchHead) GitAnnotated(repo::GitRepo, committish::AbstractString) An annotated git commit carries with it information about how it was looked up and why, so that rebase or merge operations have more information about the context of the commit. Conflict files contain information about the source/target branches in the merge which are conflicting, for instance. An annotated commit can refer to the tip of a remote branch, for instance when a [`FetchHead`](@ref) is passed, or to a branch head described using `GitReference`. """ function GitAnnotated(repo::GitRepo, commit_id::GitHash) ensure_initialized() ann_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_annotated_commit_lookup, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{GitHash}), ann_ptr_ptr, repo.ptr, Ref(commit_id)) return GitAnnotated(repo, ann_ptr_ptr[]) end function GitAnnotated(repo::GitRepo, ref::GitReference) ensure_initialized() ann_ref_ref = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_annotated_commit_from_ref, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}), ann_ref_ref, repo.ptr, ref.ptr) return GitAnnotated(repo, ann_ref_ref[]) end function GitAnnotated(repo::GitRepo, fh::FetchHead) ensure_initialized() ann_ref_ref = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_annotated_commit_from_fetchhead, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring, Ptr{GitHash}), ann_ref_ref, repo.ptr, fh.name, fh.url, Ref(fh.oid)) return GitAnnotated(repo, ann_ref_ref[]) end function GitAnnotated(repo::GitRepo, committish::AbstractString) obj = GitObject(repo, committish) cmt = peel(GitCommit, obj) return GitAnnotated(repo, GitHash(cmt)) end function GitHash(ann::GitAnnotated) ensure_initialized() GC.@preserve ann begin oid = unsafe_load(ccall((:git_annotated_commit_id, libgit2), Ptr{GitHash}, (Ptr{Cvoid},), ann.ptr)) end return oid end """ merge_analysis(repo::GitRepo, anns::Vector{GitAnnotated}) -> analysis, preference Run analysis on the branches pointed to by the annotated branch tips `anns` and determine under what circumstances they can be merged. For instance, if `anns[1]` is simply an ancestor of `ann[2]`, then `merge_analysis` will report that a fast-forward merge is possible. Return two outputs, `analysis` and `preference`. `analysis` has several possible values: * `MERGE_ANALYSIS_NONE`: it is not possible to merge the elements of `anns`. * `MERGE_ANALYSIS_NORMAL`: a regular merge, when HEAD and the commits that the user wishes to merge have all diverged from a common ancestor. In this case the changes have to be resolved and conflicts may occur. * `MERGE_ANALYSIS_UP_TO_DATE`: all the input commits the user wishes to merge can be reached from HEAD, so no merge needs to be performed. * `MERGE_ANALYSIS_FASTFORWARD`: the input commit is a descendant of HEAD and so no merge needs to be performed - instead, the user can simply checkout the input commit(s). * `MERGE_ANALYSIS_UNBORN`: the HEAD of the repository refers to a commit which does not exist. It is not possible to merge, but it may be possible to checkout the input commits. `preference` also has several possible values: * `MERGE_PREFERENCE_NONE`: the user has no preference. * `MERGE_PREFERENCE_NO_FASTFORWARD`: do not allow any fast-forward merges. * `MERGE_PREFERENCE_FASTFORWARD_ONLY`: allow only fast-forward merges and no other type (which may introduce conflicts). `preference` can be controlled through the repository or global git configuration. """ function merge_analysis(repo::GitRepo, anns::Vector{GitAnnotated}) ensure_initialized() analysis = Ref{Cint}(0) preference = Ref{Cint}(0) anns_ref = Ref(Base.map(a->a.ptr, anns), 1) anns_size = Csize_t(length(anns)) @check ccall((:git_merge_analysis, libgit2), Cint, (Ptr{Cint}, Ptr{Cint}, Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, Csize_t), analysis, preference, repo.ptr, anns_ref, anns_size) return analysis[], preference[] end """ ffmerge!(repo::GitRepo, ann::GitAnnotated) Fastforward merge changes into current HEAD. This is only possible if the commit referred to by `ann` is descended from the current HEAD (e.g. if pulling changes from a remote branch which is simply ahead of the local branch tip). """ function ffmerge!(repo::GitRepo, ann::GitAnnotated) cmt = GitCommit(repo, GitHash(ann)) checkout_tree(repo, cmt) with(head(repo)) do head_ref cmt_oid = GitHash(cmt) msg = "libgit2.merge: fastforward $(string(cmt_oid)) into $(name(head_ref))" new_head_ref = if reftype(head_ref) == Consts.REF_OID target!(head_ref, cmt_oid, msg=msg) else GitReference(repo, cmt_oid, fullname(head_ref), msg=msg) end close(new_head_ref) end return true end # Merge changes into current head """ merge!(repo::GitRepo, anns::Vector{GitAnnotated}; kwargs...) -> Bool Merge changes from the annotated commits (captured as [`GitAnnotated`](@ref) objects) `anns` into the HEAD of the repository `repo`. The keyword arguments are: * `merge_opts::MergeOptions = MergeOptions()`: options for how to perform the merge, including whether fastforwarding is allowed. See [`MergeOptions`](@ref) for more information. * `checkout_opts::CheckoutOptions = CheckoutOptions()`: options for how to perform the checkout. See [`CheckoutOptions`](@ref) for more information. `anns` may refer to remote or local branch heads. Return `true` if the merge is successful, otherwise return `false` (for instance, if no merge is possible because the branches have no common ancestor). # Examples ```julia upst_ann = LibGit2.GitAnnotated(repo, "branch/a") # merge the branch in LibGit2.merge!(repo, [upst_ann]) ``` """ function merge!(repo::GitRepo, anns::Vector{GitAnnotated}; merge_opts::MergeOptions = MergeOptions(), checkout_opts::CheckoutOptions = CheckoutOptions()) ensure_initialized() anns_size = Csize_t(length(anns)) @check ccall((:git_merge, libgit2), Cint, (Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, Csize_t, Ptr{MergeOptions}, Ptr{CheckoutOptions}), repo.ptr, Base.map(x->x.ptr, anns), anns_size, Ref(merge_opts), Ref(checkout_opts)) @info "Review and commit merged changes" return true end # Internal implementation of merge. # Returns `true` if merge was successful, otherwise `false` """ merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; kwargs...) -> Bool Merge changes from the annotated commits (captured as [`GitAnnotated`](@ref) objects) `anns` into the HEAD of the repository `repo`. If `fastforward` is `true`, *only* a fastforward merge is allowed. In this case, if conflicts occur, the merge will fail. Otherwise, if `fastforward` is `false`, the merge may produce a conflict file which the user will need to resolve. The keyword arguments are: * `merge_opts::MergeOptions = MergeOptions()`: options for how to perform the merge, including whether fastforwarding is allowed. See [`MergeOptions`](@ref) for more information. * `checkout_opts::CheckoutOptions = CheckoutOptions()`: options for how to perform the checkout. See [`CheckoutOptions`](@ref) for more information. `anns` may refer to remote or local branch heads. Return `true` if the merge is successful, otherwise return `false` (for instance, if no merge is possible because the branches have no common ancestor). # Examples ```julia upst_ann_1 = LibGit2.GitAnnotated(repo, "branch/a") # merge the branch in, fastforward LibGit2.merge!(repo, [upst_ann_1], true) # merge conflicts! upst_ann_2 = LibGit2.GitAnnotated(repo, "branch/b") # merge the branch in, try to fastforward LibGit2.merge!(repo, [upst_ann_2], true) # will return false LibGit2.merge!(repo, [upst_ann_2], false) # will return true ``` """ function merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; merge_opts::MergeOptions = MergeOptions(), checkout_opts::CheckoutOptions = CheckoutOptions()) ma, mp = merge_analysis(repo, anns) if isset(ma, Cint(Consts.MERGE_ANALYSIS_UP_TO_DATE)) return true # no merge - everything is up to date end ffpref = if fastforward Consts.MERGE_PREFERENCE_FASTFORWARD_ONLY elseif isset(mp, Cint(Consts.MERGE_PREFERENCE_NONE)) Consts.MERGE_PREFERENCE_NONE elseif isset(mp, Cint(Consts.MERGE_PREFERENCE_NO_FASTFORWARD)) Consts.MERGE_PREFERENCE_NO_FASTFORWARD elseif isset(mp, Cint(Consts.MERGE_PREFERENCE_FASTFORWARD_ONLY)) Consts.MERGE_PREFERENCE_FASTFORWARD_ONLY else throw(ArgumentError("unknown merge preference: $(mp).")) end merge_result = if ffpref == Consts.MERGE_PREFERENCE_NONE if isset(ma, Cint(Consts.MERGE_ANALYSIS_FASTFORWARD)) if length(anns) > 1 @warn "Unable to perform Fast-Forward merge with mith multiple merge heads" false else ffmerge!(repo, anns[1]) end elseif isset(ma, Cint(Consts.MERGE_ANALYSIS_NORMAL)) merge!(repo, anns, merge_opts=merge_opts, checkout_opts=checkout_opts) end elseif ffpref == Consts.MERGE_PREFERENCE_FASTFORWARD_ONLY if isset(ma, Cint(Consts.MERGE_ANALYSIS_FASTFORWARD)) if length(anns) > 1 @warn "Unable to perform Fast-Forward merge with mith multiple merge heads" false else ffmerge!(repo, anns[1]) end else @warn "Cannot perform fast-forward merge" false end elseif ffpref == Consts.MERGE_PREFERENCE_NO_FASTFORWARD if isset(ma, Cint(Consts.MERGE_ANALYSIS_NORMAL)) merge!(repo, anns, merge_opts=merge_opts, checkout_opts=checkout_opts) end else throw(ArgumentError("unknown merge analysis result: $(ma)")) end return merge_result end """ merge_base(repo::GitRepo, one::AbstractString, two::AbstractString) -> GitHash Find a merge base (a common ancestor) between the commits `one` and `two`. `one` and `two` may both be in string form. Return the `GitHash` of the merge base. """ function merge_base(repo::GitRepo, one::AbstractString, two::AbstractString) ensure_initialized() oid1_ptr = Ref(GitHash(one)) oid2_ptr = Ref(GitHash(two)) moid_ptr = Ref(GitHash()) moid = try @check ccall((:git_merge_base, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}, Ptr{GitHash}, Ptr{GitHash}), moid_ptr, repo.ptr, oid1_ptr, oid2_ptr) moid_ptr[] catch e GitHash() end return moid end n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/tag.jlB ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ LibGit2.tag_list(repo::GitRepo) -> Vector{String} Get a list of all tags in the git repository `repo`. """ function tag_list(repo::GitRepo) ensure_initialized() sa_ref = Ref(StrArrayStruct()) @check ccall((:git_tag_list, libgit2), Cint, (Ptr{StrArrayStruct}, Ptr{Cvoid}), sa_ref, repo.ptr) res = convert(Vector{String}, sa_ref[]) free(sa_ref) res end """ LibGit2.tag_delete(repo::GitRepo, tag::AbstractString) Remove the git tag `tag` from the repository `repo`. """ function tag_delete(repo::GitRepo, tag::AbstractString) ensure_initialized() @check ccall((:git_tag_delete, libgit2), Cint, (Ptr{Cvoid}, Cstring), repo.ptr, tag) end """ LibGit2.tag_create(repo::GitRepo, tag::AbstractString, commit; kwargs...) Create a new git tag `tag` (e.g. `"v0.5"`) in the repository `repo`, at the commit `commit`. The keyword arguments are: * `msg::AbstractString=""`: the message for the tag. * `force::Bool=false`: if `true`, existing references will be overwritten. * `sig::Signature=Signature(repo)`: the tagger's signature. """ function tag_create(repo::GitRepo, tag::AbstractString, commit::Union{AbstractString,AbstractGitHash}; msg::AbstractString = "", force::Bool = false, sig::Signature = Signature(repo)) oid_ptr = Ref(GitHash()) with(GitCommit(repo, commit)) do commit_obj commit_obj === nothing && return oid_ptr[] # return empty oid with(convert(GitSignature, sig)) do git_sig ensure_initialized() @check ccall((:git_tag_create, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}, Ptr{SignatureStruct}, Cstring, Cint), oid_ptr, repo.ptr, tag, commit_obj.ptr, git_sig.ptr, msg, Cint(force)) end end return oid_ptr[] end """ LibGit2.name(tag::GitTag) The name of `tag` (e.g. `"v0.5"`). """ function name(tag::GitTag) ensure_initialized() GC.@preserve tag begin str_ptr = ccall((:git_tag_name, libgit2), Cstring, (Ptr{Cvoid},), tag.ptr) str_ptr == C_NULL && throw(Error.GitError(Error.ERROR)) str = unsafe_string(str_ptr) end return str end # should we return the actual object? i.e. git_tag_target? """ LibGit2.target(tag::GitTag) The `GitHash` of the target object of `tag`. """ function target(tag::GitTag) ensure_initialized() GC.@preserve tag begin oid_ptr = ccall((:git_tag_target_id, libgit2), Ptr{GitHash}, (Ptr{Cvoid},), tag.ptr) oid_ptr == C_NULL && throw(Error.GitError(Error.ERROR)) str = unsafe_load(oid_ptr) end return str end Base.show(io::IO, tag::GitTag) = print(io, "GitTag:\nTag name: $(name(tag)) target: $(target(tag))") o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/blob.jl~ ������# This file is a part of Julia. License is MIT: https://julialang.org/license function Base.length(blob::GitBlob) ensure_initialized() return ccall((:git_blob_rawsize, libgit2), Int64, (Ptr{Cvoid},), blob.ptr) end """ rawcontent(blob::GitBlob) -> Vector{UInt8} Fetch the *raw* contents of the [`GitBlob`](@ref) `blob`. This is an `Array` containing the contents of the blob, which may be binary or may be Unicode. If you write to this `Array` the blob on disk will not be updated. `rawcontent` will allow the user to load the raw binary data into the output `Array` and will not check to ensure it is valid Unicode, so errors may occur if the result is passed to functions which expect valid Unicode data. See also [`content`](@ref), which *will* throw an error if the content of the `blob` is binary and not valid Unicode. """ function rawcontent(blob::GitBlob) ensure_initialized() ptr = ccall((:git_blob_rawcontent, libgit2), Ptr{UInt8}, (Ptr{Cvoid},), blob.ptr) copy(unsafe_wrap(Array, ptr, (length(blob),), own = false)) end """ content(blob::GitBlob) -> String Fetch the contents of the [`GitBlob`](@ref) `blob`. If the `blob` contains binary data (which can be determined using [`isbinary`](@ref)), throw an error. Otherwise, return a `String` containing the contents of the `blob`. """ function content(blob::GitBlob) s = String(rawcontent(blob)) isvalid(s) || error("Blob does not contain valid UTF-8 data") return s end """ isbinary(blob::GitBlob) -> Bool Use a heuristic to guess if a file is binary: searching for NULL bytes and looking for a reasonable ratio of printable to non-printable characters among the first 8000 bytes. """ function isbinary(blob::GitBlob) ensure_initialized() bin_flag = ccall((:git_blob_is_binary, libgit2), Cint, (Ptr{Cvoid},), blob.ptr) return bin_flag == 1 end """ LibGit2.addblob!(repo::GitRepo, path::AbstractString) Read the file at `path` and adds it to the object database of `repo` as a loose blob. Return the [`GitHash`](@ref) of the resulting blob. # Examples ```julia hash_str = string(commit_oid) blob_file = joinpath(repo_path, ".git", "objects", hash_str[1:2], hash_str[3:end]) id = LibGit2.addblob!(repo, blob_file) ``` """ function addblob!(repo::GitRepo, path::AbstractString) ensure_initialized() id_ref = Ref{GitHash}() @check ccall((:git_blob_create_from_disk, libgit2), Cint, (Ptr{GitHash}, Ptr{Cvoid}, Cstring), id_ref, repo.ptr, path) return id_ref[] end function Base.show(io::IO, blob::GitBlob) if !isbinary(blob) conts = split(content(blob), "\n") showlen = max(length(conts), 3) println(io, "GitBlob:\nBlob id: ", GitHash(blob), "\nContents:") for i in 1:showlen println(io, conts[i]) end else println(io, "GitBlob:\nBlob id: ", GitHash(blob), "\nContents are binary.") end end o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/diff.jl„������# This file is a part of Julia. License is MIT: https://julialang.org/license # TODO: make this a general purpose solution function Base.cconvert(::Type{Ptr{DiffOptionsStruct}}, pathspecs::AbstractString) str_ref = Base.cconvert(Ref{Cstring}, [pathspecs]) sa = StrArrayStruct(Base.unsafe_convert(Ref{Cstring}, str_ref), 1) do_ref = Ref(DiffOptionsStruct(pathspec = sa)) do_ref, str_ref end function Base.unsafe_convert(::Type{Ptr{DiffOptionsStruct}}, rr::Tuple{Ref{DiffOptionsStruct}, Ref{Cstring}}) Base.unsafe_convert(Ptr{DiffOptionsStruct}, first(rr)) end """ diff_tree(repo::GitRepo, tree::GitTree, pathspecs::AbstractString=""; cached::Bool=false) Generate a [`GitDiff`](@ref) between `tree` (which will be used for the "old" side of the [`DiffDelta`](@ref)) and `repo` (which will be used for the "new" side). If `repo` is `cached`, calls [`git_diff_tree_to_index`](https://libgit2.org/libgit2/#HEAD/group/diff/git_diff_tree_to_index). The `cached` version is generally used to examine the diff for staged changes from one commit to the next. If `cached` is `false`, calls [`git_diff_tree_to_workdir_with_index`](https://libgit2.org/libgit2/#HEAD/group/diff/git_diff_tree_to_workdir_with_index). This compares the current working directory against the [`GitIndex`](@ref) and can, for example, be used to examine the changes in staged files before a commit. """ function diff_tree(repo::GitRepo, tree::GitTree, pathspecs::AbstractString=""; cached::Bool=false) ensure_initialized() diff_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) if cached @check ccall((:git_diff_tree_to_index, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{DiffOptionsStruct}), diff_ptr_ptr, repo.ptr, tree.ptr, C_NULL, isempty(pathspecs) ? C_NULL : pathspecs) else @check ccall((:git_diff_tree_to_workdir_with_index, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{DiffOptionsStruct}), diff_ptr_ptr, repo.ptr, tree.ptr, isempty(pathspecs) ? C_NULL : pathspecs) end return GitDiff(repo, diff_ptr_ptr[]) end """ diff_tree(repo::GitRepo, oldtree::GitTree, newtree::GitTree) Generate a [`GitDiff`](@ref) between `oldtree` (which will be used for the "old" side of the [`DiffDelta`](@ref)) and `newtree` (which will be used for the "new" side of the `DiffDelta`). Equivalent to [`git_diff_tree_to_tree`](https://libgit2.org/libgit2/#HEAD/group/diff/git_diff_tree_to_tree). This can be used to generate a diff between two commits. For instance, it could be used to compare a commit made 2 months ago with the current latest commit, or to compare a commit on another branch with the current latest commit on `master`. """ function diff_tree(repo::GitRepo, oldtree::GitTree, newtree::GitTree) ensure_initialized() diff_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_diff_tree_to_tree, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{DiffOptionsStruct}), diff_ptr_ptr, repo.ptr, oldtree.ptr, newtree.ptr, C_NULL) return GitDiff(repo, diff_ptr_ptr[]) end """ GitDiffStats(diff::GitDiff) Get the diff statistics from the [`GitDiff`](@ref) `diff`. This object records a summary of changes made across the `diff`. In particular, it records how many files were changed, how many insertions were made, and how many deletions were made. """ function GitDiffStats(diff::GitDiff) ensure_initialized() diff_stat_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_diff_get_stats, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), diff_stat_ptr_ptr, diff.ptr) return GitDiffStats(diff.owner, diff_stat_ptr_ptr[]) end """ files_changed(diff_stat::GitDiffStats) -> Csize_t Return how many files were changed (added/modified/deleted) in the [`GitDiff`](@ref) summarized by `diff_stat`. The result may vary depending on the [`DiffOptionsStruct`](@ref) used to generate the parent `GitDiff` of `diff_stat` (for instance, whether ignored files are to be included or not). """ function files_changed(diff_stat::GitDiffStats) ensure_initialized() return ccall((:git_diff_stats_files_changed, libgit2), Csize_t, (Ptr{Cvoid},), diff_stat.ptr) end """ insertions(diff_stat::GitDiffStats) -> Csize_t Return the total number of insertions (lines added) in the [`GitDiff`](@ref) summarized by `diff_stat`. The result may vary depending on the [`DiffOptionsStruct`](@ref) used to generate the parent `GitDiff` of `diff_stat` (for instance, whether ignored files are to be included or not). """ function insertions(diff_stat::GitDiffStats) ensure_initialized() return ccall((:git_diff_stats_insertions, libgit2), Csize_t, (Ptr{Cvoid},), diff_stat.ptr) end """ deletions(diff_stat::GitDiffStats) -> Csize_t Return the total number of deletions (lines removed) in the [`GitDiff`](@ref) summarized by `diff_stat`. The result may vary depending on the [`DiffOptionsStruct`](@ref) used to generate the parent `GitDiff` of `diff_stat` (for instance, whether ignored files are to be included or not). """ function deletions(diff_stat::GitDiffStats) ensure_initialized() return ccall((:git_diff_stats_deletions, libgit2), Csize_t, (Ptr{Cvoid},), diff_stat.ptr) end function count(diff::GitDiff) ensure_initialized() return ccall((:git_diff_num_deltas, libgit2), Cint, (Ptr{Cvoid},), diff.ptr) end function Base.getindex(diff::GitDiff, i::Integer) if i < 1 || i > count(diff) throw(BoundsError(diff, (i,))) end ensure_initialized() delta_ptr = ccall((:git_diff_get_delta, libgit2), Ptr{DiffDelta}, (Ptr{Cvoid}, Csize_t), diff.ptr, i-1) return unsafe_load(delta_ptr) end function Base.show(io::IO, diff_stat::GitDiffStats) println(io, "GitDiffStats:") println(io, "Files changed: $(files_changed(diff_stat))") println(io, "Insertions: $(insertions(diff_stat))") println(io, "Deletions: $(deletions(diff_stat))") end function Base.show(io::IO, diff::GitDiff) println(io, "GitDiff:") println(io, "Number of deltas: $(count(diff))") show(io, GitDiffStats(diff)) end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/rebase.jlึ������# This file is a part of Julia. License is MIT: https://julialang.org/license function GitRebase(repo::GitRepo, branch::GitAnnotated, upstream::GitAnnotated; onto::Union{GitAnnotated, Nothing}=nothing, opts::RebaseOptions = RebaseOptions()) ensure_initialized() rebase_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_rebase_init, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{RebaseOptions}), rebase_ptr_ptr, repo.ptr, branch.ptr, upstream.ptr, onto === nothing ? C_NULL : onto.ptr, Ref(opts)) return GitRebase(repo, rebase_ptr_ptr[]) end function count(rb::GitRebase) ensure_initialized() return ccall((:git_rebase_operation_entrycount, libgit2), Csize_t, (Ptr{Cvoid},), rb.ptr) end """ current(rb::GitRebase) -> Csize_t Return the index of the current [`RebaseOperation`](@ref). If no operation has yet been applied (because the [`GitRebase`](@ref) has been constructed but `next` has not yet been called or iteration over `rb` has not yet begun), return `GIT_REBASE_NO_OPERATION`, which is equal to `typemax(Csize_t)`. """ function current(rb::GitRebase) ensure_initialized() return ccall((:git_rebase_operation_current, libgit2), Csize_t, (Ptr{Cvoid},), rb.ptr) end function Base.getindex(rb::GitRebase, i::Integer) if !(1 <= i <= count(rb)) throw(BoundsError(rb, (i,))) end ensure_initialized() GC.@preserve rb begin rb_op_ptr = ccall((:git_rebase_operation_byindex, libgit2), Ptr{RebaseOperation}, (Ptr{Cvoid}, Csize_t), rb.ptr, i-1) rb_op = unsafe_load(rb_op_ptr) end return rb_op end function Base.iterate(rb::GitRebase, state=nothing) ensure_initialized() rb_op_ptr_ptr = Ref{Ptr{RebaseOperation}}(C_NULL) GC.@preserve rb begin err = ccall((:git_rebase_next, libgit2), Cint, (Ptr{Ptr{RebaseOperation}}, Ptr{Cvoid}), rb_op_ptr_ptr, rb.ptr) if err == Cint(Error.GIT_OK) return unsafe_load(rb_op_ptr_ptr[]), nothing elseif err == Cint(Error.ITEROVER) return nothing else throw(GitError(err)) end end end function Base.show(io::IO, rb::GitRebase) println(io, "GitRebase:") println(io, "Number: ", count(rb)) println(io, "Currently performing operation: ", current(rb)+1) end """ LibGit2.commit(rb::GitRebase, sig::GitSignature) Commit the current patch to the rebase `rb`, using `sig` as the committer. Is silent if the commit has already been applied. """ function commit(rb::GitRebase, sig::GitSignature) ensure_initialized() oid_ptr = Ref(GitHash()) try @check ccall((:git_rebase_commit, libgit2), Error.Code, (Ptr{GitHash}, Ptr{Cvoid}, Ptr{SignatureStruct}, Ptr{SignatureStruct}, Ptr{UInt8}, Ptr{UInt8}), oid_ptr, rb.ptr, C_NULL, sig.ptr, C_NULL, C_NULL) catch err # TODO: return current HEAD instead err isa GitError && err.code === Error.EAPPLIED && return nothing rethrow() end return oid_ptr[] end """ abort(rb::GitRebase) -> Csize_t Cancel the in-progress rebase, undoing all changes made so far and returning the parent repository of `rb` and its working directory to their state before the rebase was initiated. Return `0` if the abort is successful, `LibGit2.Error.ENOTFOUND` if no rebase is in progress (for example, if the rebase had completed), and `-1` for other errors. """ function abort(rb::GitRebase) ensure_initialized() return ccall((:git_rebase_abort, libgit2), Csize_t, (Ptr{Cvoid},), rb.ptr) end """ finish(rb::GitRebase, sig::GitSignature) -> Csize_t Complete the rebase described by `rb`. `sig` is a [`GitSignature`](@ref) to specify the identity of the user finishing the rebase. Return `0` if the rebase finishes successfully, `-1` if there is an error. """ function finish(rb::GitRebase, sig::GitSignature) ensure_initialized() return ccall((:git_rebase_finish, libgit2), Csize_t, (Ptr{Cvoid}, Ptr{SignatureStruct}), rb.ptr, sig.ptr) end p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/blame.jl7 ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=BlameOptions()) Construct a `GitBlame` object for the file at `path`, using change information gleaned from the history of `repo`. The `GitBlame` object records who changed which chunks of the file when, and how. `options` controls how to separate the contents of the file and which commits to probe - see [`BlameOptions`](@ref) for more information. """ function GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=BlameOptions()) ensure_initialized() blame_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_blame_file, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Ptr{BlameOptions}), blame_ptr_ptr, repo.ptr, path, Ref(options)) return GitBlame(repo, blame_ptr_ptr[]) end """ counthunks(blame::GitBlame) Return the number of distinct "hunks" with a file. A hunk may contain multiple lines. A hunk is usually a piece of a file that was added/changed/removed together, for example, a function added to a source file or an inner loop that was optimized out of that function later. """ function counthunks(blame::GitBlame) ensure_initialized() return ccall((:git_blame_get_hunk_count, libgit2), Int32, (Ptr{Cvoid},), blame.ptr) end function Base.getindex(blame::GitBlame, i::Integer) if !(1 <= i <= counthunks(blame)) throw(BoundsError(blame, (i,))) end ensure_initialized() GC.@preserve blame begin hunk_ptr = ccall((:git_blame_get_hunk_byindex, libgit2), Ptr{BlameHunk}, (Ptr{Cvoid}, Csize_t), blame.ptr, i-1) elem = unsafe_load(hunk_ptr) end return elem end function Base.show(io::IO, blame_hunk::BlameHunk) println(io, "GitBlameHunk:") println(io, "Original path: ", unsafe_string(blame_hunk.orig_path)) println(io, "Lines in hunk: ", blame_hunk.lines_in_hunk) println(io, "Final commit oid: ", blame_hunk.final_commit_id) print(io, "Final signature: ") show(io, Signature(blame_hunk.final_signature)) println(io) println(io, "Original commit oid: ", blame_hunk.orig_commit_id) print(io, "Original signature: ") show(io, Signature(blame_hunk.orig_signature)) end q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/status.jll������# This file is a part of Julia. License is MIT: https://julialang.org/license """ LibGit2.GitStatus(repo::GitRepo; status_opts=StatusOptions()) Collect information about the status of each file in the git repository `repo` (e.g. is the file modified, staged, etc.). `status_opts` can be used to set various options, for instance whether or not to look at untracked files or whether to include submodules or not. See [`StatusOptions`](@ref) for more information. """ function GitStatus(repo::GitRepo; status_opts=StatusOptions()) ensure_initialized() stat_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_status_list_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{StatusOptions}), stat_ptr_ptr, repo.ptr, Ref(status_opts)) return GitStatus(repo, stat_ptr_ptr[]) end function Base.length(status::GitStatus) ensure_initialized() return Int(ccall((:git_status_list_entrycount, libgit2), Csize_t, (Ptr{Ptr{Cvoid}},), status.ptr)) end function Base.getindex(status::GitStatus, i::Integer) 1 <= i <= length(status) || throw(BoundsError()) ensure_initialized() GC.@preserve status begin entry_ptr = ccall((:git_status_byindex, libgit2), Ptr{StatusEntry}, (Ptr{Cvoid}, Csize_t), status.ptr, i-1) entry_ptr == C_NULL && throw(Error.GitError(Error.ERROR)) entry = unsafe_load(entry_ptr) end return entry end """ LibGit2.status(repo::GitRepo, path::String) -> Union{Cuint, Cvoid} Lookup the status of the file at `path` in the git repository `repo`. For instance, this can be used to check if the file at `path` has been modified and needs to be staged and committed. """ function status(repo::GitRepo, path::String) ensure_initialized() status_ptr = Ref{Cuint}(0) ret = ccall((:git_status_file, libgit2), Cint, (Ref{Cuint}, Ptr{Cvoid}, Cstring), status_ptr, repo.ptr, path) (ret == Cint(Error.ENOTFOUND) || ret == Cint(Error.EAMBIGUOUS)) && return nothing return status_ptr[] end o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/tree.jl˜������# This file is a part of Julia. License is MIT: https://julialang.org/license function GitTree(c::GitCommit) tree_out = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_commit_tree, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}), tree_out, c) GitTree(repository(c), tree_out[]) end """ treewalk(f, tree::GitTree, post::Bool=false) Traverse the entries in `tree` and its subtrees in post or pre order. Preorder means beginning at the root and then traversing the leftmost subtree (and recursively on down through that subtree's leftmost subtrees) and moving right through the subtrees. Postorder means beginning at the bottom of the leftmost subtree, traversing upwards through it, then traversing the next right subtree (again beginning at the bottom) and finally visiting the tree root last of all. The function parameter `f` should have following signature: (String, GitTreeEntry) -> Cint A negative value returned from `f` stops the tree walk. A positive value means that the entry will be skipped if `post` is `false`. """ function treewalk(f, tree::GitTree, post::Bool = false) ensure_initialized() # NOTE: don't use @check/GitError directly, because the code can be arbitrary payload = Any[ tree, f ] cbf = @cfunction(function treewalk_callback(root_cstr::Cstring, entry_ptr::Ptr{Cvoid}, payload::Vector{Any})::Cint # decode arguments root = unsafe_string(root_cstr) tree = payload[1]::GitTree f = payload[2] entry = GitTreeEntry(tree, entry_ptr, false) return f(root, entry) end, Cint, (Cstring, Ptr{Cvoid}, Ref{Vector{Any}})) err = ccall((:git_tree_walk, libgit2), Cint, (Ptr{Cvoid}, Cint, Ptr{Cvoid}, Any), tree.ptr, post, cbf, payload) if err < 0 err_class, _ = Error.last_error() if err_class != Error.Callback # now we now the code is valid throw(GitError(err)) end end nothing end repository(tree::GitTree) = tree.owner repository(te::GitTreeEntry) = repository(te.owner) """ filename(te::GitTreeEntry) Return the filename of the object on disk to which `te` refers. """ function filename(te::GitTreeEntry) ensure_initialized() str = ccall((:git_tree_entry_name, libgit2), Cstring, (Ptr{Cvoid},), te.ptr) str != C_NULL && return unsafe_string(str) return nothing end """ filemode(te::GitTreeEntry) -> Cint Return the UNIX filemode of the object on disk to which `te` refers as an integer. """ function filemode(te::GitTreeEntry) ensure_initialized() return ccall((:git_tree_entry_filemode, libgit2), Cint, (Ptr{Cvoid},), te.ptr) end """ entrytype(te::GitTreeEntry) Return the type of the object to which `te` refers. The result will be one of the types which [`objtype`](@ref) returns, e.g. a `GitTree` or `GitBlob`. """ function entrytype(te::GitTreeEntry) ensure_initialized() otype = ccall((:git_tree_entry_type, libgit2), Cint, (Ptr{Cvoid},), te.ptr) return objtype(Consts.OBJECT(otype)) end """ entryid(te::GitTreeEntry) Return the [`GitHash`](@ref) of the object to which `te` refers. """ function entryid(te::GitTreeEntry) ensure_initialized() GC.@preserve te begin oid_ptr = ccall((:git_tree_entry_id, libgit2), Ptr{UInt8}, (Ptr{Cvoid},), te.ptr) oid = GitHash(oid_ptr) end return oid end function count(tree::GitTree) ensure_initialized() return ccall((:git_tree_entrycount, libgit2), Csize_t, (Ptr{Cvoid},), tree.ptr) end function Base.getindex(tree::GitTree, i::Integer) if i < 1 || i > count(tree) throw(BoundsError(tree, i)) end ensure_initialized() te_ptr = ccall((:git_tree_entry_byindex, libgit2), Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), tree.ptr, i-1) return GitTreeEntry(tree, te_ptr, false) end """ (::Type{T})(te::GitTreeEntry) where T<:GitObject Get the git object to which `te` refers and return it as its actual type (the type [`entrytype`](@ref) would show), for instance a `GitBlob` or `GitTag`. # Examples ```julia tree = LibGit2.GitTree(repo, "HEAD^{tree}") tree_entry = tree[1] blob = LibGit2.GitBlob(tree_entry) ``` """ GitObject(e::GitTreeEntry) function (::Type{T})(te::GitTreeEntry) where T<:GitObject ensure_initialized() repo = repository(te) obj_ptr_ptr = Ref{Ptr{Cvoid}}(C_NULL) @check ccall((:git_tree_entry_to_object, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Ptr{Cvoid}), obj_ptr_ptr, repo, te) return T(repo, obj_ptr_ptr[]) end function Base.show(io::IO, te::GitTreeEntry) println(io, "GitTreeEntry:") println(io, "Entry name: ", filename(te)) println(io, "Entry type: ", entrytype(te)) println(io, "Entry OID: ", entryid(te)) end function Base.show(io::IO, tree::GitTree) println(io, "GitTree:") println(io, "Owner: ", repository(tree)) println(io, "Number of entries: ", count(tree)) end function _getindex(tree::GitTree, target::AbstractString) if basename(target) == "" # get rid of any trailing separator target = dirname(target) end if isempty(target) || target == "/" return tree end entry = Ref{Ptr{Cvoid}}(C_NULL) err = ccall((:git_tree_entry_bypath, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring), entry, tree, target) err == Int(Error.ENOTFOUND) && return nothing err < 0 && throw(Error.GitError(err)) entry = GitTreeEntry(tree, entry[], true #= N.B.: Most other lookups need false here =#) return GitObject(entry) end """ getindex(tree::GitTree, target::AbstractString) -> GitObject Look up `target` path in the `tree`, returning a [`GitObject`](@ref) (a [`GitBlob`](@ref) in the case of a file, or another [`GitTree`](@ref) if looking up a directory). # Examples ```julia tree = LibGit2.GitTree(repo, "HEAD^{tree}") readme = tree["README.md"] subtree = tree["test"] runtests = subtree["runtests.jl"] ``` """ function Base.getindex(tree::GitTree, target::AbstractString) e = _getindex(tree, target) e === nothing && throw(KeyError(target)) return e end function Base.haskey(tree::GitTree, target::AbstractString) return _getindex(tree, target) !== nothing end x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/gitcredential.jl)������# This file is a part of Julia. License is MIT: https://julialang.org/license const GIT_CRED_ATTRIBUTES = ("protocol", "host", "path", "username", "password", "url") """ GitCredential Git credential information used in communication with git credential helpers. The field are named using the [input/output key specification](https://git-scm.com/docs/git-credential#IOFMT). """ mutable struct GitCredential protocol::Union{String, Nothing} host::Union{String, Nothing} path::Union{String, Nothing} username::Union{String, Nothing} password::Union{Base.SecretBuffer, Nothing} use_http_path::Bool function GitCredential( protocol::Union{AbstractString, Nothing}=nothing, host::Union{AbstractString, Nothing}=nothing, path::Union{AbstractString, Nothing}=nothing, username::Union{AbstractString, Nothing}=nothing, password::Union{AbstractString, Nothing}=nothing) new(protocol, host, path, username, password, true) end end function GitCredential(cfg::GitConfig, url::AbstractString) fill!(cfg, parse(GitCredential, url)) end function GitCredential(user_pass_cred::UserPasswordCredential, url::AbstractString) cred = parse(GitCredential, url) cred.username = user_pass_cred.user cred.password = deepcopy(user_pass_cred.pass) return cred end Base.:(==)(c1::GitCredential, c2::GitCredential) = (c1.protocol, c1.host, c1.path, c1.username, c1.password, c1.use_http_path) == (c2.protocol, c2.host, c2.path, c2.username, c2.password, c2.use_http_path) Base.hash(cred::GitCredential, h::UInt) = hash(GitCredential, hash((cred.protocol, cred.host, cred.path, cred.username, cred.password, cred.use_http_path), h)) function Base.shred!(cred::GitCredential) cred.protocol = nothing cred.host = nothing cred.path = nothing cred.username = nothing pwd = cred.password pwd !== nothing && Base.shred!(pwd) cred.password = nothing return cred end """ ismatch(url, git_cred) -> Bool Checks if the `git_cred` is valid for the given `url`. """ function ismatch(url::AbstractString, git_cred::GitCredential) isempty(url) && return true m = match(URL_REGEX, url) m === nothing && error("Unable to parse URL") # Note: missing URL groups match anything (m[:scheme] === nothing ? true : m[:scheme] == git_cred.protocol) && (m[:host] === nothing ? true : m[:host] == git_cred.host) && (m[:path] === nothing ? true : m[:path] == git_cred.path) && (m[:user] === nothing ? true : m[:user] == git_cred.username) end function isfilled(cred::GitCredential) cred.username !== nothing && cred.password !== nothing end function Base.parse(::Type{GitCredential}, url::AbstractString) m = match(URL_REGEX, url) m === nothing && error("Unable to parse URL") return GitCredential( m[:scheme], m[:host], m[:path], m[:user], m[:password], ) end function Base.copy!(a::GitCredential, b::GitCredential) Base.shred!(a) a.protocol = b.protocol a.host = b.host a.path = b.path a.username = b.username a.password = b.password === nothing ? nothing : copy(b.password) return a end function Base.write(io::IO, cred::GitCredential) cred.protocol !== nothing && write(io, "protocol=", cred.protocol, '\n') cred.host !== nothing && write(io, "host=", cred.host, '\n') cred.path !== nothing && cred.use_http_path && write(io, "path=", cred.path, '\n') cred.username !== nothing && write(io, "username=", cred.username, '\n') cred.password !== nothing && write(io, "password=", cred.password, '\n') nothing end function Base.read!(io::IO, cred::GitCredential) # https://git-scm.com/docs/git-credential#IOFMT while !(eof(io)::Bool) key::AbstractString = readuntil(io, '=') if key == "password" value = Base.SecretBuffer() while !(eof(io)::Bool) && (c = read(io, UInt8)) != UInt8('\n') write(value, c) end seekstart(value) else value = readuntil(io, '\n') end if key == "url" # Any components which are missing from the URL will be set to empty # https://git-scm.com/docs/git-credential#git-credential-codeurlcode Base.shred!(parse(GitCredential, value::AbstractString)) do urlcred copy!(cred, urlcred) end elseif key in GIT_CRED_ATTRIBUTES field = getproperty(cred, Symbol(key)) field !== nothing && Symbol(key) === :password && Base.shred!(field) setproperty!(cred, Symbol(key), value) elseif !all(isspace, key) @warn "Unknown git credential attribute found: $(repr(key))" end end return cred end function fill!(cfg::GitConfig, cred::GitCredential) cred.use_http_path = use_http_path(cfg, cred) # When the username is missing default to using the username set in the configuration if cred.username === nothing cred.username = default_username(cfg, cred) end for helper in credential_helpers(cfg, cred) fill!(helper, cred) # "Once Git has acquired both a username and a password, no more helpers will be # tried." โ€“ https://git-scm.com/docs/gitcredentials#gitcredentials-helper !isfilled(cred) && break end return cred end struct GitCredentialHelper cmd::Cmd end function Base.parse(::Type{GitCredentialHelper}, helper::AbstractString) # The helper string can take on different behaviors depending on the value: # - "Code after `!` evaluated in shell" โ€“ https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage # - "If the helper name is not an absolute path, then the string `git credential-` is # prepended." โ€“ https://git-scm.com/docs/gitcredentials#gitcredentials-helper if startswith(helper, '!') cmd_str = helper[2:end] elseif isabspath(first(Base.shell_split(helper))) cmd_str = helper else cmd_str = "git credential-$helper" end GitCredentialHelper(`$(Base.shell_split(cmd_str))`) end function Base.:(==)(a::GitCredentialHelper, b::GitCredentialHelper) a.cmd == b.cmd end function run!(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential) cmd = `$(helper.cmd) $operation` p = open(cmd, "r+") # Provide the helper with the credential information we know write(p, cred) write(p, "\n") t = @async close(p.in) # Process the response from the helper Base.read!(p, cred) wait(p) return cred end function run(helper::GitCredentialHelper, operation::AbstractString, cred::GitCredential) run!(helper, operation, deepcopy(cred)) end # The available actions between using `git credential` and helpers are slightly different. # We will directly interact with the helpers as that way we can request credential # information without a prompt (helper `get` vs. git credential `fill`). # https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage fill!(helper::GitCredentialHelper, cred::GitCredential) = run!(helper, "get", cred) approve(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "store", cred) reject(helper::GitCredentialHelper, cred::GitCredential) = run(helper, "erase", cred) """ credential_helpers(config, git_cred) -> Vector{GitCredentialHelper} Return all of the `GitCredentialHelper`s found within the provided `config` which are valid for the specified `git_cred`. """ function credential_helpers(cfg::GitConfig, cred::GitCredential) helpers = GitCredentialHelper[] # https://git-scm.com/docs/gitcredentials#gitcredentials-helper for entry in GitConfigIter(cfg, r"credential.*\.helper$") section, url, name, value = split_cfg_entry(entry) @assert name == "helper" # Only use configuration settings where the URL applies to the git credential ismatch(url, cred) || continue # An empty credential.helper resets the list to empty if isempty(value) empty!(helpers) else Base.push!(helpers, parse(GitCredentialHelper, value)) end end return helpers end """ default_username(config, git_cred) -> Union{String, Nothing} Return the default username, if any, provided by the `config` which is valid for the specified `git_cred`. """ function default_username(cfg::GitConfig, cred::GitCredential) # https://git-scm.com/docs/gitcredentials#gitcredentials-username for entry in GitConfigIter(cfg, r"credential.*\.username") section, url, name, value = split_cfg_entry(entry) @assert name == "username" # Only use configuration settings where the URL applies to the git credential ismatch(url, cred) || continue return value end return nothing end function use_http_path(cfg::GitConfig, cred::GitCredential) seen_specific = false use_path = false # Default is to ignore the path # https://git-scm.com/docs/gitcredentials#gitcredentials-useHttpPath # # Note: Ideally the regular expression should use "useHttpPath" # https://github.com/libgit2/libgit2/issues/4390 for entry in GitConfigIter(cfg, r"credential.*\.usehttppath") section, url, name, value = split_cfg_entry(entry) # Ignore global configuration if we have already encountered more specific entry if ismatch(url, cred) && (!isempty(url) || !seen_specific) seen_specific = !isempty(url) use_path = value == "true" end end return use_path end approve(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing reject(cfg::GitConfig, cred::AbstractCredential, url::AbstractString) = nothing function approve(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString) git_cred = GitCredential(cred, url) git_cred.use_http_path = use_http_path(cfg, git_cred) for helper in credential_helpers(cfg, git_cred) approve(helper, git_cred) end Base.shred!(git_cred) nothing end function reject(cfg::GitConfig, cred::UserPasswordCredential, url::AbstractString) git_cred = GitCredential(cred, url) git_cred.use_http_path = use_http_path(cfg, git_cred) for helper in credential_helpers(cfg, git_cred) reject(helper, git_cred) end Base.shred!(git_cred) nothing end t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibGit2/src/callbacks.jl๓P������# This file is a part of Julia. License is MIT: https://julialang.org/license """Mirror callback function Function sets `+refs/*:refs/*` refspecs and `mirror` flag for remote reference. """ function mirror_callback(remote::Ptr{Ptr{Cvoid}}, repo_ptr::Ptr{Cvoid}, name::Cstring, url::Cstring, payload::Ptr{Cvoid}) ensure_initialized() # Create the remote with a mirroring url fetch_spec = "+refs/*:refs/*" err = ccall((:git_remote_create_with_fetchspec, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring, Cstring), remote, repo_ptr, name, url, fetch_spec) err != 0 && return Cint(err) # And set the configuration option to true for the push command config = GitConfig(GitRepo(repo_ptr,false)) name_str = unsafe_string(name) err= try set!(config, "remote.$name_str.mirror", true) catch; -1 finally close(config) end err != 0 && return Cint(err) return Cint(0) end """ LibGit2.is_passphrase_required(private_key) -> Bool Return `true` if the `private_key` file requires a passphrase, `false` otherwise. """ function is_passphrase_required(private_key::AbstractString) !isfile(private_key) && return false # In encrypted private keys, the second line is "Proc-Type: 4,ENCRYPTED" return open(private_key) do f readline(f) readline(f) == "Proc-Type: 4,ENCRYPTED" end end function user_abort() ensure_initialized() # Note: Potentially it could be better to just throw a Julia error. ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "Aborting, user cancelled credential request.") return Cint(Error.EUSER) end function prompt_limit() ensure_initialized() ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "Aborting, maximum number of prompts reached.") return Cint(Error.EAUTH) end function exhausted_abort() ensure_initialized() ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "All authentication methods have failed.") return Cint(Error.EAUTH) end function authenticate_ssh(libgit2credptr::Ptr{Ptr{Cvoid}}, p::CredentialPayload, username_ptr) ensure_initialized() cred = p.credential::SSHCredential revised = false # Use a filled credential as-is on the first pass. Reset password on successive calls. if p.first_pass && isfilled(cred) revised = true elseif !p.first_pass cred.pass = "" end # first try ssh-agent if credentials support its usage if p.use_ssh_agent && username_ptr != Cstring(C_NULL) && (!revised || !isfilled(cred)) err = ccall((:git_cred_ssh_key_from_agent, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring), libgit2credptr, username_ptr) p.use_ssh_agent = false # use ssh-agent only one time err == 0 && return Cint(0) end if p.use_env && (!revised || !isfilled(cred)) if isempty(cred.user) && username_ptr != Cstring(C_NULL) cred.user = unsafe_string(username_ptr) end if haskey(ENV, "SSH_KEY_PATH") cred.prvkey = ENV["SSH_KEY_PATH"] elseif isempty(cred.prvkey) for keytype in ("rsa", "ecdsa") private_key_file = joinpath(homedir(), ".ssh", "id_$keytype") if isfile(private_key_file) cred.prvkey = private_key_file break end end end cred.pubkey = Base.get(ENV, "SSH_PUB_KEY_PATH") do default = cred.prvkey * ".pub" if isempty(cred.pubkey) && isfile(default) default else cred.pubkey end end cred.pass = Base.get(ENV, "SSH_KEY_PASS", cred.pass) revised = true p.use_env = false end if p.remaining_prompts > 0 && (!revised || !isfilled(cred)) if isempty(cred.user) || username_ptr == Cstring(C_NULL) url = git_url(scheme=p.scheme, host=p.host) response = Base.prompt("Username for '$url'", default=cred.user) response === nothing && return user_abort() cred.user = response end url = git_url(scheme=p.scheme, host=p.host, username=cred.user) # For SSH we need a private key location last_private_key = cred.prvkey if !isfile(cred.prvkey) || !revised || !haskey(ENV, "SSH_KEY_PATH") response = Base.prompt("Private key location for '$url'", default=cred.prvkey) response === nothing && return user_abort() cred.prvkey = expanduser(response) # Only update the public key if the private key changed if cred.prvkey != last_private_key cred.pubkey = cred.prvkey * ".pub" end end # For SSH we need a public key location. Avoid asking about the public key as # typically this will just annoy users. stale = !p.first_pass && cred.prvkey == last_private_key && cred.pubkey != cred.prvkey * ".pub" if isfile(cred.prvkey) && (stale || !isfile(cred.pubkey)) response = Base.prompt("Public key location for '$url'", default=cred.pubkey) response === nothing && return user_abort() cred.pubkey = expanduser(response) end # Ask for a passphrase when the private key exists and requires a passphrase if isempty(cred.pass) && is_passphrase_required(cred.prvkey) if Sys.iswindows() response = Base.winprompt( "Your SSH Key requires a password, please enter it now:", "Passphrase required", cred.prvkey; prompt_username=false) response === nothing && return user_abort() cred.pass = response[2] else response = Base.getpass("Passphrase for $(cred.prvkey)") response === nothing && return user_abort() cred.pass = response isempty(cred.pass) && return user_abort() # Ambiguous if EOF or newline end end revised = true p.remaining_prompts -= 1 p.remaining_prompts <= 0 && return prompt_limit() end if !revised return exhausted_abort() end return ccall((:git_cred_ssh_key_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cstring, Cstring, Cstring), libgit2credptr, cred.user, cred.pubkey, cred.prvkey, cred.pass) end function authenticate_userpass(libgit2credptr::Ptr{Ptr{Cvoid}}, p::CredentialPayload) ensure_initialized() cred = p.credential::UserPasswordCredential revised = false # Use a filled credential as-is on the first pass. Reset password on successive calls. if p.first_pass && isfilled(cred) revised = true elseif !p.first_pass cred.pass = "" end if p.use_git_helpers && (!revised || !isfilled(cred)) git_cred = GitCredential(p.config, p.url) # Use `deepcopy` to ensure shredding the `git_cred` does not shred the `cred`s copy cred.user = something(git_cred.username, "") cred.pass = deepcopy(something(git_cred.password, "")) Base.shred!(git_cred) revised = true p.use_git_helpers = false end if p.remaining_prompts > 0 && (!revised || !isfilled(cred)) url = git_url(scheme=p.scheme, host=p.host) username = isempty(cred.user) ? p.username : cred.user if Sys.iswindows() response = Base.winprompt( "Please enter your credentials for '$url'", "Credentials required", username; prompt_username=true) response === nothing && return user_abort() cred.user, cred.pass = response else response = Base.prompt("Username for '$url'", default=username) response === nothing && return user_abort() cred.user = response url = git_url(scheme=p.scheme, host=p.host, username=cred.user) response = Base.getpass("Password for '$url'") response === nothing && return user_abort() cred.pass = response isempty(cred.pass) && return user_abort() # Ambiguous if EOF or newline end revised = true p.remaining_prompts -= 1 p.remaining_prompts <= 0 && return prompt_limit() end if !revised return exhausted_abort() end return ccall((:git_cred_userpass_plaintext_new, libgit2), Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cstring), libgit2credptr, cred.user, cred.pass) end """ credential_callback(...) -> Cint A LibGit2 credential callback function which provides different credential acquisition functionality w.r.t. a connection protocol. The `payload_ptr` is required to contain a `LibGit2.CredentialPayload` object which will keep track of state and settings. The `allowed_types` contains a bitmask of `LibGit2.Consts.GIT_CREDTYPE` values specifying which authentication methods should be attempted. Credential authentication is done in the following order (if supported): - SSH agent - SSH private/public key pair - Username/password plain text If a user is presented with a credential prompt they can abort the prompt by typing `^D` (pressing the control key together with the `d` key). **Note**: Due to the specifics of the `libgit2` authentication procedure, when authentication fails, this function is called again without any indication whether authentication was successful or not. To avoid an infinite loop from repeatedly using the same faulty credentials, we will keep track of state using the payload. For addition details see the LibGit2 guide on [authenticating against a server](https://libgit2.org/docs/guides/authentication/). """ function credentials_callback(libgit2credptr::Ptr{Ptr{Cvoid}}, url_ptr::Cstring, username_ptr::Cstring, allowed_types::Cuint, p::CredentialPayload) err = Cint(0) # Parse URL only during the first call to this function. Future calls will use the # information cached inside the payload. if isempty(p.url) p.url = unsafe_string(url_ptr) m = match(URL_REGEX, p.url)::RegexMatch p.scheme = something(m[:scheme], SubString("")) p.username = something(m[:user], SubString("")) p.host = something(m[:host]) # When an explicit credential is supplied we will make sure to use the given # credential during the first callback by modifying the allowed types. The # modification only is in effect for the first callback since `allowed_types` cannot # be mutated. cache = p.cache explicit = p.explicit if explicit !== nothing cred = explicit # Copy explicit credentials to avoid mutating approved credentials. # invalidation fix from cred being non-inferrable p.credential = Base.invokelatest(deepcopy, cred) if isa(cred, SSHCredential) allowed_types &= Cuint(Consts.CREDTYPE_SSH_KEY) elseif isa(cred, UserPasswordCredential) allowed_types &= Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT) else allowed_types &= Cuint(0) # Unhandled credential type end elseif cache !== nothing cred_id = credential_identifier(p.scheme, p.host) # Perform a deepcopy as we do not want to mutate approved cached credentials if haskey(cache, cred_id) # invalidation fix from cache[cred_id] being non-inferrable p.credential = Base.invokelatest(deepcopy, cache[cred_id]) end end p.first_pass = true else p.first_pass = false end # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) if p.credential === nothing || !isa(p.credential, SSHCredential) p.credential = SSHCredential(p.username) end err = authenticate_ssh(libgit2credptr, p, username_ptr) err == 0 && return err end if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) if p.credential === nothing || !isa(p.credential, UserPasswordCredential) p.credential = UserPasswordCredential(p.username) end err = authenticate_userpass(libgit2credptr, p) err == 0 && return err end # No authentication method we support succeeded. The most likely cause is # that explicit credentials were passed in, but said credentials are incompatible # with the requested authentication method. if err == 0 if p.explicit !== nothing ensure_initialized() ccall((:giterr_set_str, libgit2), Cvoid, (Cint, Cstring), Cint(Error.Callback), "The explicitly provided credential is incompatible with the requested " * "authentication methods.") end err = Cint(Error.EAUTH) end return err end function credentials_callback(libgit2credptr::Ptr{Ptr{Cvoid}}, url_ptr::Cstring, username_ptr::Cstring, allowed_types::Cuint, payloads::Dict{Symbol, Any}) p = payloads[:credentials] return credentials_callback(libgit2credptr, url_ptr, username_ptr, allowed_types, p) end function fetchhead_foreach_callback(ref_name::Cstring, remote_url::Cstring, oid_ptr::Ptr{GitHash}, is_merge::Cuint, payload::Any) fhead_vec = payload::Vector{FetchHead} Base.push!(fhead_vec, FetchHead(unsafe_string(ref_name), unsafe_string(remote_url), unsafe_load(oid_ptr), is_merge == 1)) return Cint(0) end struct CertHostKey parent :: Cint mask :: Cint md5 :: NTuple{16,UInt8} sha1 :: NTuple{20,UInt8} sha256 :: NTuple{32,UInt8} type :: Cint hostkey :: Ptr{Cchar} len :: Csize_t end function verify_host_error(message::AbstractString) printstyled(stderr, "$message\n", color = :cyan, bold = true) end function certificate_callback( cert_p :: Ptr{CertHostKey}, valid :: Cint, host_p :: Ptr{Cchar}, data_p :: Ptr{Cvoid}, )::Cint valid != 0 && return Consts.CERT_ACCEPT host = unsafe_string(host_p) cert_type = unsafe_load(convert(Ptr{Cint}, cert_p)) transport = cert_type == Consts.CERT_TYPE_TLS ? "TLS" : cert_type == Consts.CERT_TYPE_SSH ? "SSH" : nothing if !NetworkOptions.verify_host(host, transport) # user has opted out of host verification return Consts.CERT_ACCEPT end if transport == "TLS" # TLS verification is done before the callback and indicated with the # incoming `valid` flag, so if we get here then host verification failed verify_host_error("TLS host verification: the identity of the server `$host` could not be verified. Someone could be trying to man-in-the-middle your connection. It is also possible that the correct server is using an invalid certificate or that your system's certificate authority root store is misconfigured.") return Consts.CERT_REJECT elseif transport == "SSH" # SSH verification has to be done here files = NetworkOptions.ssh_known_hosts_files() cert = unsafe_load(cert_p) check = ssh_knownhost_check(files, host, cert) valid = false if check == Consts.LIBSSH2_KNOWNHOST_CHECK_MATCH valid = true elseif check == Consts.LIBSSH2_KNOWNHOST_CHECK_NOTFOUND if Sys.which("ssh-keyscan") !== nothing msg = "Please run `ssh-keyscan $host >> $(files[1])` in order to add the server to your known hosts file and then try again." else msg = "Please connect once using `ssh $host` in order to add the server to your known hosts file and then try again. You may not be allowed to log in (wrong user and/or no login allowed), but ssh will prompt you to add a host key for the server which will allow libgit2 to verify the server." end verify_host_error("SSH host verification: the server `$host` is not a known host. $msg") elseif check == Consts.LIBSSH2_KNOWNHOST_CHECK_MISMATCH verify_host_error("SSH host verification: the identity of the server `$host` does not match its known hosts record. Someone could be trying to man-in-the-middle your connection. It is also possible that the server has changed its key, in which case you should check with the server administrator and if they confirm that the key has been changed, update your known hosts file.") else @error("unexpected SSH known host check result", check) end return valid ? Consts.CERT_ACCEPT : Consts.CERT_REJECT end @error("unexpected transport encountered, refusing to validate", cert_type) return Consts.CERT_REJECT end struct KnownHost magic :: Cuint node :: Ptr{Cvoid} name :: Ptr{Cchar} key :: Ptr{Cchar} type :: Cint end function ssh_knownhost_check( files :: AbstractVector{<:AbstractString}, host :: AbstractString, cert :: CertHostKey, ) key = unsafe_wrap(Array, cert.hostkey, cert.len) return ssh_knownhost_check(files, host, key) end function ssh_knownhost_check( files :: AbstractVector{<:AbstractString}, host :: AbstractString, key :: Vector{Cchar}, ) if (m = match(r"^(.+):(\d+)$", host)) !== nothing host = m.captures[1] port = parse(Int, something(m.captures[2])) else port = 22 # default SSH port end len = length(key) mask = Consts.LIBSSH2_KNOWNHOST_TYPE_PLAIN | Consts.LIBSSH2_KNOWNHOST_KEYENC_RAW session = @ccall "libssh2".libssh2_session_init_ex( C_NULL :: Ptr{Cvoid}, C_NULL :: Ptr{Cvoid}, C_NULL :: Ptr{Cvoid}, C_NULL :: Ptr{Cvoid}, ) :: Ptr{Cvoid} for file in files ispath(file) || continue hosts = @ccall "libssh2".libssh2_knownhost_init( session :: Ptr{Cvoid}, ) :: Ptr{Cvoid} count = @ccall "libssh2".libssh2_knownhost_readfile( hosts :: Ptr{Cvoid}, file :: Cstring, 1 :: Cint, # standard OpenSSH format ) :: Cint if count < 0 @warn("Error parsing SSH known hosts file `$file`") @ccall "libssh2".libssh2_knownhost_free(hosts::Ptr{Cvoid})::Cvoid continue end check = @ccall "libssh2".libssh2_knownhost_checkp( hosts :: Ptr{Cvoid}, host :: Cstring, port :: Cint, key :: Ptr{Cchar}, len :: Csize_t, mask :: Cint, C_NULL :: Ptr{Ptr{KnownHost}}, ) :: Cint if check == Consts.LIBSSH2_KNOWNHOST_CHECK_MATCH || check == Consts.LIBSSH2_KNOWNHOST_CHECK_MISMATCH @ccall "libssh2".libssh2_knownhost_free(hosts::Ptr{Cvoid})::Cvoid @assert 0 == @ccall "libssh2".libssh2_session_free(session::Ptr{Cvoid})::Cint return check else @ccall "libssh2".libssh2_knownhost_free(hosts::Ptr{Cvoid})::Cvoid if check == Consts.LIBSSH2_KNOWNHOST_CHECK_FAILURE @warn("Error searching SSH known hosts file `$file`") end continue end end # name not found in any known hosts files @assert 0 == @ccall "libssh2".libssh2_session_free(session::Ptr{Cvoid})::Cint return Consts.LIBSSH2_KNOWNHOST_CHECK_NOTFOUND end function trace_callback(level::Cint, msg::Cstring)::Cint println(stderr, "[$level]: $(unsafe_string(msg))") return 0 end "C function pointer for `mirror_callback`" mirror_cb() = @cfunction(mirror_callback, Cint, (Ptr{Ptr{Cvoid}}, Ptr{Cvoid}, Cstring, Cstring, Ptr{Cvoid})) "C function pointer for `credentials_callback`" credentials_cb() = @cfunction(credentials_callback, Cint, (Ptr{Ptr{Cvoid}}, Cstring, Cstring, Cuint, Any)) "C function pointer for `fetchhead_foreach_callback`" fetchhead_foreach_cb() = @cfunction(fetchhead_foreach_callback, Cint, (Cstring, Cstring, Ptr{GitHash}, Cuint, Any)) "C function pointer for `certificate_callback`" certificate_cb() = @cfunction(certificate_callback, Cint, (Ptr{CertHostKey}, Cint, Ptr{Cchar}, Ptr{Cvoid})) "C function pointer for `trace_callback`" trace_cb() = @cfunction(trace_callback, Cint, (Cint, Cstring)) n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/UUIDs/src/UUIDs.jl‰������# This file is a part of Julia. License is MIT: https://julialang.org/license """ This module provides universally unique identifiers (UUIDs), along with functions creating the different variants. """ module UUIDs using Random import SHA export UUID, uuid1, uuid4, uuid5, uuid_version import Base: UUID """ uuid_version(u::UUID) -> Int Inspects the given UUID and returns its version (see [RFC 4122](https://www.ietf.org/rfc/rfc4122)). # Examples ```jldoctest julia> uuid_version(uuid4()) 4 ``` """ uuid_version(u::UUID) = Int((u.value >> 76) & 0xf) # Some UUID namespaces provided in the appendix of RFC 4122 # https://tools.ietf.org/html/rfc4122.html#appendix-C const namespace_dns = UUID(0x6ba7b8109dad11d180b400c04fd430c8) # 6ba7b810-9dad-11d1-80b4-00c04fd430c8 const namespace_url = UUID(0x6ba7b8119dad11d180b400c04fd430c8) # 6ba7b811-9dad-11d1-80b4-00c04fd430c8 const namespace_oid = UUID(0x6ba7b8129dad11d180b400c04fd430c8) # 6ba7b812-9dad-11d1-80b4-00c04fd430c8 const namespace_x500 = UUID(0x6ba7b8149dad11d180b400c04fd430c8) # 6ba7b814-9dad-11d1-80b4-00c04fd430c8 """ uuid1([rng::AbstractRNG]) -> UUID Generates a version 1 (time-based) universally unique identifier (UUID), as specified by RFC 4122. Note that the Node ID is randomly generated (does not identify the host) according to section 4.5 of the RFC. The default rng used by `uuid1` is not `GLOBAL_RNG` and every invocation of `uuid1()` without an argument should be expected to return a unique identifier. Importantly, the outputs of `uuid1` do not repeat even when `Random.seed!(seed)` is called. Currently (as of Julia 1.6), `uuid1` uses `Random.RandomDevice` as the default rng. However, this is an implementation detail that may change in the future. !!! compat "Julia 1.6" The output of `uuid1` does not depend on `GLOBAL_RNG` as of Julia 1.6. # Examples ```jldoctest; filter = r"[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}" julia> rng = MersenneTwister(1234); julia> uuid1(rng) UUID("cfc395e8-590f-11e8-1f13-43a2532b2fa8") ``` """ function uuid1(rng::AbstractRNG=Random.RandomDevice()) u = rand(rng, UInt128) # mask off clock sequence and node u &= 0x00000000000000003fffffffffffffff # set the unicast/multicast bit and version u |= 0x00000000000010000000010000000000 # 0x01b21dd213814000 is the number of 100 nanosecond intervals # between the UUID epoch and Unix epoch timestamp = round(UInt64, time() * 1e7) + 0x01b21dd213814000 ts_low = timestamp & typemax(UInt32) ts_mid = (timestamp >> 32) & typemax(UInt16) ts_hi = (timestamp >> 48) & 0x0fff u |= UInt128(ts_low) << 96 u |= UInt128(ts_mid) << 80 u |= UInt128(ts_hi) << 64 UUID(u) end """ uuid4([rng::AbstractRNG]) -> UUID Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), as specified by RFC 4122. The default rng used by `uuid4` is not `GLOBAL_RNG` and every invocation of `uuid4()` without an argument should be expected to return a unique identifier. Importantly, the outputs of `uuid4` do not repeat even when `Random.seed!(seed)` is called. Currently (as of Julia 1.6), `uuid4` uses `Random.RandomDevice` as the default rng. However, this is an implementation detail that may change in the future. !!! compat "Julia 1.6" The output of `uuid4` does not depend on `GLOBAL_RNG` as of Julia 1.6. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> uuid4(rng) UUID("7a052949-c101-4ca3-9a7e-43a2532b2fa8") ``` """ function uuid4(rng::AbstractRNG=Random.RandomDevice()) u = rand(rng, UInt128) u &= 0xffffffffffff0fff3fffffffffffffff u |= 0x00000000000040008000000000000000 UUID(u) end """ uuid5(ns::UUID, name::String) -> UUID Generates a version 5 (namespace and domain-based) universally unique identifier (UUID), as specified by RFC 4122. !!! compat "Julia 1.1" This function requires at least Julia 1.1. # Examples ```jldoctest julia> rng = MersenneTwister(1234); julia> u4 = uuid4(rng) UUID("7a052949-c101-4ca3-9a7e-43a2532b2fa8") julia> u5 = uuid5(u4, "julia") UUID("086cc5bb-2461-57d8-8068-0aed7f5b5cd1") ``` """ function uuid5(ns::UUID, name::String) nsbytes = zeros(UInt8, 16) nsv = ns.value for idx in Base.OneTo(16) nsbytes[idx] = nsv >> 120 nsv = nsv << 8 end hash_result = SHA.sha1(append!(nsbytes, convert(Vector{UInt8}, codeunits(unescape_string(name))))) # set version number to 5 hash_result[7] = (hash_result[7] & 0x0F) | (0x50) hash_result[9] = (hash_result[9] & 0x3F) | (0x80) v = zero(UInt128) #use only the first 16 bytes of the SHA1 hash for idx in Base.OneTo(16) v = (v << 0x08) | hash_result[idx] end return UUID(v) end end l���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jlึ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ Run Evaluate Print Loop (REPL) Example minimal code ```julia import REPL term = REPL.Terminals.TTYTerminal("dumb", stdin, stdout, stderr) repl = REPL.LineEditREPL(term, true) REPL.run_repl(repl) ``` """ module REPL Base.Experimental.@optlevel 1 Base.Experimental.@max_methods 1 using Base.Meta, Sockets import InteractiveUtils export AbstractREPL, BasicREPL, LineEditREPL, StreamREPL import Base: AbstractDisplay, display, show, AnyDict, == _displaysize(io::IO) = displaysize(io)::Tuple{Int,Int} include("Terminals.jl") using .Terminals abstract type AbstractREPL end include("options.jl") include("LineEdit.jl") using .LineEdit import ..LineEdit: CompletionProvider, HistoryProvider, add_history, complete_line, history_next, history_next_prefix, history_prev, history_prev_prefix, history_first, history_last, history_search, accept_result, setmodifiers!, terminal, MIState, PromptState, TextInterface, mode_idx include("REPLCompletions.jl") using .REPLCompletions include("TerminalMenus/TerminalMenus.jl") include("docview.jl") @nospecialize # use only declared type signatures answer_color(::AbstractREPL) = "" const JULIA_PROMPT = "julia> " const PKG_PROMPT = "pkg> " const SHELL_PROMPT = "shell> " const HELP_PROMPT = "help?> " mutable struct REPLBackend "channel for AST" repl_channel::Channel{Any} "channel for results: (value, iserror)" response_channel::Channel{Any} "flag indicating the state of this backend" in_eval::Bool "transformation functions to apply before evaluating expressions" ast_transforms::Vector{Any} "current backend task" backend_task::Task REPLBackend(repl_channel, response_channel, in_eval, ast_transforms=copy(repl_ast_transforms)) = new(repl_channel, response_channel, in_eval, ast_transforms) end REPLBackend() = REPLBackend(Channel(1), Channel(1), false) """ softscope(ex) Return a modified version of the parsed expression `ex` that uses the REPL's "soft" scoping rules for global syntax blocks. """ function softscope(@nospecialize ex) if ex isa Expr h = ex.head if h === :toplevel exโ€ฒ = Expr(h) map!(softscope, resize!(exโ€ฒ.args, length(ex.args)), ex.args) return exโ€ฒ elseif h in (:meta, :import, :using, :export, :module, :error, :incomplete, :thunk) return ex elseif h === :global && all(x->isa(x, Symbol), ex.args) return ex else return Expr(:block, Expr(:softscope, true), ex) end end return ex end # Temporary alias until Documenter updates const softscope! = softscope const repl_ast_transforms = Any[softscope] # defaults for new REPL backends # Allows an external package to add hooks into the code loading. # The hook should take a Vector{Symbol} of package names and # return true if all packages could be installed, false if not # to e.g. install packages on demand const install_packages_hooks = Any[] function eval_user_input(@nospecialize(ast), backend::REPLBackend, mod::Module) lasterr = nothing Base.sigatomic_begin() while true try Base.sigatomic_end() if lasterr !== nothing put!(backend.response_channel, Pair{Any, Bool}(lasterr, true)) else backend.in_eval = true if !isempty(install_packages_hooks) check_for_missing_packages_and_run_hooks(ast) end for xf in backend.ast_transforms ast = Base.invokelatest(xf, ast) end value = Core.eval(mod, ast) backend.in_eval = false setglobal!(Base.MainInclude, :ans, value) put!(backend.response_channel, Pair{Any, Bool}(value, false)) end break catch err if lasterr !== nothing println("SYSTEM ERROR: Failed to report error to REPL frontend") println(err) end lasterr = current_exceptions() end end Base.sigatomic_end() nothing end function check_for_missing_packages_and_run_hooks(ast) isa(ast, Expr) || return mods = modules_to_be_loaded(ast) filter!(mod -> isnothing(Base.identify_package(String(mod))), mods) # keep missing modules if !isempty(mods) for f in install_packages_hooks Base.invokelatest(f, mods) && return end end end function modules_to_be_loaded(ast::Expr, mods::Vector{Symbol} = Symbol[]) ast.head === :quote && return mods # don't search if it's not going to be run during this eval if ast.head === :using || ast.head === :import for arg in ast.args arg = arg::Expr arg1 = first(arg.args) if arg1 isa Symbol # i.e. `Foo` if arg1 != :. # don't include local imports push!(mods, arg1) end else # i.e. `Foo: bar` push!(mods, first((arg1::Expr).args)) end end end for arg in ast.args if isexpr(arg, (:block, :if, :using, :import)) modules_to_be_loaded(arg, mods) end end filter!(mod -> !in(String(mod), ["Base", "Main", "Core"]), mods) # Exclude special non-package modules return unique(mods) end """ start_repl_backend(repl_channel::Channel, response_channel::Channel) Starts loop for REPL backend Returns a REPLBackend with backend_task assigned Deprecated since sync / async behavior cannot be selected """ function start_repl_backend(repl_channel::Channel{Any}, response_channel::Channel{Any} ; get_module::Function = ()->Main) # Maintain legacy behavior of asynchronous backend backend = REPLBackend(repl_channel, response_channel, false) # Assignment will be made twice, but will be immediately available backend.backend_task = @async start_repl_backend(backend; get_module) return backend end """ start_repl_backend(backend::REPLBackend) Call directly to run backend loop on current Task. Use @async for run backend on new Task. Does not return backend until loop is finished. """ function start_repl_backend(backend::REPLBackend, @nospecialize(consumer = x -> nothing); get_module::Function = ()->Main) backend.backend_task = Base.current_task() consumer(backend) repl_backend_loop(backend, get_module) return backend end function repl_backend_loop(backend::REPLBackend, get_module::Function) # include looks at this to determine the relative include path # nothing means cwd while true tls = task_local_storage() tls[:SOURCE_PATH] = nothing ast, show_value = take!(backend.repl_channel) if show_value == -1 # exit flag break end eval_user_input(ast, backend, get_module()) end return nothing end struct REPLDisplay{R<:AbstractREPL} <: AbstractDisplay repl::R end ==(a::REPLDisplay, b::REPLDisplay) = a.repl === b.repl function display(d::REPLDisplay, mime::MIME"text/plain", x) x = Ref{Any}(x) with_repl_linfo(d.repl) do io io = IOContext(io, :limit => true, :module => active_module(d)::Module) if d.repl isa LineEditREPL mistate = d.repl.mistate mode = LineEdit.mode(mistate) if mode isa LineEdit.Prompt LineEdit.write_output_prefix(io, mode, get(io, :color, false)::Bool) end end get(io, :color, false)::Bool && write(io, answer_color(d.repl)) if isdefined(d.repl, :options) && isdefined(d.repl.options, :iocontext) # this can override the :limit property set initially io = foldl(IOContext, d.repl.options.iocontext, init=io) end show(io, mime, x[]) println(io) end return nothing end display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) function print_response(repl::AbstractREPL, response, show_value::Bool, have_color::Bool) repl.waserror = response[2] with_repl_linfo(repl) do io io = IOContext(io, :module => active_module(repl)::Module) print_response(io, response, show_value, have_color, specialdisplay(repl)) end return nothing end function repl_display_error(errio::IO, @nospecialize errval) # this will be set to true if types in the stacktrace are truncated limitflag = Ref(false) errio = IOContext(errio, :stacktrace_types_limited => limitflag) Base.invokelatest(Base.display_error, errio, errval) if limitflag[] print(errio, "Some type information was truncated. Use `show(err)` to see complete types.") println(errio) end return nothing end function print_response(errio::IO, response, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing) Base.sigatomic_begin() val, iserr = response while true try Base.sigatomic_end() if iserr val = Base.scrub_repl_backtrace(val) Base.istrivialerror(val) || setglobal!(Base.MainInclude, :err, val) repl_display_error(errio, val) else if val !== nothing && show_value try if specialdisplay === nothing Base.invokelatest(display, val) else Base.invokelatest(display, specialdisplay, val) end catch println(errio, "Error showing value of type ", typeof(val), ":") rethrow() end end end break catch ex if iserr println(errio) # an error during printing is likely to leave us mid-line println(errio, "SYSTEM (REPL): showing an error caused an error") try excs = Base.scrub_repl_backtrace(current_exceptions()) setglobal!(Base.MainInclude, :err, excs) repl_display_error(errio, excs) catch e # at this point, only print the name of the type as a Symbol to # minimize the possibility of further errors. println(errio) println(errio, "SYSTEM (REPL): caught exception of type ", typeof(e).name.name, " while trying to handle a nested exception; giving up") end break end val = current_exceptions() iserr = true end end Base.sigatomic_end() nothing end # A reference to a backend that is not mutable struct REPLBackendRef repl_channel::Channel{Any} response_channel::Channel{Any} end REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel) function destroy(ref::REPLBackendRef, state::Task) if istaskfailed(state) close(ref.repl_channel, TaskFailedException(state)) close(ref.response_channel, TaskFailedException(state)) end close(ref.repl_channel) close(ref.response_channel) end """ run_repl(repl::AbstractREPL) run_repl(repl, consumer = backend->nothing; backend_on_current_task = true) Main function to start the REPL consumer is an optional function that takes a REPLBackend as an argument """ function run_repl(repl::AbstractREPL, @nospecialize(consumer = x -> nothing); backend_on_current_task::Bool = true, backend = REPLBackend()) backend_ref = REPLBackendRef(backend) cleanup = @task try destroy(backend_ref, t) catch e Core.print(Core.stderr, "\nINTERNAL ERROR: ") Core.println(Core.stderr, e) Core.println(Core.stderr, catch_backtrace()) end get_module = () -> active_module(repl) if backend_on_current_task t = @async run_frontend(repl, backend_ref) errormonitor(t) Base._wait2(t, cleanup) start_repl_backend(backend, consumer; get_module) else t = @async start_repl_backend(backend, consumer; get_module) errormonitor(t) Base._wait2(t, cleanup) run_frontend(repl, backend_ref) end return backend end ## BasicREPL ## mutable struct BasicREPL <: AbstractREPL terminal::TextTerminal waserror::Bool frontend_task::Task BasicREPL(t) = new(t, false) end outstream(r::BasicREPL) = r.terminal hascolor(r::BasicREPL) = hascolor(r.terminal) function run_frontend(repl::BasicREPL, backend::REPLBackendRef) repl.frontend_task = current_task() d = REPLDisplay(repl) dopushdisplay = !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) hit_eof = false while true Base.reseteof(repl.terminal) write(repl.terminal, JULIA_PROMPT) line = "" ast = nothing interrupted = false while true try line *= readline(repl.terminal, keep=true) catch e if isa(e,InterruptException) try # raise the debugger if present ccall(:jl_raise_debugger, Int, ()) catch end line = "" interrupted = true break elseif isa(e,EOFError) hit_eof = true break else rethrow() end end ast = Base.parse_input_line(line) (isa(ast,Expr) && ast.head === :incomplete) || break end if !isempty(line) response = eval_with_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), false) end write(repl.terminal, '\n') ((!interrupted && isempty(line)) || hit_eof) && break end # terminate backend put!(backend.repl_channel, (nothing, -1)) dopushdisplay && popdisplay(d) nothing end ## LineEditREPL ## mutable struct LineEditREPL <: AbstractREPL t::TextTerminal hascolor::Bool prompt_color::String input_color::String answer_color::String shell_color::String help_color::String history_file::Bool in_shell::Bool in_help::Bool envcolors::Bool waserror::Bool specialdisplay::Union{Nothing,AbstractDisplay} options::Options mistate::Union{MIState,Nothing} last_shown_line_infos::Vector{Tuple{String,Int}} interface::ModalInterface backendref::REPLBackendRef frontend_task::Task function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) opts = Options() opts.hascolor = hascolor if !hascolor opts.beep_colors = [""] end new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) end end outstream(r::LineEditREPL) = (t = r.t; t isa TTYTerminal ? t.out_stream : t) specialdisplay(r::LineEditREPL) = r.specialdisplay specialdisplay(r::AbstractREPL) = nothing terminal(r::LineEditREPL) = r.t hascolor(r::LineEditREPL) = r.hascolor LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) = LineEditREPL(t, hascolor, hascolor ? Base.text_colors[:green] : "", hascolor ? Base.input_color() : "", hascolor ? Base.answer_color() : "", hascolor ? Base.text_colors[:red] : "", hascolor ? Base.text_colors[:yellow] : "", false, false, false, envcolors ) mutable struct REPLCompletionProvider <: CompletionProvider modifiers::LineEdit.Modifiers end REPLCompletionProvider() = REPLCompletionProvider(LineEdit.Modifiers()) mutable struct ShellCompletionProvider <: CompletionProvider end struct LatexCompletions <: CompletionProvider end function active_module() # this method is also called from Base isdefined(Base, :active_repl) || return Main return active_module(Base.active_repl::AbstractREPL) end active_module((; mistate)::LineEditREPL) = mistate === nothing ? Main : mistate.active_module active_module(::AbstractREPL) = Main active_module(d::REPLDisplay) = active_module(d.repl) setmodifiers!(c::CompletionProvider, m::LineEdit.Modifiers) = nothing setmodifiers!(c::REPLCompletionProvider, m::LineEdit.Modifiers) = c.modifiers = m """ activate(mod::Module=Main) Set `mod` as the default contextual module in the REPL, both for evaluating expressions and printing them. """ function activate(mod::Module=Main) mistate = (Base.active_repl::LineEditREPL).mistate mistate === nothing && return nothing mistate.active_module = mod Base.load_InteractiveUtils(mod) return nothing end beforecursor(buf::IOBuffer) = String(buf.data[1:buf.ptr-1]) function complete_line(c::REPLCompletionProvider, s::PromptState, mod::Module) partial = beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = completions(full, lastindex(partial), mod, c.modifiers.shift) c.modifiers = LineEdit.Modifiers() return unique!(map(completion_text, ret)), partial[range], should_complete end function complete_line(c::ShellCompletionProvider, s::PromptState) # First parse everything up to the current position partial = beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = shell_completions(full, lastindex(partial)) return unique!(map(completion_text, ret)), partial[range], should_complete end function complete_line(c::LatexCompletions, s) partial = beforecursor(LineEdit.buffer(s)) full = LineEdit.input_string(s)::String ret, range, should_complete = bslash_completions(full, lastindex(partial))[2] return unique!(map(completion_text, ret)), partial[range], should_complete end with_repl_linfo(f, repl) = f(outstream(repl)) function with_repl_linfo(f, repl::LineEditREPL) linfos = Tuple{String,Int}[] io = IOContext(outstream(repl), :last_shown_line_infos => linfos) f(io) if !isempty(linfos) repl.last_shown_line_infos = linfos end nothing end mutable struct REPLHistoryProvider <: HistoryProvider history::Vector{String} file_path::String history_file::Union{Nothing,IO} start_idx::Int cur_idx::Int last_idx::Int last_buffer::IOBuffer last_mode::Union{Nothing,Prompt} mode_mapping::Dict{Symbol,Prompt} modes::Vector{Symbol} end REPLHistoryProvider(mode_mapping::Dict{Symbol}) = REPLHistoryProvider(String[], "", nothing, 0, 0, -1, IOBuffer(), nothing, mode_mapping, UInt8[]) invalid_history_message(path::String) = """ Invalid history file ($path) format: If you have a history file left over from an older version of Julia, try renaming or deleting it. Invalid character: """ munged_history_message(path::String) = """ Invalid history file ($path) format: An editor may have converted tabs to spaces at line """ function hist_open_file(hp::REPLHistoryProvider) f = open(hp.file_path, read=true, write=true, create=true) hp.history_file = f seekend(f) end function hist_from_file(hp::REPLHistoryProvider, path::String) getline(lines, i) = i > length(lines) ? "" : lines[i] file_lines = readlines(path) countlines = 0 while true # First parse the metadata that starts with '#' in particular the REPL mode countlines += 1 line = getline(file_lines, countlines) mode = :julia isempty(line) && break line[1] != '#' && error(invalid_history_message(path), repr(line[1]), " at line ", countlines) while !isempty(line) startswith(line, '#') || break if startswith(line, "# mode: ") mode = Symbol(SubString(line, 9)) end countlines += 1 line = getline(file_lines, countlines) end isempty(line) && break # Now parse the code for the current REPL mode line[1] == ' ' && error(munged_history_message(path), countlines) line[1] != '\t' && error(invalid_history_message(path), repr(line[1]), " at line ", countlines) lines = String[] while !isempty(line) push!(lines, chomp(SubString(line, 2))) next_line = getline(file_lines, countlines+1) isempty(next_line) && break first(next_line) == ' ' && error(munged_history_message(path), countlines) # A line not starting with a tab means we are done with code for this entry first(next_line) != '\t' && break countlines += 1 line = getline(file_lines, countlines) end push!(hp.modes, mode) push!(hp.history, join(lines, '\n')) end hp.start_idx = length(hp.history) return hp end function add_history(hist::REPLHistoryProvider, s::PromptState) str = rstrip(String(take!(copy(s.input_buffer)))) isempty(strip(str)) && return mode = mode_idx(hist, LineEdit.mode(s)) !isempty(hist.history) && isequal(mode, hist.modes[end]) && str == hist.history[end] && return push!(hist.modes, mode) push!(hist.history, str) hist.history_file === nothing && return entry = """ # time: $(Libc.strftime("%Y-%m-%d %H:%M:%S %Z", time())) # mode: $mode $(replace(str, r"^"ms => "\t")) """ # TODO: write-lock history file try seekend(hist.history_file) catch err (err isa SystemError) || rethrow() # File handle might get stale after a while, especially under network file systems # If this doesn't fix it (e.g. when file is deleted), we'll end up rethrowing anyway hist_open_file(hist) end print(hist.history_file, entry) flush(hist.history_file) nothing end function history_move(s::Union{LineEdit.MIState,LineEdit.PrefixSearchState}, hist::REPLHistoryProvider, idx::Int, save_idx::Int = hist.cur_idx) max_idx = length(hist.history) + 1 @assert 1 <= hist.cur_idx <= max_idx (1 <= idx <= max_idx) || return :none idx != hist.cur_idx || return :none # save the current line if save_idx == max_idx hist.last_mode = LineEdit.mode(s) hist.last_buffer = copy(LineEdit.buffer(s)) else hist.history[save_idx] = LineEdit.input_string(s) hist.modes[save_idx] = mode_idx(hist, LineEdit.mode(s)) end # load the saved line if idx == max_idx last_buffer = hist.last_buffer LineEdit.transition(s, hist.last_mode) do LineEdit.replace_line(s, last_buffer) end hist.last_mode = nothing hist.last_buffer = IOBuffer() else if haskey(hist.mode_mapping, hist.modes[idx]) LineEdit.transition(s, hist.mode_mapping[hist.modes[idx]]) do LineEdit.replace_line(s, hist.history[idx]) end else return :skip end end hist.cur_idx = idx return :ok end # REPL History can also transitions modes function LineEdit.accept_result_newmode(hist::REPLHistoryProvider) if 1 <= hist.cur_idx <= length(hist.modes) return hist.mode_mapping[hist.modes[hist.cur_idx]] end return nothing end function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider, num::Int=1, save_idx::Int = hist.cur_idx) num <= 0 && return history_next(s, hist, -num, save_idx) hist.last_idx = -1 m = history_move(s, hist, hist.cur_idx-num, save_idx) if m === :ok LineEdit.move_input_start(s) LineEdit.reset_key_repeats(s) do LineEdit.move_line_end(s) end return LineEdit.refresh_line(s) elseif m === :skip return history_prev(s, hist, num+1, save_idx) else return Terminals.beep(s) end end function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, num::Int=1, save_idx::Int = hist.cur_idx) if num == 0 Terminals.beep(s) return end num < 0 && return history_prev(s, hist, -num, save_idx) cur_idx = hist.cur_idx max_idx = length(hist.history) + 1 if cur_idx == max_idx && 0 < hist.last_idx # issue #6312 cur_idx = hist.last_idx hist.last_idx = -1 end m = history_move(s, hist, cur_idx+num, save_idx) if m === :ok LineEdit.move_input_end(s) return LineEdit.refresh_line(s) elseif m === :skip return history_next(s, hist, num+1, save_idx) else return Terminals.beep(s) end end history_first(s::LineEdit.MIState, hist::REPLHistoryProvider) = history_prev(s, hist, hist.cur_idx - 1 - (hist.cur_idx > hist.start_idx+1 ? hist.start_idx : 0)) history_last(s::LineEdit.MIState, hist::REPLHistoryProvider) = history_next(s, hist, length(hist.history) - hist.cur_idx + 1) function history_move_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString, backwards::Bool, cur_idx::Int = hist.cur_idx) cur_response = String(take!(copy(LineEdit.buffer(s)))) # when searching forward, start at last_idx if !backwards && hist.last_idx > 0 cur_idx = hist.last_idx end hist.last_idx = -1 max_idx = length(hist.history)+1 idxs = backwards ? ((cur_idx-1):-1:1) : ((cur_idx+1):1:max_idx) for idx in idxs if (idx == max_idx) || (startswith(hist.history[idx], prefix) && (hist.history[idx] != cur_response || get(hist.mode_mapping, hist.modes[idx], nothing) !== LineEdit.mode(s))) m = history_move(s, hist, idx) if m === :ok if idx == max_idx # on resuming the in-progress edit, leave the cursor where the user last had it elseif isempty(prefix) # on empty prefix search, move cursor to the end LineEdit.move_input_end(s) else # otherwise, keep cursor at the prefix position as a visual cue seek(LineEdit.buffer(s), sizeof(prefix)) end LineEdit.refresh_line(s) return :ok elseif m === :skip return history_move_prefix(s,hist,prefix,backwards,idx) end end end Terminals.beep(s) nothing end history_next_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = history_move_prefix(s, hist, prefix, false) history_prev_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = history_move_prefix(s, hist, prefix, true) function history_search(hist::REPLHistoryProvider, query_buffer::IOBuffer, response_buffer::IOBuffer, backwards::Bool=false, skip_current::Bool=false) qpos = position(query_buffer) qpos > 0 || return true searchdata = beforecursor(query_buffer) response_str = String(take!(copy(response_buffer))) # Alright, first try to see if the current match still works a = position(response_buffer) + 1 # position is zero-indexed # FIXME: I'm pretty sure this is broken since it uses an index # into the search data to index into the response string b = a + sizeof(searchdata) b = b โ‰ค ncodeunits(response_str) ? prevind(response_str, b) : b-1 b = min(lastindex(response_str), b) # ensure that b is valid searchstart = backwards ? b : a if searchdata == response_str[a:b] if skip_current searchstart = backwards ? prevind(response_str, b) : nextind(response_str, a) else return true end end # Start searching # First the current response buffer if 1 <= searchstart <= lastindex(response_str) match = backwards ? findprev(searchdata, response_str, searchstart) : findnext(searchdata, response_str, searchstart) if match !== nothing seek(response_buffer, first(match) - 1) return true end end # Now search all the other buffers idxs = backwards ? ((hist.cur_idx-1):-1:1) : ((hist.cur_idx+1):1:length(hist.history)) for idx in idxs h = hist.history[idx] match = backwards ? findlast(searchdata, h) : findfirst(searchdata, h) if match !== nothing && h != response_str && haskey(hist.mode_mapping, hist.modes[idx]) truncate(response_buffer, 0) write(response_buffer, h) seek(response_buffer, first(match) - 1) hist.cur_idx = idx return true end end return false end function history_reset_state(hist::REPLHistoryProvider) if hist.cur_idx != length(hist.history) + 1 hist.last_idx = hist.cur_idx hist.cur_idx = length(hist.history) + 1 end nothing end LineEdit.reset_state(hist::REPLHistoryProvider) = history_reset_state(hist) function return_callback(s) ast = Base.parse_input_line(String(take!(copy(LineEdit.buffer(s)))), depwarn=false) return !(isa(ast, Expr) && ast.head === :incomplete) end find_hist_file() = get(ENV, "JULIA_HISTORY", !isempty(DEPOT_PATH) ? joinpath(DEPOT_PATH[1], "logs", "repl_history.jl") : error("DEPOT_PATH is empty and and ENV[\"JULIA_HISTORY\"] not set.")) backend(r::AbstractREPL) = r.backendref function eval_with_backend(ast, backend::REPLBackendRef) put!(backend.repl_channel, (ast, 1)) return take!(backend.response_channel) # (val, iserr) end function respond(f, repl, main; pass_empty::Bool = false, suppress_on_semicolon::Bool = true) return function do_respond(s::MIState, buf, ok::Bool) if !ok return transition(s, :abort) end line = String(take!(buf)::Vector{UInt8}) if !isempty(line) || pass_empty reset(repl) local response try ast = Base.invokelatest(f, line) response = eval_with_backend(ast, backend(repl)) catch response = Pair{Any, Bool}(current_exceptions(), true) end hide_output = suppress_on_semicolon && ends_with_semicolon(line) print_response(repl, response, !hide_output, hascolor(repl)) end prepare_next(repl) reset_state(s) return s.current_mode.sticky ? true : transition(s, main) end end function reset(repl::LineEditREPL) raw!(repl.t, false) hascolor(repl) && print(repl.t, Base.text_colors[:normal]) nothing end function prepare_next(repl::LineEditREPL) println(terminal(repl)) end function mode_keymap(julia_prompt::Prompt) AnyDict( '\b' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) transition(s, julia_prompt) do LineEdit.state(s, julia_prompt).input_buffer = buf end else LineEdit.edit_backspace(s) end end, "^C" => function (s::MIState,o...) LineEdit.move_input_end(s) LineEdit.refresh_line(s) print(LineEdit.terminal(s), "^C\n\n") transition(s, julia_prompt) transition(s, :reset) LineEdit.refresh_line(s) end) end repl_filename(repl, hp::REPLHistoryProvider) = "REPL[$(max(length(hp.history)-hp.start_idx, 1))]" repl_filename(repl, hp) = "REPL" const JL_PROMPT_PASTE = Ref(true) enable_promptpaste(v::Bool) = JL_PROMPT_PASTE[] = v function contextual_prompt(repl::LineEditREPL, prompt::Union{String,Function}) function () mod = active_module(repl) prefix = mod == Main ? "" : string('(', mod, ") ") pr = prompt isa String ? prompt : prompt() prefix * pr end end setup_interface( repl::LineEditREPL; # those keyword arguments may be deprecated eventually in favor of the Options mechanism hascolor::Bool = repl.options.hascolor, extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) # This non keyword method can be precompiled which is important function setup_interface( repl::LineEditREPL, hascolor::Bool, extra_repl_keymap::Any, # Union{Dict,Vector{<:Dict}}, ) # The precompile statement emitter has problem outputting valid syntax for the # type of `Union{Dict,Vector{<:Dict}}` (see #28808). # This function is however important to precompile for REPL startup time, therefore, # make the type Any and just assert that we have the correct type below. @assert extra_repl_keymap isa Union{Dict,Vector{<:Dict}} ### # # This function returns the main interface that describes the REPL # functionality, it is called internally by functions that setup a # Terminal-based REPL frontend. # # See run_frontend(repl::LineEditREPL, backend::REPLBackendRef) # for usage # ### ### # We setup the interface in two stages. # First, we set up all components (prompt,rsearch,shell,help) # Second, we create keymaps with appropriate transitions between them # and assign them to the components # ### ############################### Stage I ################################ # This will provide completions for REPL and help mode replc = REPLCompletionProvider() # Set up the main Julia prompt julia_prompt = Prompt(contextual_prompt(repl, JULIA_PROMPT); # Copy colors from the prompt object prompt_prefix = hascolor ? repl.prompt_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, on_enter = return_callback) # Setup help mode help_mode = Prompt(contextual_prompt(repl, "help?> "), prompt_prefix = hascolor ? repl.help_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = replc, # When we're done transform the entered line into a call to helpmode function on_done = respond(line::String->helpmode(outstream(repl), line, repl.mistate.active_module), repl, julia_prompt, pass_empty=true, suppress_on_semicolon=false)) # Set up shell mode shell_mode = Prompt(SHELL_PROMPT; prompt_prefix = hascolor ? repl.shell_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", repl = repl, complete = ShellCompletionProvider(), # Transform "foo bar baz" into `foo bar baz` (shell quoting) # and pass into Base.repl_cmd for processing (handles `ls` and `cd` # special) on_done = respond(repl, julia_prompt) do line Expr(:call, :(Base.repl_cmd), :(Base.cmd_gen($(Base.shell_parse(line::String)[1]))), outstream(repl)) end, sticky = true) ################################# Stage II ############################# # Setup history # We will have a unified history for all REPL modes hp = REPLHistoryProvider(Dict{Symbol,Prompt}(:julia => julia_prompt, :shell => shell_mode, :help => help_mode)) if repl.history_file try hist_path = find_hist_file() mkpath(dirname(hist_path)) hp.file_path = hist_path hist_open_file(hp) finalizer(replc) do replc close(hp.history_file) end hist_from_file(hp, hist_path) catch # use REPL.hascolor to avoid using the local variable with the same name print_response(repl, Pair{Any, Bool}(current_exceptions(), true), true, REPL.hascolor(repl)) println(outstream(repl)) @info "Disabling history file for this session" repl.history_file = false end end history_reset_state(hp) julia_prompt.hist = hp shell_mode.hist = hp help_mode.hist = hp julia_prompt.on_done = respond(x->Base.parse_input_line(x,filename=repl_filename(repl,hp)), repl, julia_prompt) search_prompt, skeymap = LineEdit.setup_search_keymap(hp) search_prompt.complete = LatexCompletions() shell_prompt_len = length(SHELL_PROMPT) help_prompt_len = length(HELP_PROMPT) jl_prompt_regex = r"^In \[[0-9]+\]: |^(?:\(.+\) )?julia> " pkg_prompt_regex = r"^(?:\(.+\) )?pkg> " # Canonicalize user keymap input if isa(extra_repl_keymap, Dict) extra_repl_keymap = AnyDict[extra_repl_keymap] end repl_keymap = AnyDict( ';' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) transition(s, shell_mode) do LineEdit.state(s, shell_mode).input_buffer = buf end else edit_insert(s, ';') end end, '?' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) transition(s, help_mode) do LineEdit.state(s, help_mode).input_buffer = buf end else edit_insert(s, '?') end end, # Bracketed Paste Mode "\e[200~" => (s::MIState,o...)->begin input = LineEdit.bracketed_paste(s) # read directly from s until reaching the end-bracketed-paste marker sbuffer = LineEdit.buffer(s) curspos = position(sbuffer) seek(sbuffer, 0) shouldeval = (bytesavailable(sbuffer) == curspos && !occursin(UInt8('\n'), sbuffer)) seek(sbuffer, curspos) if curspos == 0 # if pasting at the beginning, strip leading whitespace input = lstrip(input) end if !shouldeval # when pasting in the middle of input, just paste in place # don't try to execute all the WIP, since that's rather confusing # and is often ill-defined how it should behave edit_insert(s, input) return end LineEdit.push_undo(s) edit_insert(sbuffer, input) input = String(take!(sbuffer)) oldpos = firstindex(input) firstline = true isprompt_paste = false curr_prompt_len = 0 pasting_help = false while oldpos <= lastindex(input) # loop until all lines have been executed if JL_PROMPT_PASTE[] # Check if the next statement starts with a prompt i.e. "julia> ", in that case # skip it. But first skip whitespace unless pasting in a docstring which may have # indented prompt examples that we don't want to execute while input[oldpos] in (pasting_help ? ('\n') : ('\n', ' ', '\t')) oldpos = nextind(input, oldpos) oldpos >= sizeof(input) && return end substr = SubString(input, oldpos) # Check if input line starts with "julia> ", remove it if we are in prompt paste mode if (firstline || isprompt_paste) && startswith(substr, jl_prompt_regex) detected_jl_prompt = match(jl_prompt_regex, substr).match isprompt_paste = true curr_prompt_len = sizeof(detected_jl_prompt) oldpos += curr_prompt_len transition(s, julia_prompt) pasting_help = false # Check if input line starts with "pkg> " or "(...) pkg> ", remove it if we are in prompt paste mode and switch mode elseif (firstline || isprompt_paste) && startswith(substr, pkg_prompt_regex) detected_pkg_prompt = match(pkg_prompt_regex, substr).match isprompt_paste = true curr_prompt_len = sizeof(detected_pkg_prompt) oldpos += curr_prompt_len Base.active_repl.interface.modes[1].keymap_dict[']'](s, o...) pasting_help = false # Check if input line starts with "shell> ", remove it if we are in prompt paste mode and switch mode elseif (firstline || isprompt_paste) && startswith(substr, SHELL_PROMPT) isprompt_paste = true oldpos += shell_prompt_len curr_prompt_len = shell_prompt_len transition(s, shell_mode) pasting_help = false # Check if input line starts with "help?> ", remove it if we are in prompt paste mode and switch mode elseif (firstline || isprompt_paste) && startswith(substr, HELP_PROMPT) isprompt_paste = true oldpos += help_prompt_len curr_prompt_len = help_prompt_len transition(s, help_mode) pasting_help = true # If we are prompt pasting and current statement does not begin with a mode prefix, skip to next line elseif isprompt_paste while input[oldpos] != '\n' oldpos = nextind(input, oldpos) oldpos >= sizeof(input) && return end continue end end dump_tail = false nl_pos = findfirst('\n', input[oldpos:end]) if s.current_mode == julia_prompt ast, pos = Meta.parse(input, oldpos, raise=false, depwarn=false) if (isa(ast, Expr) && (ast.head === :error || ast.head === :incomplete)) || (pos > ncodeunits(input) && !endswith(input, '\n')) # remaining text is incomplete (an error, or parser ran to the end but didn't stop with a newline): # Insert all the remaining text as one line (might be empty) dump_tail = true end elseif isnothing(nl_pos) # no newline at end, so just dump the tail into the prompt and don't execute dump_tail = true elseif s.current_mode == shell_mode # handle multiline shell commands lines = split(input[oldpos:end], '\n') pos = oldpos + sizeof(lines[1]) + 1 if length(lines) > 1 for line in lines[2:end] # to be recognized as a multiline shell command, the lines must be indented to the # same prompt position if !startswith(line, ' '^curr_prompt_len) break end pos += sizeof(line) + 1 end end else pos = oldpos + nl_pos end if dump_tail tail = input[oldpos:end] if !firstline # strip leading whitespace, but only if it was the result of executing something # (avoids modifying the user's current leading wip line) tail = lstrip(tail) end if isprompt_paste # remove indentation spaces corresponding to the prompt tail = replace(tail, r"^"m * ' '^curr_prompt_len => "") end LineEdit.replace_line(s, tail, true) LineEdit.refresh_line(s) break end # get the line and strip leading and trailing whitespace line = strip(input[oldpos:prevind(input, pos)]) if !isempty(line) if isprompt_paste # remove indentation spaces corresponding to the prompt line = replace(line, r"^"m * ' '^curr_prompt_len => "") end # put the line on the screen and history LineEdit.replace_line(s, line) LineEdit.commit_line(s) # execute the statement terminal = LineEdit.terminal(s) # This is slightly ugly but ok for now raw!(terminal, false) && disable_bracketed_paste(terminal) LineEdit.mode(s).on_done(s, LineEdit.buffer(s), true) raw!(terminal, true) && enable_bracketed_paste(terminal) LineEdit.push_undo(s) # when the last line is incomplete end oldpos = pos firstline = false end end, # Open the editor at the location of a stackframe or method # This is accessing a contextual variable that gets set in # the show_backtrace and show_method_table functions. "^Q" => (s::MIState, o...) -> begin linfos = repl.last_shown_line_infos str = String(take!(LineEdit.buffer(s))) n = tryparse(Int, str) n === nothing && @goto writeback if n <= 0 || n > length(linfos) || startswith(linfos[n][1], "REPL[") @goto writeback end try InteractiveUtils.edit(Base.fixup_stdlib_path(linfos[n][1]), linfos[n][2]) catch ex ex isa ProcessFailedException || ex isa Base.IOError || ex isa SystemError || rethrow() @info "edit failed" _exception=ex end LineEdit.refresh_line(s) return @label writeback write(LineEdit.buffer(s), str) return end, ) prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, julia_prompt) a = Dict{Any,Any}[skeymap, repl_keymap, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(a, extra_repl_keymap) julia_prompt.keymap_dict = LineEdit.keymap(a) mk = mode_keymap(julia_prompt) b = Dict{Any,Any}[skeymap, mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(b, extra_repl_keymap) shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt] return ModalInterface(allprompts) end function run_frontend(repl::LineEditREPL, backend::REPLBackendRef) repl.frontend_task = current_task() d = REPLDisplay(repl) dopushdisplay = repl.specialdisplay === nothing && !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) if !isdefined(repl,:interface) interface = repl.interface = setup_interface(repl) else interface = repl.interface end repl.backendref = backend repl.mistate = LineEdit.init_state(terminal(repl), interface) run_interface(terminal(repl), interface, repl.mistate) # Terminate Backend put!(backend.repl_channel, (nothing, -1)) dopushdisplay && popdisplay(d) nothing end ## StreamREPL ## mutable struct StreamREPL <: AbstractREPL stream::IO prompt_color::String input_color::String answer_color::String waserror::Bool frontend_task::Task StreamREPL(stream,pc,ic,ac) = new(stream,pc,ic,ac,false) end StreamREPL(stream::IO) = StreamREPL(stream, Base.text_colors[:green], Base.input_color(), Base.answer_color()) run_repl(stream::IO) = run_repl(StreamREPL(stream)) outstream(s::StreamREPL) = s.stream hascolor(s::StreamREPL) = get(s.stream, :color, false)::Bool answer_color(r::LineEditREPL) = r.envcolors ? Base.answer_color() : r.answer_color answer_color(r::StreamREPL) = r.answer_color input_color(r::LineEditREPL) = r.envcolors ? Base.input_color() : r.input_color input_color(r::StreamREPL) = r.input_color let matchend = Dict("\"" => r"\"", "\"\"\"" => r"\"\"\"", "'" => r"'", "`" => r"`", "```" => r"```", "#" => r"$"m, "#=" => r"=#|#=") global _rm_strings_and_comments function _rm_strings_and_comments(code::Union{String,SubString{String}}) buf = IOBuffer(sizehint = sizeof(code)) pos = 1 while true i = findnext(r"\"(?!\"\")|\"\"\"|'|`(?!``)|```|#(?!=)|#=", code, pos) isnothing(i) && break match = SubString(code, i) j = findnext(matchend[match]::Regex, code, nextind(code, last(i))) if match == "#=" # possibly nested nested = 1 while j !== nothing nested += SubString(code, j) == "#=" ? +1 : -1 iszero(nested) && break j = findnext(r"=#|#=", code, nextind(code, last(j))) end elseif match[1] != '#' # quote match: check non-escaped while j !== nothing notbackslash = findprev(!=('\\'), code, prevind(code, first(j)))::Int isodd(first(j) - notbackslash) && break # not escaped j = findnext(matchend[match]::Regex, code, nextind(code, first(j))) end end isnothing(j) && break if match[1] == '#' print(buf, SubString(code, pos, prevind(code, first(i)))) else print(buf, SubString(code, pos, last(i)), ' ', SubString(code, j)) end pos = nextind(code, last(j)) end print(buf, SubString(code, pos, lastindex(code))) return String(take!(buf)) end end # heuristic function to decide if the presence of a semicolon # at the end of the expression was intended for suppressing output ends_with_semicolon(code::AbstractString) = ends_with_semicolon(String(code)) ends_with_semicolon(code::Union{String,SubString{String}}) = contains(_rm_strings_and_comments(code), r";\s*$") function run_frontend(repl::StreamREPL, backend::REPLBackendRef) repl.frontend_task = current_task() have_color = hascolor(repl) Base.banner(repl.stream) d = REPLDisplay(repl) dopushdisplay = !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) while !eof(repl.stream)::Bool if have_color print(repl.stream,repl.prompt_color) end print(repl.stream, "julia> ") if have_color print(repl.stream, input_color(repl)) end line = readline(repl.stream, keep=true) if !isempty(line) ast = Base.parse_input_line(line) if have_color print(repl.stream, Base.color_normal) end response = eval_with_backend(ast, backend) print_response(repl, response, !ends_with_semicolon(line), have_color) end end # Terminate Backend put!(backend.repl_channel, (nothing, -1)) dopushdisplay && popdisplay(d) nothing end module Numbered using ..REPL __current_ast_transforms() = isdefined(Base, :active_repl_backend) ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms function repl_eval_counter(hp) return length(hp.history) - hp.start_idx end function out_transform(@nospecialize(x), n::Ref{Int}) return Expr(:toplevel, get_usings!([], x)..., quote let __temp_val_a72df459 = $x $capture_result($n, __temp_val_a72df459) __temp_val_a72df459 end end) end function get_usings!(usings, ex) ex isa Expr || return usings # get all `using` and `import` statements which are at the top level for (i, arg) in enumerate(ex.args) if Base.isexpr(arg, :toplevel) get_usings!(usings, arg) elseif Base.isexpr(arg, [:using, :import]) push!(usings, popat!(ex.args, i)) end end return usings end function capture_result(n::Ref{Int}, @nospecialize(x)) n = n[] mod = Base.MainInclude if !isdefined(mod, :Out) @eval mod global Out @eval mod export Out setglobal!(mod, :Out, Dict{Int, Any}()) end if x !== getglobal(mod, :Out) && x !== nothing # remove this? getglobal(mod, :Out)[n] = x end nothing end function set_prompt(repl::LineEditREPL, n::Ref{Int}) julia_prompt = repl.interface.modes[1] julia_prompt.prompt = function() n[] = repl_eval_counter(julia_prompt.hist)+1 string("In [", n[], "]: ") end nothing end function set_output_prefix(repl::LineEditREPL, n::Ref{Int}) julia_prompt = repl.interface.modes[1] if REPL.hascolor(repl) julia_prompt.output_prefix_prefix = Base.text_colors[:red] end julia_prompt.output_prefix = () -> string("Out[", n[], "]: ") nothing end function __current_ast_transforms(backend) if backend === nothing isdefined(Base, :active_repl_backend) ? Base.active_repl_backend.ast_transforms : REPL.repl_ast_transforms else backend.ast_transforms end end function numbered_prompt!(repl::LineEditREPL=Base.active_repl, backend=nothing) n = Ref{Int}(0) set_prompt(repl, n) set_output_prefix(repl, n) push!(__current_ast_transforms(backend), @nospecialize(ast) -> out_transform(ast, n)) return end """ Out[n] A variable referring to all previously computed values, automatically imported to the interactive prompt. Only defined and exists while using [Numbered prompt](@ref Numbered-prompt). See also [`ans`](@ref). """ Base.MainInclude.Out end import .Numbered.numbered_prompt! end # module q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/Terminals.jl������# This file is a part of Julia. License is MIT: https://julialang.org/license module Terminals export AbstractTerminal, TextTerminal, UnixTerminal, TerminalBuffer, TTYTerminal, cmove, cmove_col, cmove_down, cmove_left, cmove_line_down, cmove_line_up, cmove_right, cmove_up, disable_bracketed_paste, enable_bracketed_paste, end_keypad_transmit_mode, getX, getY, hascolor, pos, raw! import Base: check_open, # stream.jl displaysize, flush, pipe_reader, pipe_writer, read, readuntil ## AbstractTerminal: abstract supertype of all terminals ## abstract type AbstractTerminal <: Base.AbstractPipe end ## TextTerminal ## abstract type TextTerminal <: AbstractTerminal end # Terminal interface: pipe_reader(::TextTerminal) = error("Unimplemented") pipe_writer(::TextTerminal) = error("Unimplemented") displaysize(::TextTerminal) = error("Unimplemented") cmove(t::TextTerminal, x, y) = error("Unimplemented") getX(t::TextTerminal) = error("Unimplemented") getY(t::TextTerminal) = error("Unimplemented") pos(t::TextTerminal) = (getX(t), getY(t)) # Relative moves (Absolute position fallbacks) cmove_up(t::TextTerminal, n) = cmove(getX(t), max(1, getY(t)-n)) cmove_up(t) = cmove_up(t, 1) cmove_down(t::TextTerminal, n) = cmove(getX(t), max(height(t), getY(t)+n)) cmove_down(t) = cmove_down(t, 1) cmove_left(t::TextTerminal, n) = cmove(max(1, getX(t)-n), getY(t)) cmove_left(t) = cmove_left(t, 1) cmove_right(t::TextTerminal, n) = cmove(max(width(t), getX(t)+n), getY(t)) cmove_right(t) = cmove_right(t, 1) cmove_line_up(t::TextTerminal, n) = cmove(1, max(1, getY(t)-n)) cmove_line_up(t) = cmove_line_up(t, 1) cmove_line_down(t::TextTerminal, n) = cmove(1, max(height(t), getY(t)+n)) cmove_line_down(t) = cmove_line_down(t, 1) cmove_col(t::TextTerminal, c) = cmove(c, getY(t)) # Defaults hascolor(::TextTerminal) = false # Utility Functions width(t::TextTerminal) = (displaysize(t)::Tuple{Int,Int})[2] height(t::TextTerminal) = (displaysize(t)::Tuple{Int,Int})[1] # For terminals with buffers flush(t::TextTerminal) = nothing clear(t::TextTerminal) = error("Unimplemented") clear_line(t::TextTerminal, row) = error("Unimplemented") clear_line(t::TextTerminal) = error("Unimplemented") raw!(t::TextTerminal, raw::Bool) = error("Unimplemented") beep(t::TextTerminal) = nothing enable_bracketed_paste(t::TextTerminal) = nothing disable_bracketed_paste(t::TextTerminal) = nothing ## UnixTerminal ## abstract type UnixTerminal <: TextTerminal end pipe_reader(t::UnixTerminal) = t.in_stream::IO pipe_writer(t::UnixTerminal) = t.out_stream::IO mutable struct TerminalBuffer <: UnixTerminal out_stream::IO end mutable struct TTYTerminal <: UnixTerminal term_type::String in_stream::IO out_stream::IO err_stream::IO end const CSI = "\x1b[" cmove_up(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)A") cmove_down(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)B") cmove_right(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)C") cmove_left(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)D") cmove_line_up(t::UnixTerminal, n) = (cmove_up(t, n); cmove_col(t, 1)) cmove_line_down(t::UnixTerminal, n) = (cmove_down(t, n); cmove_col(t, 1)) cmove_col(t::UnixTerminal, n) = (write(t.out_stream, '\r'); n > 1 && cmove_right(t, n-1)) const is_precompiling = Ref(false) if Sys.iswindows() function raw!(t::TTYTerminal,raw::Bool) is_precompiling[] && return true check_open(t.in_stream) if Base.ispty(t.in_stream) run((raw ? `stty raw -echo onlcr -ocrnl opost` : `stty sane`), t.in_stream, t.out_stream, t.err_stream) true else ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) != -1 end end else function raw!(t::TTYTerminal, raw::Bool) check_open(t.in_stream) ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), t.in_stream.handle::Ptr{Cvoid}, raw) != -1 end end # eval some of these definitions to insert CSI as a constant string @eval enable_bracketed_paste(t::UnixTerminal) = write(t.out_stream, $"$(CSI)?2004h") @eval disable_bracketed_paste(t::UnixTerminal) = write(t.out_stream, $"$(CSI)?2004l") @eval end_keypad_transmit_mode(t::UnixTerminal) = # tput rmkx write(t.out_stream, $"$(CSI)?1l\x1b>") @eval clear(t::UnixTerminal) = write(t.out_stream, $"$(CSI)H$(CSI)2J") @eval clear_line(t::UnixTerminal) = write(t.out_stream, $"\r$(CSI)0K") beep(t::UnixTerminal) = write(t.err_stream,"\x7") Base.displaysize(t::UnixTerminal) = displaysize(t.out_stream) hascolor(t::TTYTerminal) = get(t.out_stream, :color, false)::Bool # use cached value of have_color Base.in(key_value::Pair, t::TTYTerminal) = in(key_value, pipe_writer(t)) Base.haskey(t::TTYTerminal, key) = haskey(pipe_writer(t), key) Base.getindex(t::TTYTerminal, key) = getindex(pipe_writer(t), key) Base.get(t::TTYTerminal, key, default) = get(pipe_writer(t), key, default) Base.peek(t::TTYTerminal, ::Type{T}) where {T} = peek(t.in_stream, T)::T end # module o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/options.jlย ������# This file is a part of Julia. License is MIT: https://julialang.org/license ## User Options mutable struct Options hascolor::Bool extra_keymap::Union{Dict,Vector{<:Dict}} # controls the presumed tab width of code pasted into the REPL. # Must satisfy `0 < tabwidth <= 16`. tabwidth::Int # Maximum number of entries in the kill ring queue. # Beyond this number, oldest entries are discarded first. kill_ring_max::Int region_animation_duration::Float64 beep_duration::Float64 beep_blink::Float64 beep_maxduration::Float64 beep_colors::Vector{String} beep_use_current::Bool backspace_align::Bool backspace_adjust::Bool confirm_exit::Bool # ^D must be repeated to confirm exit auto_indent::Bool # indent a newline like line above auto_indent_tmp_off::Bool # switch auto_indent temporarily off if copy&paste auto_indent_bracketed_paste::Bool # set to true if terminal knows paste mode # cancel auto-indent when next character is entered within this time frame : auto_indent_time_threshold::Float64 # refresh after time delay auto_refresh_time_delay::Float64 # default IOContext settings at the REPL iocontext::Dict{Symbol,Any} end Options(; hascolor = true, extra_keymap = AnyDict[], tabwidth = 8, kill_ring_max = 100, region_animation_duration = 0.2, beep_duration = 0.2, beep_blink = 0.2, beep_maxduration = 1.0, beep_colors = ["\e[90m"], # gray (text_colors not yet available) beep_use_current = true, backspace_align = true, backspace_adjust = backspace_align, confirm_exit = false, auto_indent = true, auto_indent_tmp_off = false, auto_indent_bracketed_paste = false, auto_indent_time_threshold = 0.005, auto_refresh_time_delay = Sys.iswindows() ? 0.05 : 0.0, iocontext = Dict{Symbol,Any}()) = Options(hascolor, extra_keymap, tabwidth, kill_ring_max, region_animation_duration, beep_duration, beep_blink, beep_maxduration, beep_colors, beep_use_current, backspace_align, backspace_adjust, confirm_exit, auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, auto_indent_time_threshold, auto_refresh_time_delay, iocontext) # for use by REPLs not having an options field const GlobalOptions = Options() p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/LineEdit.jlฏl�����# This file is a part of Julia. License is MIT: https://julialang.org/license module LineEdit import ..REPL using REPL: AbstractREPL, Options using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep import Base: ensureroom, show, AnyDict, position using Base: something using InteractiveUtils: InteractiveUtils abstract type TextInterface end # see interface immediately below abstract type ModeState end # see interface below abstract type HistoryProvider end abstract type CompletionProvider end export run_interface, Prompt, ModalInterface, transition, reset_state, edit_insert, keymap @nospecialize # use only declared type signatures const StringLike = Union{Char,String,SubString{String}} # interface for TextInterface function Base.getproperty(ti::TextInterface, name::Symbol) if name === :hp return getfield(ti, :hp)::HistoryProvider elseif name === :complete return getfield(ti, :complete)::CompletionProvider elseif name === :keymap_dict return getfield(ti, :keymap_dict)::Dict{Char,Any} end return getfield(ti, name) end struct ModalInterface <: TextInterface modes::Vector{TextInterface} end mutable struct Prompt <: TextInterface # A string or function to be printed as the prompt. prompt::Union{String,Function} # A string or function to be printed before the prompt. May not change the length of the prompt. # This may be used for changing the color, issuing other terminal escape codes, etc. prompt_prefix::Union{String,Function} # Same as prefix except after the prompt prompt_suffix::Union{String,Function} output_prefix::Union{String,Function} output_prefix_prefix::Union{String,Function} output_prefix_suffix::Union{String,Function} keymap_dict::Dict{Char,Any} repl::Union{AbstractREPL,Nothing} complete::CompletionProvider on_enter::Function on_done::Function hist::HistoryProvider # TODO?: rename this `hp` (consistency with other TextInterfaces), or is the type-assert useful for mode(s)? sticky::Bool end show(io::IO, x::Prompt) = show(io, string("Prompt(\"", prompt_string(x.prompt), "\",...)")) mutable struct MIState interface::ModalInterface active_module::Module current_mode::TextInterface aborted::Bool mode_state::IdDict{TextInterface,ModeState} kill_ring::Vector{String} kill_idx::Int previous_key::Vector{Char} key_repeats::Int last_action::Symbol current_action::Symbol end MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} function show(io::IO, s::MIState) print(io, "MI State (", mode(s), " active)") end struct InputAreaState num_rows::Int64 curs_row::Int64 end mutable struct PromptState <: ModeState terminal::AbstractTerminal p::Prompt input_buffer::IOBuffer region_active::Symbol # :shift or :mark or :off undo_buffers::Vector{IOBuffer} undo_idx::Int ias::InputAreaState # indentation of lines which do not include the prompt # if negative, the width of the prompt is used indent::Int refresh_lock::Threads.SpinLock # this would better be Threads.Atomic{Float64}, but not supported on some platforms beeping::Float64 # this option is to detect when code is pasted in non-"bracketed paste mode" : last_newline::Float64 # register when last newline was entered # this option is to speed up output refresh_wait::Union{Timer,Nothing} end struct Modifiers shift::Bool end Modifiers() = Modifiers(false) options(s::PromptState) = if isdefined(s.p, :repl) && isdefined(s.p.repl, :options) # we can't test isa(s.p.repl, LineEditREPL) as LineEditREPL is defined # in the REPL module s.p.repl.options::Options else REPL.GlobalOptions::Options end function setmark(s::MIState, guess_region_active::Bool=true) refresh = set_action!(s, :setmark) s.current_action === :setmark && s.key_repeats > 0 && activate_region(s, :mark) mark(buffer(s)) refresh && refresh_line(s) nothing end # the default mark is 0 getmark(s::BufferLike) = max(0, buffer(s).mark) const Region = Pair{Int,Int} _region(s::BufferLike) = getmark(s) => position(s) region(s::BufferLike) = Pair(extrema(_region(s))...) bufend(s::BufferLike) = buffer(s).size axes(reg::Region) = first(reg)+1:last(reg) content(s::BufferLike, reg::Region = 0=>bufend(s)) = String(buffer(s).data[axes(reg)]) function activate_region(s::PromptState, state::Symbol) @assert state in (:mark, :shift, :off) s.region_active = state nothing end activate_region(s::ModeState, state::Symbol) = false deactivate_region(s::ModeState) = activate_region(s, :off) is_region_active(s::PromptState) = s.region_active in (:shift, :mark) is_region_active(s::ModeState) = false region_active(s::PromptState) = s.region_active region_active(s::ModeState) = :off input_string(s::PromptState) = String(take!(copy(s.input_buffer))) input_string_newlines(s::PromptState) = count(c->(c == '\n'), input_string(s)) function input_string_newlines_aftercursor(s::PromptState) str = input_string(s) isempty(str) && return 0 rest = str[nextind(str, position(s)):end] return count(c->(c == '\n'), rest) end struct EmptyCompletionProvider <: CompletionProvider end struct EmptyHistoryProvider <: HistoryProvider end reset_state(::EmptyHistoryProvider) = nothing complete_line(c::EmptyCompletionProvider, s) = String[], "", true # complete_line can be specialized for only two arguments, when the active module # doesn't matter (e.g. Pkg does this) complete_line(c::CompletionProvider, s, ::Module) = complete_line(c, s) terminal(s::IO) = s terminal(s::PromptState) = s.terminal function beep(s::PromptState, duration::Real=options(s).beep_duration, blink::Real=options(s).beep_blink, maxduration::Real=options(s).beep_maxduration; colors=options(s).beep_colors, use_current::Bool=options(s).beep_use_current) isinteractive() || return # some tests fail on some platforms s.beeping = min(s.beeping + duration, maxduration) let colors = Base.copymutable(colors) errormonitor(@async begin trylock(s.refresh_lock) || return try orig_prefix = s.p.prompt_prefix use_current && push!(colors, prompt_string(orig_prefix)) i = 0 while s.beeping > 0.0 prefix = colors[mod1(i+=1, end)] s.p.prompt_prefix = prefix refresh_multi_line(s, beeping=true) sleep(blink) s.beeping -= blink end s.p.prompt_prefix = orig_prefix refresh_multi_line(s, beeping=true) s.beeping = 0.0 finally unlock(s.refresh_lock) end end) end nothing end function cancel_beep(s::PromptState) # wait till beeping finishes while !trylock(s.refresh_lock) s.beeping = 0.0 sleep(.05) end unlock(s.refresh_lock) nothing end beep(::ModeState) = nothing cancel_beep(::ModeState) = nothing for f in Union{Symbol,Expr}[ :terminal, :on_enter, :add_history, :_buffer, :(Base.isempty), :replace_line, :refresh_multi_line, :input_string, :update_display_buffer, :empty_undo, :push_undo, :pop_undo, :options, :cancel_beep, :beep, :deactivate_region, :activate_region, :is_region_active, :region_active] @eval ($f)(s::MIState, args...) = $(f)(state(s), args...) end for f in [:edit_insert, :edit_insert_newline, :edit_backspace, :edit_move_left, :edit_move_right, :edit_move_word_left, :edit_move_word_right] @eval function ($f)(s::MIState, args...) set_action!(s, $(Expr(:quote, f))) $(f)(state(s), args...) end end const COMMAND_GROUPS = Dict(:movement => [:edit_move_left, :edit_move_right, :edit_move_word_left, :edit_move_word_right, :edit_move_up, :edit_move_down, :edit_exchange_point_and_mark], :deletion => [:edit_clear, :edit_backspace, :edit_delete, :edit_werase, :edit_delete_prev_word, :edit_delete_next_word, :edit_kill_line_forwards, :edit_kill_line_backwards, :edit_kill_region], :insertion => [:edit_insert, :edit_insert_newline, :edit_yank], :replacement => [:edit_yank_pop, :edit_transpose_chars, :edit_transpose_words, :edit_upper_case, :edit_lower_case, :edit_title_case, :edit_indent, :edit_transpose_lines_up!, :edit_transpose_lines_down!], :copy => [:edit_copy_region], :misc => [:complete_line, :setmark, :edit_undo!, :edit_redo!]) const COMMAND_GROUP = Dict{Symbol,Symbol}(command=>group for (group, commands) in COMMAND_GROUPS for command in commands) command_group(command::Symbol) = get(COMMAND_GROUP, command, :nogroup) command_group(command::Function) = command_group(nameof(command)) # return true if command should keep active a region function preserve_active(command::Symbol) command โˆˆ [:edit_indent, :edit_transpose_lines_down!, :edit_transpose_lines_up!] end # returns whether the "active region" status changed visibly, # i.e. whether there should be a visual refresh function set_action!(s::MIState, command::Symbol) # if a command is already running, don't update the current_action field, # as the caller is used as a helper function s.current_action === :unknown || return false active = region_active(s) ## record current action s.current_action = command ## handle activeness of the region if startswith(String(command), "shift_") # shift-move command if active !== :shift setmark(s) # s.current_action must already have been set activate_region(s, :shift) # NOTE: if the region was already active from a non-shift # move (e.g. ^Space^Space), the region is visibly changed return active !== :off # active status is reset end elseif !(preserve_active(command) || command_group(command) === :movement && region_active(s) === :mark) # if we move after a shift-move, the region is de-activated # (e.g. like emacs behavior) deactivate_region(s) return active !== :off end false end set_action!(s, command::Symbol) = nothing function common_prefix(completions::Vector{String}) ret = "" c1 = completions[1] isempty(c1) && return ret i = 1 cc, nexti = iterate(c1, i) while true for c in completions (i > lastindex(c) || c[i] != cc) && return ret end ret = string(ret, cc) i >= lastindex(c1) && return ret i = nexti cc, nexti = iterate(c1, i) end end # This is the maximum number of completions that will be displayed in a single # column, anything above that and multiple columns will be used. Note that this # does not restrict column length when multiple columns are used. const MULTICOLUMN_THRESHOLD = 5 # Show available completions function show_completions(s::PromptState, completions::Vector{String}) # skip any lines of input after the cursor cmove_down(terminal(s), input_string_newlines_aftercursor(s)) println(terminal(s)) if any(Base.Fix1(occursin, '\n'), completions) foreach(Base.Fix1(println, terminal(s)), completions) else n = length(completions) colmax = 2 + maximum(length, completions; init=1) # n.b. length >= textwidth num_cols = min(cld(n, MULTICOLUMN_THRESHOLD), max(div(width(terminal(s)), colmax), 1)) entries_per_col = cld(n, num_cols) idx = 0 for _ in 1:entries_per_col for col = 0:(num_cols-1) idx += 1 idx > n && break cmove_col(terminal(s), colmax*col+1) print(terminal(s), completions[idx]) end println(terminal(s)) end end # make space for the prompt for i = 1:input_string_newlines(s) println(terminal(s)) end end # Prompt Completions function complete_line(s::MIState) set_action!(s, :complete_line) if complete_line(state(s), s.key_repeats, s.active_module) return refresh_line(s) else beep(s) return :ignore end end function complete_line(s::PromptState, repeats::Int, mod::Module) completions, partial, should_complete = complete_line(s.p.complete, s, mod)::Tuple{Vector{String},String,Bool} isempty(completions) && return false if !should_complete # should_complete is false for cases where we only want to show # a list of possible completions but not complete, e.g. foo(\t show_completions(s, completions) elseif length(completions) == 1 # Replace word by completion prev_pos = position(s) push_undo(s) edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1]) else p = common_prefix(completions) if !isempty(p) && p != partial # All possible completions share the same prefix, so we might as # well complete that prev_pos = position(s) push_undo(s) edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, p) elseif repeats > 0 show_completions(s, completions) end end return true end function clear_input_area(terminal::AbstractTerminal, s::PromptState) if s.refresh_wait !== nothing close(s.refresh_wait) s.refresh_wait = nothing end _clear_input_area(terminal, s.ias) s.ias = InputAreaState(0, 0) end clear_input_area(terminal::AbstractTerminal, s::ModeState) = (_clear_input_area(terminal, s.ias); s.ias = InputAreaState(0, 0)) clear_input_area(s::ModeState) = clear_input_area(s.terminal, s) function _clear_input_area(terminal::AbstractTerminal, state::InputAreaState) # Go to the last line if state.curs_row < state.num_rows cmove_down(terminal, state.num_rows - state.curs_row) end # Clear lines one by one going up for j = 2:state.num_rows clear_line(terminal) cmove_up(terminal) end # Clear top line clear_line(terminal) nothing end prompt_string(s::PromptState) = prompt_string(s.p) prompt_string(p::Prompt) = prompt_string(p.prompt) prompt_string(s::AbstractString) = s prompt_string(f::Function) = Base.invokelatest(f) function refresh_multi_line(s::PromptState; kw...) if s.refresh_wait !== nothing close(s.refresh_wait) s.refresh_wait = nothing end refresh_multi_line(terminal(s), s; kw...) end refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw...) refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...) refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term === terminal(s); refresh_multi_line(termbuf,s; kw...)) function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf::IOBuffer, state::InputAreaState, prompt = ""; indent::Int = 0, region_active::Bool = false) _clear_input_area(termbuf, state) cols = width(terminal) rows = height(terminal) curs_row = -1 # relative to prompt (1-based) curs_pos = -1 # 1-based column position of the cursor cur_row = 0 # count of the number of rows buf_pos = position(buf) line_pos = buf_pos regstart, regstop = region(buf) written = 0 # Write out the prompt string lindent = write_prompt(termbuf, prompt, hascolor(terminal))::Int # Count the '\n' at the end of the line if the terminal emulator does (specific to DOS cmd prompt) miscountnl = @static Sys.iswindows() ? (isa(Terminals.pipe_reader(terminal), Base.TTY) && !(Base.ispty(Terminals.pipe_reader(terminal)))::Bool) : false # Now go through the buffer line by line seek(buf, 0) moreinput = true # add a blank line if there is a trailing newline on the last line lastline = false # indicates when to stop printing lines, even when there are potentially # more (for the case where rows is too small to print everything) # Note: when there are too many lines for rows, we still print the first lines # even if they are going to not be visible in the end: for simplicity, but # also because it does the 'right thing' when the window is resized while moreinput line = readline(buf, keep=true) moreinput = endswith(line, "\n") if rows == 1 && line_pos <= sizeof(line) - moreinput # we special case rows == 1, as otherwise by the time the cursor is seen to # be in the current line, it's too late to chop the '\n' away lastline = true curs_row = 1 curs_pos = lindent + line_pos end if moreinput && lastline # we want to print only one "visual" line, so line = chomp(line) # don't include the trailing "\n" end # We need to deal with on-screen characters, so use textwidth to compute occupied columns llength = textwidth(line) slength = sizeof(line) cur_row += 1 # lwrite: what will be written to termbuf lwrite = region_active ? highlight_region(line, regstart, regstop, written, slength) : line written += slength cmove_col(termbuf, lindent + 1) write(termbuf, lwrite) # We expect to be line after the last valid output line (due to # the '\n' at the end of the previous line) if curs_row == -1 line_pos -= slength # '\n' gets an extra pos # in this case, we haven't yet written the cursor position if line_pos < 0 || !moreinput num_chars = line_pos >= 0 ? llength : textwidth(line[1:prevind(line, line_pos + slength + 1)]) curs_row, curs_pos = divrem(lindent + num_chars - 1, cols) curs_row += cur_row curs_pos += 1 # There's an issue if the cursor is after the very right end of the screen. In that case we need to # move the cursor to the next line, and emit a newline if needed if curs_pos == cols # only emit the newline if the cursor is at the end of the line we're writing if line_pos == 0 write(termbuf, "\n") cur_row += 1 end curs_row += 1 curs_pos = 0 cmove_col(termbuf, 1) end end end cur_row += div(max(lindent + llength + miscountnl - 1, 0), cols) lindent = indent < 0 ? lindent : indent lastline && break if curs_row >= 0 && cur_row + 1 >= rows && # when too many lines, cur_row - curs_row + 1 >= rows รท 2 # center the cursor lastline = true end end seek(buf, buf_pos) # Let's move the cursor to the right position # The line first n = cur_row - curs_row if n > 0 cmove_up(termbuf, n) end #columns are 1 based cmove_col(termbuf, curs_pos + 1) # Updated cur_row,curs_row return InputAreaState(cur_row, curs_row) end function highlight_region(lwrite::Union{String,SubString{String}}, regstart::Int, regstop::Int, written::Int, slength::Int) if written <= regstop <= written+slength i = thisind(lwrite, regstop-written) lwrite = lwrite[1:i] * Base.disable_text_style[:reverse] * lwrite[nextind(lwrite, i):end] end if written <= regstart <= written+slength i = thisind(lwrite, regstart-written) lwrite = lwrite[1:i] * Base.text_colors[:reverse] * lwrite[nextind(lwrite, i):end] end return lwrite end function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) outbuf = IOBuffer() termbuf = TerminalBuffer(outbuf) ret = refresh_multi_line(termbuf, terminal, args...;kwargs...) # Output the entire refresh at once write(terminal, take!(outbuf)) flush(terminal) return ret end # Edit functionality is_non_word_char(c::Char) = c in """ \t\n\"\\'`@\$><=:;|&{}()[].,+-*/?%^~""" function reset_key_repeats(f::Function, s::MIState) key_repeats_sav = s.key_repeats try s.key_repeats = 0 return f() finally s.key_repeats = key_repeats_sav end end function edit_exchange_point_and_mark(s::MIState) set_action!(s, :edit_exchange_point_and_mark) return edit_exchange_point_and_mark(buffer(s)) ? refresh_line(s) : false end function edit_exchange_point_and_mark(buf::IOBuffer) m = getmark(buf) m == position(buf) && return false mark(buf) seek(buf, m) return true end char_move_left(s::PromptState) = char_move_left(s.input_buffer) function char_move_left(buf::IOBuffer) while position(buf) > 0 seek(buf, position(buf)-1) c = peek(buf) (((c & 0x80) == 0) || ((c & 0xc0) == 0xc0)) && break end pos = position(buf) c = read(buf, Char) seek(buf, pos) return c end function edit_move_left(buf::IOBuffer) if position(buf) > 0 #move to the next base UTF8 character to the left while true c = char_move_left(buf) if textwidth(c) != 0 || c == '\n' || position(buf) == 0 break end end return true end return false end edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) ? refresh_line(s) : false function edit_move_word_left(s::PromptState) if position(s) > 0 char_move_word_left(s.input_buffer) return refresh_line(s) end return nothing end char_move_right(s::MIState) = char_move_right(buffer(s)) function char_move_right(buf::IOBuffer) return !eof(buf) && read(buf, Char) end function char_move_word_right(buf::IOBuffer, is_delimiter::Function=is_non_word_char) while !eof(buf) && is_delimiter(char_move_right(buf)) end while !eof(buf) pos = position(buf) if is_delimiter(char_move_right(buf)) seek(buf, pos) break end end end function char_move_word_left(buf::IOBuffer, is_delimiter::Function=is_non_word_char) while position(buf) > 0 && is_delimiter(char_move_left(buf)) end while position(buf) > 0 pos = position(buf) if is_delimiter(char_move_left(buf)) seek(buf, pos) break end end end char_move_word_right(s::Union{MIState,ModeState}) = char_move_word_right(buffer(s)) char_move_word_left(s::Union{MIState,ModeState}) = char_move_word_left(buffer(s)) function edit_move_right(buf::IOBuffer) if !eof(buf) # move to the next base UTF8 character to the right while true c = char_move_right(buf) eof(buf) && break pos = position(buf) nextc = read(buf,Char) seek(buf,pos) (textwidth(nextc) != 0 || nextc == '\n') && break end return true end return false end edit_move_right(s::PromptState) = edit_move_right(s.input_buffer) ? refresh_line(s) : false function edit_move_word_right(s::PromptState) if !eof(s.input_buffer) char_move_word_right(s) return refresh_line(s) end return nothing end ## Move line up/down # Querying the terminal is expensive, memory access is cheap # so to find the current column, we find the offset for the start # of the line. function edit_move_up(buf::IOBuffer) npos = findprev(isequal(UInt8('\n')), buf.data, position(buf)) npos === nothing && return false # we're in the first line # We're interested in character count, not byte count offset = length(content(buf, npos => position(buf))) npos2 = something(findprev(isequal(UInt8('\n')), buf.data, npos-1), 0) seek(buf, npos2) for _ = 1:offset pos = position(buf) if read(buf, Char) == '\n' seek(buf, pos) break end end return true end function edit_move_up(s::MIState) set_action!(s, :edit_move_up) changed = edit_move_up(buffer(s)) changed && refresh_line(s) return changed end function edit_move_down(buf::IOBuffer) npos = something(findprev(isequal(UInt8('\n')), buf.data[1:buf.size], position(buf)), 0) # We're interested in character count, not byte count offset = length(String(buf.data[(npos+1):(position(buf))])) npos2 = findnext(isequal(UInt8('\n')), buf.data[1:buf.size], position(buf)+1) if npos2 === nothing #we're in the last line return false end seek(buf, npos2) for _ = 1:offset pos = position(buf) if eof(buf) || read(buf, Char) == '\n' seek(buf, pos) break end end return true end function edit_move_down(s::MIState) set_action!(s, :edit_move_down) changed = edit_move_down(buffer(s)) changed && refresh_line(s) return changed end function edit_shift_move(s::MIState, move_function::Function) @assert command_group(move_function) === :movement set_action!(s, Symbol(:shift_, move_function)) return move_function(s) end # splice! for IOBuffer: convert from close-open region to index, update the size, # and keep the cursor position and mark stable with the text # returns the removed portion as a String function edit_splice!(s::BufferLike, r::Region=region(s), ins::String = ""; rigid_mark::Bool=true) A, B = first(r), last(r) A >= B && isempty(ins) && return String(ins) buf = buffer(s) pos = position(buf) adjust_pos = true if A <= pos < B seek(buf, A) elseif B <= pos seek(buf, pos - B + A) else adjust_pos = false end if A < buf.mark < B || A == buf.mark == B # rigid_mark is used only if the mark is strictly "inside" # the region, or the region is empty and the mark is at the boundary buf.mark = rigid_mark ? A : A + sizeof(ins) elseif buf.mark >= B buf.mark += sizeof(ins) - B + A end ensureroom(buf, B) # handle !buf.reinit from take! ret = splice!(buf.data, A+1:B, codeunits(String(ins))) # position(), etc, are 0-indexed buf.size = buf.size + sizeof(ins) - B + A adjust_pos && seek(buf, position(buf) + sizeof(ins)) return String(copy(ret)) end edit_splice!(s::MIState, ins::AbstractString) = edit_splice!(s, region(s), ins) function edit_insert(s::PromptState, c::StringLike) push_undo(s) buf = s.input_buffer if ! options(s).auto_indent_bracketed_paste pos = position(buf) if pos > 0 if buf.data[pos] != _space && string(c) != " " options(s).auto_indent_tmp_off = false end if buf.data[pos] == _space #tabulators are already expanded to space #this expansion may take longer than auto_indent_time_threshold which breaks the timing s.last_newline = time() else #if characters after new line are coming in very fast #its probably copy&paste => switch auto-indent off for the next coming new line if ! options(s).auto_indent_tmp_off && time() - s.last_newline < options(s).auto_indent_time_threshold options(s).auto_indent_tmp_off = true end end end end old_wait = s.refresh_wait !== nothing if old_wait close(s.refresh_wait) s.refresh_wait = nothing end str = string(c) edit_insert(buf, str) if '\n' in str refresh_line(s) else after = options(s).auto_refresh_time_delay termbuf = terminal(s) w = width(termbuf) offset = s.ias.curs_row == 1 || s.indent < 0 ? sizeof(prompt_string(s.p.prompt)::String) : s.indent offset += position(buf) - beginofline(buf) # size of current line spinner = '\0' delayup = !eof(buf) || old_wait if offset + textwidth(str) <= w && !(after == 0 && delayup) # Avoid full update when appending characters to the end # and an update of curs_row isn't necessary (conservatively estimated) write(termbuf, str) spinner = ' ' # temporarily clear under the cursor elseif after == 0 refresh_line(s) delayup = false else # render a spinner for each key press if old_wait || length(str) != 1 spinner = spin_seq[mod1(position(buf) - w, length(spin_seq))] else spinner = str[end] end delayup = true end if delayup if spinner != '\0' write(termbuf, spinner) cmove_left(termbuf) end s.refresh_wait = Timer(after) do t s.refresh_wait === t || return s.refresh_wait = nothing refresh_line(s) end end end nothing end const spin_seq = ("โ‹ฏ", "โ‹ฑ", "โ‹ฎ", "โ‹ฐ") function edit_insert(buf::IOBuffer, c::StringLike) if eof(buf) return write(buf, c) else s = string(c) edit_splice!(buf, position(buf) => position(buf), s) return sizeof(s) end end # align: number of ' ' to insert after '\n' # if align < 0: align like line above function edit_insert_newline(s::PromptState, align::Int = 0 - options(s).auto_indent) push_undo(s) buf = buffer(s) autoindent = align < 0 if autoindent && ! options(s).auto_indent_tmp_off beg = beginofline(buf) align = min(something(findnext(_notspace, buf.data[beg+1:buf.size], 1), 0) - 1, position(buf) - beg) # indentation must not increase align < 0 && (align = buf.size-beg) #else # align = 0 end align < 0 && (align = 0) edit_insert(buf, '\n' * ' '^align) refresh_line(s) # updating s.last_newline should happen after refresh_line(s) which can take # an unpredictable amount of time and makes "paste detection" unreliable if ! options(s).auto_indent_bracketed_paste s.last_newline = time() end nothing end # align: delete up to 4 spaces to align to a multiple of 4 chars # adjust: also delete spaces on the right of the cursor to try to keep aligned what is # on the right function edit_backspace(s::PromptState, align::Bool=options(s).backspace_align, adjust::Bool=options(s).backspace_adjust) push_undo(s) if edit_backspace(buffer(s), align, adjust) return refresh_line(s) else pop_undo(s) return beep(s) end end const _newline = UInt8('\n') const _space = UInt8(' ') _notspace(c) = c != _space beginofline(buf::IOBuffer, pos::Int=position(buf)) = something(findprev(isequal(_newline), buf.data, pos), 0) function endofline(buf::IOBuffer, pos::Int=position(buf)) eol = findnext(isequal(_newline), buf.data[pos+1:buf.size], 1) eol === nothing ? buf.size : pos + eol - 1 end function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=false) !align && adjust && throw(DomainError((align, adjust), "if `adjust` is `true`, `align` must be `true`")) oldpos = position(buf) oldpos == 0 && return false c = char_move_left(buf) newpos = position(buf) if align && c == ' ' # maybe delete multiple spaces beg = beginofline(buf, newpos) align = textwidth(String(buf.data[1+beg:newpos])) % 4 nonspace = something(findprev(_notspace, buf.data, newpos), 0) if newpos - align >= nonspace newpos -= align seek(buf, newpos) if adjust spaces = something(findnext(_notspace, buf.data[newpos+2:buf.size], 1), 0) oldpos = spaces == 0 ? buf.size : buf.data[newpos+1+spaces] == _newline ? newpos+spaces : newpos + min(spaces, 4) end end end edit_splice!(buf, newpos => oldpos) return true end function edit_delete(s::MIState) set_action!(s, :edit_delete) push_undo(s) if edit_delete(buffer(s)) return refresh_line(s) else pop_undo(s) return beep(s) end end function edit_delete(buf::IOBuffer) eof(buf) && return false oldpos = position(buf) char_move_right(buf) edit_splice!(buf, oldpos => position(buf)) return true end function edit_werase(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf, isspace) pos0 = position(buf) return edit_splice!(buf, pos0 => pos1) end function edit_werase(s::MIState) set_action!(s, :edit_werase) push_undo(s) if push_kill!(s, edit_werase(buffer(s)), rev=true) return refresh_line(s) else pop_undo(s) return :ignore end end function edit_delete_prev_word(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf) pos0 = position(buf) return edit_splice!(buf, pos0 => pos1) end function edit_delete_prev_word(s::MIState) set_action!(s, :edit_delete_prev_word) push_undo(s) if push_kill!(s, edit_delete_prev_word(buffer(s)), rev=true) return refresh_line(s) else pop_undo(s) return :ignore end end function edit_delete_next_word(buf::IOBuffer) pos0 = position(buf) char_move_word_right(buf) pos1 = position(buf) return edit_splice!(buf, pos0 => pos1) end function edit_delete_next_word(s::MIState) set_action!(s, :edit_delete_next_word) push_undo(s) if push_kill!(s, edit_delete_next_word(buffer(s))) return refresh_line(s) else pop_undo(s) return :ignore end end function edit_yank(s::MIState) set_action!(s, :edit_yank) if isempty(s.kill_ring) beep(s) return :ignore end setmark(s) # necessary for edit_yank_pop push_undo(s) edit_insert(buffer(s), s.kill_ring[mod1(s.kill_idx, end)]) return refresh_line(s) end function edit_yank_pop(s::MIState, require_previous_yank::Bool=true) set_action!(s, :edit_yank_pop) repeat = s.last_action โˆˆ (:edit_yank, :edit_yank_pop) if require_previous_yank && !repeat || isempty(s.kill_ring) beep(s) return :ignore else require_previous_yank || repeat || setmark(s) push_undo(s) edit_splice!(s, s.kill_ring[mod1(s.kill_idx -= 1, end)]) return refresh_line(s) end end function push_kill!(s::MIState, killed::String, concat::Bool = s.key_repeats > 0; rev::Bool=false) isempty(killed) && return false if concat && !isempty(s.kill_ring) s.kill_ring[end] = rev ? killed * s.kill_ring[end] : # keep expected order for backward deletion s.kill_ring[end] * killed else push!(s.kill_ring, killed) length(s.kill_ring) > options(s).kill_ring_max && popfirst!(s.kill_ring) end s.kill_idx = lastindex(s.kill_ring) return true end function edit_kill_line(s::MIState, backwards::Bool=false) buf = buffer(s) if backwards set_action!(s, :edit_kill_line_backwards) pos = beginofline(buf) endpos = position(buf) pos == endpos && pos > 0 && (pos -= 1) else set_action!(s, :edit_kill_line_forwards) pos = position(buf) endpos = endofline(buf) endpos == pos && buf.size > pos && (endpos += 1) end push_undo(s) if push_kill!(s, edit_splice!(s, pos => endpos); rev=backwards) return refresh_line(s) else pop_undo(s) beep(s) return :ignore end end edit_kill_line_forwards(s::MIState) = edit_kill_line(s, false) edit_kill_line_backwards(s::MIState) = edit_kill_line(s, true) function edit_copy_region(s::MIState) set_action!(s, :edit_copy_region) buf = buffer(s) push_kill!(s, content(buf, region(buf)), false) || return :ignore if options(s).region_animation_duration > 0.0 edit_exchange_point_and_mark(s) sleep(options(s).region_animation_duration) edit_exchange_point_and_mark(s) end nothing end function edit_kill_region(s::MIState) set_action!(s, :edit_kill_region) push_undo(s) if push_kill!(s, edit_splice!(s), false) return refresh_line(s) else pop_undo(s) return :ignore end end function edit_transpose_chars(s::MIState) set_action!(s, :edit_transpose_chars) push_undo(s) return edit_transpose_chars(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_transpose_chars(buf::IOBuffer) # Moving left but not transpoing anything is intentional, and matches Emacs's behavior eof(buf) && position(buf) !== 0 && char_move_left(buf) position(buf) == 0 && return false char_move_left(buf) pos = position(buf) a, b = read(buf, Char), read(buf, Char) seek(buf, pos) write(buf, b, a) return true end function edit_transpose_words(s::MIState) set_action!(s, :edit_transpose_words) push_undo(s) return edit_transpose_words(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_transpose_words(buf::IOBuffer, mode::Symbol=:emacs) mode in [:readline, :emacs] || throw(ArgumentError("`mode` must be `:readline` or `:emacs`")) pos = position(buf) if mode === :emacs char_move_word_left(buf) char_move_word_right(buf) end char_move_word_right(buf) e2 = position(buf) char_move_word_left(buf) b2 = position(buf) char_move_word_left(buf) b1 = position(buf) char_move_word_right(buf) e1 = position(buf) e1 >= b2 && (seek(buf, pos); return false) word2 = edit_splice!(buf, b2 => e2, content(buf, b1 => e1)) edit_splice!(buf, b1 => e1, word2) seek(buf, e2) return true end # swap all lines intersecting the region with line above function edit_transpose_lines_up!(buf::IOBuffer, reg::Region) b2 = beginofline(buf, first(reg)) b2 == 0 && return false b1 = beginofline(buf, b2-1) # we do in this order so that the buffer's position is maintained in current line line1 = edit_splice!(buf, b1 => b2) # delete whole previous line line1 = '\n'*line1[1:end-1] # don't include the final '\n' pos = position(buf) # save pos in case it's at the end of line b = endofline(buf, last(reg) - b2 + b1) # b2-b1 is the size of the removed line1 edit_splice!(buf, b => b, line1) seek(buf, pos) return true end # swap all lines intersecting the region with line below function edit_transpose_lines_down!(buf::IOBuffer, reg::Region) e1 = endofline(buf, last(reg)) e1 == buf.size && return false e2 = endofline(buf, e1+1) line2 = edit_splice!(buf, e1 => e2) # delete whole next line line2 = line2[2:end]*'\n' # don't include leading '\n' b = beginofline(buf, first(reg)) edit_splice!(buf, b => b, line2, rigid_mark=false) return true end # return the region if active, or the current position as a Region otherwise region_if_active(s::MIState)::Region = is_region_active(s) ? region(s) : position(s)=>position(s) function edit_transpose_lines_up!(s::MIState) set_action!(s, :edit_transpose_lines_up!) if edit_transpose_lines_up!(buffer(s), region_if_active(s)) return refresh_line(s) else # beeping would be too noisy here return :ignore end end function edit_transpose_lines_down!(s::MIState) set_action!(s, :edit_transpose_lines_down!) if edit_transpose_lines_down!(buffer(s), region_if_active(s)) return refresh_line(s) else return :ignore end end function edit_upper_case(s::BufferLike) set_action!(s, :edit_upper_case) return edit_replace_word_right(s, uppercase) end function edit_lower_case(s::BufferLike) set_action!(s, :edit_lower_case) return edit_replace_word_right(s, lowercase) end function edit_title_case(s::BufferLike) set_action!(s, :edit_title_case) return edit_replace_word_right(s, titlecase) end function edit_replace_word_right(s::Union{MIState,ModeState}, replace::Function) push_undo(s) return edit_replace_word_right(buffer(s), replace) ? refresh_line(s) : pop_undo(s) end function edit_replace_word_right(buf::IOBuffer, replace::Function) # put the cursor at the beginning of the next word skipchars(is_non_word_char, buf) b = position(buf) char_move_word_right(buf) e = position(buf) e == b && return false edit_splice!(buf, b => e, replace(content(buf, b => e))) return true end edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) set_action!(s, :edit_clear) push_undo(s) if push_kill!(s, edit_splice!(s, 0 => bufend(s)), false) return refresh_line(s) else pop_undo(s) return :ignore end end function replace_line(s::PromptState, l::IOBuffer) empty_undo(s) s.input_buffer = copy(l) deactivate_region(s) nothing end function replace_line(s::PromptState, l::Union{String,SubString{String}}, keep_undo::Bool=false) keep_undo || empty_undo(s) s.input_buffer.ptr = 1 s.input_buffer.size = 0 write(s.input_buffer, l) deactivate_region(s) nothing end edit_indent_left(s::MIState, n=1) = edit_indent(s, -n) edit_indent_right(s::MIState, n=1) = edit_indent(s, n) function edit_indent(s::MIState, num::Int) set_action!(s, :edit_indent) push_undo(s) if edit_indent(buffer(s), num, is_region_active(s)) return refresh_line(s) else pop_undo(s) return :ignore end end # return the indices in buffer(s) of the beginning of each lines # having a non-empty intersection with region(s) function get_lines_in_region(s::BufferLike) buf = buffer(s) b, e = region(buf) bol = Int[beginofline(buf, b)] # begin of lines while true b = endofline(buf, b) b >= e && break # b < e ==> b+1 <= e <= buf.size push!(bol, b += 1) end return bol end # compute the number of spaces from b till the next non-space on the right # (which can also be "end of line" or "end of buffer") function leadingspaces(buf::IOBuffer, b::Int) @views ls = something(findnext(_notspace, buf.data[1:buf.size], b+1), 0)-1 ls == -1 && (ls = buf.size) ls -= b return ls end # indent by abs(num) characters, on the right if num >= 0, on the left otherwise # if multiline is true, indent all the lines in the region as a block. function edit_indent(buf::IOBuffer, num::Int, multiline::Bool) bol = multiline ? get_lines_in_region(buf) : Int[beginofline(buf)] if num < 0 # count leading spaces on the lines, which are an upper bound # on the number of spaces characters that can be removed ls_min = minimum(leadingspaces(buf, b) for b in bol) ls_min == 0 && return false # can't left-indent, no space can be removed num = -min(-num, ls_min) end for b in reverse!(bol) # reverse! to not mess-up the bol's offsets _edit_indent(buf, b, num) end return true end # indents line starting a position b by num positions # if num < 0, it is assumed that there are at least num white spaces # at the beginning of line _edit_indent(buf::IOBuffer, b::Int, num::Int) = num >= 0 ? edit_splice!(buf, b => b, ' '^num, rigid_mark=false) : edit_splice!(buf, b => (b - num)) function mode_idx(hist::HistoryProvider, mode::TextInterface) c = :julia for (k,v) in hist.mode_mapping isequal(v, mode) && (c = k) end return c end function guess_current_mode_name(s) try mode_idx(s.current_mode.hist, s.current_mode) catch nothing end end # edit current input in editor function edit_input(s, f = (filename, line, column) -> InteractiveUtils.edit(filename, line, column)) mode_name = guess_current_mode_name(s) filename = tempname() if mode_name === :julia filename *= ".jl" elseif mode_name === :shell filename *= ".sh" end buf = buffer(s) pos = position(buf) str = String(take!(buf)) lines = readlines(IOBuffer(str); keep=true) # Compute line line_start_offset = 0 line = 1 while line < length(lines) && line_start_offset + sizeof(lines[line]) <= pos line_start_offset += sizeof(lines[line]) line += 1 end # Compute column col = 0 off = line_start_offset while off <= pos off = nextind(str, off) col += 1 end # Write current input to temp file, edit, read back write(filename, str) f(filename, line, col) str_mod = readchomp(filename) rm(filename) # Write updated content write(buf, str_mod) if str == str_mod # If input was not modified: reset cursor seek(buf, pos) else # If input was modified: move cursor to end move_input_end(s) end refresh_line(s) end # return the identifier under the cursor, possibly with other words concatenated # to it with dots (e.g. "A.B.C" in "X; A.B.C*3", if the cursor is between "A" and "C") function current_word_with_dots(buf::IOBuffer) pos = position(buf) while true char_move_word_right(buf) if eof(buf) || peek(buf, Char) != '.' break end end pend = position(buf) while true char_move_word_left(buf) p = position(buf) p == 0 && break seek(buf, p-1) if peek(buf, Char) != '.' seek(buf, p) break end end pbegin = position(buf) word = pend > pbegin ? String(buf.data[pbegin+1:pend]) : "" seek(buf, pos) word end current_word_with_dots(s::MIState) = current_word_with_dots(buffer(s)) function activate_module(s::MIState) word = current_word_with_dots(s); isempty(word) && return beep(s) try mod = Base.Core.eval(Base.active_module(), Base.Meta.parse(word)) REPL.activate(mod) edit_clear(s) catch beep(s) end end history_prev(::EmptyHistoryProvider) = ("", false) history_next(::EmptyHistoryProvider) = ("", false) history_first(::EmptyHistoryProvider) = ("", false) history_last(::EmptyHistoryProvider) = ("", false) history_search(::EmptyHistoryProvider, args...) = false add_history(::EmptyHistoryProvider, s) = nothing add_history(s::PromptState) = add_history(mode(s).hist, s) history_next_prefix(s, hist, prefix) = false history_prev_prefix(s, hist, prefix) = false function history_prev(s::ModeState, hist) l, ok = history_prev(mode(s).hist) if ok replace_line(s, l) move_input_start(s) refresh_line(s) else beep(s) end nothing end function history_next(s::ModeState, hist) l, ok = history_next(mode(s).hist) if ok replace_line(s, l) move_input_end(s) refresh_line(s) else beep(s) end nothing end refresh_line(s::BufferLike) = refresh_multi_line(s) refresh_line(s::BufferLike, termbuf::AbstractTerminal) = refresh_multi_line(termbuf, s) default_completion_cb(::IOBuffer) = [] default_enter_cb(_) = true write_prompt(terminal::AbstractTerminal, s::PromptState, color::Bool) = write_prompt(terminal, s.p, color) function write_prompt(terminal::AbstractTerminal, p::Prompt, color::Bool) prefix = prompt_string(p.prompt_prefix) suffix = prompt_string(p.prompt_suffix) write(terminal, prefix) color && write(terminal, Base.text_colors[:bold]) width = write_prompt(terminal, p.prompt, color) color && write(terminal, Base.text_colors[:normal]) write(terminal, suffix) return width end function write_output_prefix(io::IO, p::Prompt, color::Bool) prefix = prompt_string(p.output_prefix_prefix) suffix = prompt_string(p.output_prefix_suffix) print(io, prefix) color && write(io, Base.text_colors[:bold]) width = write_prompt(io, p.output_prefix, color) color && write(io, Base.text_colors[:normal]) print(io, suffix) return width end # On Windows, when launching external processes, we cannot control what assumption they make on the # console mode. We thus forcibly reset the console mode at the start of the prompt to ensure they do # not leave the console mode in a corrupt state. # FIXME: remove when pseudo-tty are implemented for child processes if Sys.iswindows() function _console_mode() hOutput = ccall(:GetStdHandle, stdcall, Ptr{Cvoid}, (UInt32,), -11 % UInt32) # STD_OUTPUT_HANDLE dwMode = Ref{UInt32}() ccall(:GetConsoleMode, stdcall, Int32, (Ref{Cvoid}, Ref{UInt32}), hOutput, dwMode) return dwMode[] end const default_console_mode_ref = Ref{UInt32}() const default_console_mode_assigned = Ref(false) function get_default_console_mode() if default_console_mode_assigned[] == false default_console_mode_assigned[] = true default_console_mode_ref[] = _console_mode() end return default_console_mode_ref[] end function _reset_console_mode() mode = _console_mode() if mode !== get_default_console_mode() hOutput = ccall(:GetStdHandle, stdcall, Ptr{Cvoid}, (UInt32,), -11 % UInt32) # STD_OUTPUT_HANDLE ccall(:SetConsoleMode, stdcall, Int32, (Ptr{Cvoid}, UInt32), hOutput, default_console_mode_ref[]) end nothing end end # returns the width of the written prompt function write_prompt(terminal::Union{IO, AbstractTerminal}, s::Union{AbstractString,Function}, color::Bool) @static Sys.iswindows() && _reset_console_mode() promptstr = prompt_string(s)::String write(terminal, promptstr) return textwidth(promptstr) end ### Keymap Support const wildcard = '\U10f7ff' # "Private Use" Char normalize_key(key::Char) = string(key) normalize_key(key::Union{Int,UInt8}) = normalize_key(Char(key)) function normalize_key(key::Union{String,SubString{String}}) wildcard in key && error("Matching '\U10f7ff' not supported.") buf = IOBuffer() i = firstindex(key) while i <= ncodeunits(key) c, i = iterate(key, i) if c == '*' write(buf, wildcard) elseif c == '^' c, i = iterate(key, i) write(buf, uppercase(c)-64) elseif c == '\\' c, i = iterate(key, i) if c == 'C' c, i = iterate(key, i) c == '-' || error("the Control key specifier must start with \"\\\\C-\"") c, i = iterate(key, i) write(buf, uppercase(c)-64) elseif c == 'M' c, i = iterate(key, i) c == '-' || error("the Meta key specifier must start with \"\\\\M-\"") c, i = iterate(key, i) write(buf, '\e') write(buf, c) end else write(buf, c) end end return String(take!(buf)) end function normalize_keys(keymap::Union{Dict{Char,Any},AnyDict}) ret = Dict{Any,Any}() for (k,v) in keymap normalized = normalize_key(k) if haskey(ret,normalized) error("""Multiple spellings of a key in a single keymap (\"$k\" conflicts with existing mapping)""") end ret[normalized] = v end return ret end function add_nested_key!(keymap::Dict{Char, Any}, key::Union{String, Char}, value; override::Bool = false) y = iterate(key) while y !== nothing c, i = y y = iterate(key, i) if !override && c in keys(keymap) && (y === nothing || !isa(keymap[c], Dict)) error("Conflicting definitions for keyseq " * escape_string(key) * " within one keymap") end if y === nothing keymap[c] = value break elseif !(c in keys(keymap) && isa(keymap[c], Dict)) keymap[c] = Dict{Char,Any}() end keymap = keymap[c]::Dict{Char, Any} end end # Redirect a key as if `seq` had been the keysequence instead in a lazy fashion. # This is different from the default eager redirect, which only looks at the current and lower # layers of the stack. struct KeyAlias seq::String KeyAlias(seq) = new(normalize_key(seq)) end function match_input(f::Function, s::Union{Nothing,MIState}, term, cs::Vector{Char}, keymap) update_key_repeats(s, cs) c = String(cs) return function (s, p) # s::Union{Nothing,MIState}; p can be (at least) a LineEditREPL, PrefixSearchState, Nothing r = Base.invokelatest(f, s, p, c) if isa(r, Symbol) return r else return :ok end end end match_input(k::Nothing, s, term, cs, keymap) = (s,p) -> return :ok match_input(k::KeyAlias, s::Union{Nothing,MIState}, term, cs, keymap::Dict{Char}) = match_input(keymap, s, IOBuffer(k.seq), Char[], keymap) function match_input(k::Dict{Char}, s::Union{Nothing,MIState}, term::Union{AbstractTerminal,IOBuffer}=terminal(s), cs::Vector{Char}=Char[], keymap::Dict{Char} = k) # if we run out of characters to match before resolving an action, # return an empty keymap function eof(term) && return (s, p) -> :abort c = read(term, Char) # Ignore any `wildcard` as this is used as a # placeholder for the wildcard (see normalize_key("*")) c == wildcard && return (s, p) -> :ok push!(cs, c) key = haskey(k, c) ? c : wildcard # if we don't match on the key, look for a default action then fallback on 'nothing' to ignore return match_input(get(k, key, nothing), s, term, cs, keymap) end update_key_repeats(s, keystroke) = nothing function update_key_repeats(s::MIState, keystroke::Vector{Char}) s.key_repeats = s.previous_key == keystroke ? s.key_repeats + 1 : 0 s.previous_key = keystroke return end ## Conflict fixing # Consider a keymap of the form # # { # "**" => f # "ab" => g # } # # Naively this is transformed into a tree as # # { # '*' => { # '*' => f # } # 'a' => { # 'b' => g # } # } # # However, that's not what we want, because now "ac" is # is not defined. We need to fix this up and turn it into # # { # '*' => { # '*' => f # } # 'a' => { # '*' => f # 'b' => g # } # } # # i.e. copy over the appropriate default subdict # # deep merge where target has higher precedence function keymap_merge!(target::Dict{Char,Any}, source::Union{Dict{Char,Any},AnyDict}) for k in keys(source) if !haskey(target, k) target[k] = source[k] elseif isa(target[k], Dict) keymap_merge!(target[k], source[k]) else # Ignore, target has higher precedence end end end fixup_keymaps!(d, l, s, sk) = nothing function fixup_keymaps!(dict::Dict{Char,Any}, level, s, subkeymap) if level > 0 for d in values(dict) fixup_keymaps!(d, level-1, s, subkeymap) end else if haskey(dict, s) if isa(dict[s], Dict) && isa(subkeymap, Dict) keymap_merge!(dict[s], subkeymap) end else dict[s] = deepcopy(subkeymap) end end nothing end function add_specialisations(dict::Dict{Char,Any}, subdict::Dict{Char,Any}, level::Int) default_branch = subdict[wildcard] if isa(default_branch, Dict) default_branch = default_branch::Dict{Char,Any} # Go through all the keymaps in the default branch # and copy them over to dict for s in keys(default_branch) s == wildcard && add_specialisations(dict, default_branch, level+1) fixup_keymaps!(dict, level, s, default_branch[s]) end end end postprocess!(others) = nothing function postprocess!(dict::Dict{Char,Any}) # needs to be done first for every branch if haskey(dict, wildcard) add_specialisations(dict, dict, 1) end for (k,v) in dict k == wildcard && continue postprocess!(v) end end function getEntry(keymap::Dict{Char,Any},key::Union{String,Char}) v = keymap for c in key if !(haskey(v,c)::Bool) return nothing end v = v[c] end return v end # `target` is the total keymap being built up, already being a nested tree of Dicts. # source is the keymap specified by the user (with normalized keys) function keymap_merge(target::Dict{Char,Any}, source::Union{Dict{Char,Any},AnyDict}) ret = copy(target) direct_keys = filter(p -> isa(p.second, Union{Function, KeyAlias, Nothing}), source) # first direct entries for key in keys(direct_keys) add_nested_key!(ret, key, source[key]; override = true) end # then redirected entries for key in setdiff(keys(source), keys(direct_keys)) key::Union{String, Char} # We first resolve redirects in the source value = source[key] visited = Vector{Any}() while isa(value, Union{Char,String}) value = normalize_key(value) if value in visited throw_eager_redirection_cycle(key) end push!(visited,value) if !haskey(source,value) break end value = source[value] end if isa(value, Union{Char,String}) value = getEntry(ret, value) if value === nothing throw_could_not_find_redirected_value(key) end end add_nested_key!(ret, key, value; override = true) end return ret end throw_eager_redirection_cycle(key::Union{Char, String}) = error("Eager redirection cycle detected for key ", repr(key)) throw_could_not_find_redirected_value(key::Union{Char, String}) = error("Could not find redirected value ", repr(key)) function keymap_unify(keymaps) ret = Dict{Char,Any}() for keymap in keymaps ret = keymap_merge(ret, keymap) end postprocess!(ret) return ret end function validate_keymap(keymap) for key in keys(keymap) visited_keys = Any[key] v = getEntry(keymap,key) while isa(v,KeyAlias) if v.seq in visited_keys error("Alias cycle detected in keymap") end push!(visited_keys,v.seq) v = getEntry(keymap,v.seq) end end end function keymap(keymaps::Union{Vector{AnyDict},Vector{Dict{Char,Any}}}) # keymaps is a vector of prioritized keymaps, with highest priority first ret = keymap_unify(map(normalize_keys, reverse(keymaps))) validate_keymap(ret) return ret end const escape_defaults = merge!( AnyDict(Char(i) => nothing for i=vcat(0:26, 28:31)), # Ignore control characters by default AnyDict( # And ignore other escape sequences by default "\e*" => nothing, "\e[*" => nothing, "\eO*" => nothing, # Also ignore extended escape sequences # TODO: Support ranges of characters "\e[1**" => nothing, "\e[2**" => nothing, "\e[3**" => nothing, "\e[4**" => nothing, "\e[5**" => nothing, "\e[6**" => nothing, # less commonly used VT220 editing keys "\e[2~" => nothing, # insert "\e[3~" => nothing, # delete "\e[5~" => nothing, # page up "\e[6~" => nothing, # page down # These are different spellings of arrow keys, home keys, etc. # and should always do the same as the canonical key sequence "\e[1~" => KeyAlias("\e[H"), # home "\e[4~" => KeyAlias("\e[F"), # end "\e[7~" => KeyAlias("\e[H"), # home "\e[8~" => KeyAlias("\e[F"), # end "\eOA" => KeyAlias("\e[A"), "\eOB" => KeyAlias("\e[B"), "\eOC" => KeyAlias("\e[C"), "\eOD" => KeyAlias("\e[D"), "\eOH" => KeyAlias("\e[H"), "\eOF" => KeyAlias("\e[F"), ), # set mode commands AnyDict("\e[$(c)h" => nothing for c in 1:20), # reset mode commands AnyDict("\e[$(c)l" => nothing for c in 1:20) ) mutable struct HistoryPrompt <: TextInterface hp::HistoryProvider complete::CompletionProvider keymap_dict::Dict{Char,Any} HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) end mutable struct SearchState <: ModeState terminal::AbstractTerminal histprompt::HistoryPrompt #rsearch (true) or ssearch (false) backward::Bool query_buffer::IOBuffer response_buffer::IOBuffer failed::Bool ias::InputAreaState #The prompt whose input will be replaced by the matched history parent::Prompt SearchState(terminal, histprompt, backward, query_buffer, response_buffer) = new(terminal, histprompt, backward, query_buffer, response_buffer, false, InputAreaState(0,0)) end init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) terminal(s::SearchState) = s.terminal function update_display_buffer(s::SearchState, data::ModeState) s.failed = !history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, false) s.failed && beep(s) refresh_line(s) nothing end function history_next_result(s::MIState, data::ModeState) data.failed = !history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, true) data.failed && beep(s) refresh_line(data) nothing end function history_set_backward(s::SearchState, backward::Bool) s.backward = backward nothing end input_string(s::SearchState) = String(take!(copy(s.query_buffer))) function reset_state(s::SearchState) if s.query_buffer.size != 0 s.query_buffer.size = 0 s.query_buffer.ptr = 1 end if s.response_buffer.size != 0 s.response_buffer.size = 0 s.response_buffer.ptr = 1 end reset_state(s.histprompt.hp) s.failed = false nothing end # a meta-prompt that presents itself as parent_prompt, but which has an independent keymap # for prefix searching mutable struct PrefixHistoryPrompt <: TextInterface hp::HistoryProvider parent_prompt::Prompt complete::CompletionProvider keymap_dict::Dict{Char,Any} PrefixHistoryPrompt(hp, parent_prompt) = new(hp, parent_prompt, EmptyCompletionProvider()) end mutable struct PrefixSearchState <: ModeState terminal::AbstractTerminal histprompt::PrefixHistoryPrompt prefix::String response_buffer::IOBuffer ias::InputAreaState indent::Int # The modal interface state, if present mi::MIState #The prompt whose input will be replaced by the matched history parent::Prompt PrefixSearchState(terminal, histprompt, prefix, response_buffer) = new(terminal, histprompt, prefix, response_buffer, InputAreaState(0,0), 0) end # interface for ModeState function Base.getproperty(s::ModeState, name::Symbol) if name === :terminal return getfield(s, :terminal)::AbstractTerminal elseif name === :prompt return getfield(s, :prompt)::Prompt elseif name === :histprompt return getfield(s, :histprompt)::Union{HistoryPrompt,PrefixHistoryPrompt} elseif name === :parent return getfield(s, :parent)::Prompt elseif name === :response_buffer return getfield(s, :response_buffer)::IOBuffer elseif name === :ias return getfield(s, :ias)::InputAreaState elseif name === :indent return getfield(s, :indent)::Int # # unique fields, but no harm in declaring them # elseif name === :input_buffer # return getfield(s, :input_buffer)::IOBuffer # elseif name === :region_active # return getfield(s, :region_active)::Symbol # elseif name === :undo_buffers # return getfield(s, :undo_buffers)::Vector{IOBuffer} # elseif name === :undo_idx end return getfield(s, name) end init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) function show(io::IO, s::PrefixSearchState) print(io, "PrefixSearchState ", isdefined(s,:parent) ? string("(", s.parent, " active)") : "(no parent)", " for ", isdefined(s,:mi) ? s.mi : "no MI") end function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, s::Union{PromptState,PrefixSearchState}; beeping::Bool=false) beeping || cancel_beep(s) ias = refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s; indent = s.indent, region_active = is_region_active(s)) s.ias = ias return ias end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) write_prompt(terminal, s::PrefixSearchState, color::Bool) = write_prompt(terminal, s.histprompt.parent_prompt, color) prompt_string(s::PrefixSearchState) = prompt_string(s.histprompt.parent_prompt.prompt) terminal(s::PrefixSearchState) = s.terminal function reset_state(s::PrefixSearchState) if s.response_buffer.size != 0 s.response_buffer.size = 0 s.response_buffer.ptr = 1 end reset_state(s.histprompt.hp) nothing end function transition(f::Function, s::PrefixSearchState, mode::Prompt) if isdefined(s, :mi) transition(s.mi, mode) end s.parent = mode s.histprompt.parent_prompt = mode if isdefined(s, :mi) transition(f, s.mi, s.histprompt) else f() end nothing end replace_line(s::PrefixSearchState, l::IOBuffer) = (s.response_buffer = l; nothing) function replace_line(s::PrefixSearchState, l::Union{String,SubString{String}}) s.response_buffer.ptr = 1 s.response_buffer.size = 0 write(s.response_buffer, l) nothing end function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) buf = IOBuffer() unsafe_write(buf, pointer(s.query_buffer.data), s.query_buffer.ptr-1) write(buf, "': ") offset = buf.ptr ptr = s.response_buffer.ptr seek(s.response_buffer, 0) write(buf, read(s.response_buffer, String)) buf.ptr = offset + ptr - 1 s.response_buffer.ptr = ptr failed = s.failed ? "failed " : "" ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "($(failed)reverse-i-search)`" : "($(failed)forward-i-search)`") s.ias = ias return ias end state(s::MIState, p::TextInterface=mode(s)) = s.mode_state[p] state(s::PromptState, p::Prompt=mode(s)) = (@assert s.p == p; s) mode(s::MIState) = s.current_mode # ::TextInterface, and might be a Prompt mode(s::PromptState) = s.p # ::Prompt mode(s::SearchState) = @assert false mode(s::PrefixSearchState) = s.histprompt.parent_prompt # ::Prompt setmodifiers!(s::MIState, m::Modifiers) = setmodifiers!(mode(s), m) setmodifiers!(p::Prompt, m::Modifiers) = setmodifiers!(p.complete, m) setmodifiers!(c) = nothing # Search Mode completions function complete_line(s::SearchState, repeats, mod::Module) completions, partial, should_complete = complete_line(s.histprompt.complete, s, mod) # For now only allow exact completions in search mode if length(completions) == 1 prev_pos = position(s) push_undo(s) edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1]) return true end return false end accept_result_newmode(hp::HistoryProvider) = nothing function accept_result(s::MIState, p::TextInterface) parent = something(accept_result_newmode(p.hp), state(s, p).parent) transition(s, parent) do replace_line(state(s, parent), state(s, p).response_buffer) nothing end nothing end function copybuf!(dst::IOBuffer, src::IOBuffer) n = src.size ensureroom(dst, n) copyto!(dst.data, 1, src.data, 1, n) dst.size = src.size dst.ptr = src.ptr nothing end function enter_search(s::MIState, p::HistoryPrompt, backward::Bool) # a bit of hack to help fix #6325 buf = copy(buffer(s)) parent = mode(s) p.hp.last_mode = mode(s) p.hp.last_buffer = buf transition(s, p) do ss = state(s, p) ss.parent = parent ss.backward = backward truncate(ss.query_buffer, 0) ss.failed = false copybuf!(ss.response_buffer, buf) end nothing end function enter_prefix_search(s::MIState, p::PrefixHistoryPrompt, backward::Bool) buf = copy(buffer(s)) parent = mode(s) transition(s, p) do local pss = state(s, p) pss.parent = parent pss.histprompt.parent_prompt = parent pss.prefix = String(buf.data[1:position(buf)]) copybuf!(pss.response_buffer, buf) pss.indent = state(s, parent).indent pss.mi = s end pss = state(s, p) if backward history_prev_prefix(pss, pss.histprompt.hp, pss.prefix) else history_next_prefix(pss, pss.histprompt.hp, pss.prefix) end nothing end function setup_search_keymap(hp) p = HistoryPrompt(hp) pkeymap = AnyDict( "^R" => (s::MIState,data::ModeState,c)->(history_set_backward(data, true); history_next_result(s, data)), "^S" => (s::MIState,data::ModeState,c)->(history_set_backward(data, false); history_next_result(s, data)), '\r' => (s::MIState,o...)->accept_result(s, p), '\n' => '\r', # Limited form of tab completions '\t' => (s::MIState,data::ModeState,c)->(complete_line(s); update_display_buffer(s, data)), "^L" => (s::MIState,data::ModeState,c)->(Terminals.clear(terminal(s)); update_display_buffer(s, data)), # Backspace/^H '\b' => (s::MIState,data::ModeState,c)->(edit_backspace(data.query_buffer) ? update_display_buffer(s, data) : beep(s)), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s::MIState,data::ModeState,c)->(isempty(edit_delete_prev_word(data.query_buffer)) ? beep(s) : update_display_buffer(s, data)), "\e\x7f" => "\e\b", # Word erase to whitespace "^W" => (s::MIState,data::ModeState,c)->(isempty(edit_werase(data.query_buffer)) ? beep(s) : update_display_buffer(s, data)), # ^C and ^D "^C" => (s::MIState,data::ModeState,c)->(edit_clear(data.query_buffer); edit_clear(data.response_buffer); update_display_buffer(s, data); reset_state(data.histprompt.hp); transition(s, data.parent)), "^D" => "^C", # Other ways to cancel search mode (it's difficult to bind \e itself) "^G" => "^C", "\e\e" => "^C", "^K" => (s::MIState,o...)->transition(s, state(s, p).parent), "^Y" => (s::MIState,data::ModeState,c)->(edit_yank(s); update_display_buffer(s, data)), "^U" => (s::MIState,data::ModeState,c)->(edit_clear(data.query_buffer); edit_clear(data.response_buffer); update_display_buffer(s, data)), # Right Arrow "\e[C" => (s::MIState,o...)->(accept_result(s, p); edit_move_right(s)), # Left Arrow "\e[D" => (s::MIState,o...)->(accept_result(s, p); edit_move_left(s)), # Up Arrow "\e[A" => (s::MIState,o...)->(accept_result(s, p); edit_move_up(s)), # Down Arrow "\e[B" => (s::MIState,o...)->(accept_result(s, p); edit_move_down(s)), "^B" => (s::MIState,o...)->(accept_result(s, p); edit_move_left(s)), "^F" => (s::MIState,o...)->(accept_result(s, p); edit_move_right(s)), # Meta B "\eb" => (s::MIState,o...)->(accept_result(s, p); edit_move_word_left(s)), # Meta F "\ef" => (s::MIState,o...)->(accept_result(s, p); edit_move_word_right(s)), # Ctrl-Left Arrow "\e[1;5D" => "\eb", # Ctrl-Left Arrow on rxvt "\eOd" => "\eb", # Ctrl-Right Arrow "\e[1;5C" => "\ef", # Ctrl-Right Arrow on rxvt "\eOc" => "\ef", "^A" => (s::MIState,o...)->(accept_result(s, p); move_line_start(s); refresh_line(s)), "^E" => (s::MIState,o...)->(accept_result(s, p); move_line_end(s); refresh_line(s)), "^Z" => (s::MIState,o...)->(return :suspend), # Try to catch all Home/End keys "\e[H" => (s::MIState,o...)->(accept_result(s, p); move_input_start(s); refresh_line(s)), "\e[F" => (s::MIState,o...)->(accept_result(s, p); move_input_end(s); refresh_line(s)), # Use ^N and ^P to change search directions and iterate through results "^N" => (s::MIState,data::ModeState,c)->(history_set_backward(data, false); history_next_result(s, data)), "^P" => (s::MIState,data::ModeState,c)->(history_set_backward(data, true); history_next_result(s, data)), # Bracketed paste mode "\e[200~" => (s::MIState,data::ModeState,c)-> begin ps = state(s, mode(s)) input = readuntil(ps.terminal, "\e[201~", keep=false) edit_insert(data.query_buffer, input); update_display_buffer(s, data) end, "*" => (s::MIState,data::ModeState,c::StringLike)->(edit_insert(data.query_buffer, c); update_display_buffer(s, data)) ) p.keymap_dict = keymap([pkeymap, escape_defaults]) skeymap = AnyDict( "^R" => (s::MIState,o...)->(enter_search(s, p, true)), "^S" => (s::MIState,o...)->(enter_search(s, p, false)), ) return (p, skeymap) end keymap(state, p::Union{HistoryPrompt,PrefixHistoryPrompt}) = p.keymap_dict keymap_data(state, ::Union{HistoryPrompt, PrefixHistoryPrompt}) = state Base.isempty(s::PromptState) = s.input_buffer.size == 0 on_enter(s::PromptState) = s.p.on_enter(s) move_input_start(s::BufferLike) = (seek(buffer(s), 0); nothing) move_input_end(buf::IOBuffer) = (seekend(buf); nothing) move_input_end(s::Union{MIState,ModeState}) = (move_input_end(buffer(s)); nothing) function move_line_start(s::MIState) set_action!(s, :move_line_start) buf = buffer(s) curpos = position(buf) curpos == 0 && return if s.key_repeats > 0 move_input_start(s) else seek(buf, something(findprev(isequal(UInt8('\n')), buf.data, curpos), 0)) end nothing end function move_line_end(s::MIState) set_action!(s, :move_line_end) s.key_repeats > 0 ? move_input_end(s) : move_line_end(buffer(s)) nothing end function move_line_end(buf::IOBuffer) eof(buf) && return @views pos = findnext(isequal(UInt8('\n')), buf.data[1:buf.size], position(buf)+1) if pos === nothing move_input_end(buf) return end seek(buf, pos - 1) nothing end edit_insert_last_word(s::MIState) = edit_insert(s, get_last_word(IOBuffer(mode(s).hist.history[end]))) function get_last_word(buf::IOBuffer) move_line_end(buf) char_move_word_left(buf) posbeg = position(buf) char_move_word_right(buf) posend = position(buf) buf = take!(buf) word = String(buf[posbeg+1:posend]) rest = String(buf[posend+1:end]) lp, rp, lb, rb = count.(.==(('(', ')', '[', ']')), rest) special = any(in.(('\'', '"', '`'), rest)) !special && lp == rp && lb == rb ? word *= rest : word end function commit_line(s::MIState) cancel_beep(s) move_input_end(s) refresh_line(s) println(terminal(s)) add_history(s) ias = InputAreaState(0, 0) state(s, mode(s)).ias = ias nothing end function bracketed_paste(s::MIState; tabwidth::Int=options(s).tabwidth) options(s).auto_indent_bracketed_paste = true ps = state(s, mode(s))::PromptState input = readuntil(ps.terminal, "\e[201~") input = replace(input, '\r' => '\n') if position(buffer(s)) == 0 indent = Base.indentation(input; tabwidth=tabwidth)[1] input = Base.unindent(input, indent; tabwidth=tabwidth) end return replace(input, '\t' => " "^tabwidth) end function tab_should_complete(s::MIState) # Yes, we are ignoring the possibility # the we could be in the middle of a multi-byte # sequence, here but that's ok, since any # whitespace we're interested in is only one byte buf = buffer(s) pos = position(buf) pos == 0 && return true c = buf.data[pos] return c != _newline && c != UInt8('\t') && # hack to allow path completion in cmds # after a space, e.g., `cd <tab>`, while still # allowing multiple indent levels (c != _space || pos <= 3 || buf.data[pos-1] != _space) end # jump_spaces: if cursor is on a ' ', move it to the first non-' ' char on the right # if `delete_trailing`, ignore trailing ' ' by deleting them function edit_tab(s::MIState, jump_spaces::Bool=false, delete_trailing::Bool=jump_spaces) tab_should_complete(s) && return complete_line(s) set_action!(s, :edit_insert_tab) push_undo(s) edit_insert_tab(buffer(s), jump_spaces, delete_trailing) || pop_undo(s) return refresh_line(s) end function shift_tab_completion(s::MIState) setmodifiers!(s, Modifiers(true)) return complete_line(s) end # return true iff the content of the buffer is modified # return false when only the position changed function edit_insert_tab(buf::IOBuffer, jump_spaces::Bool=false, delete_trailing::Bool=jump_spaces) i = position(buf) if jump_spaces && i < buf.size && buf.data[i+1] == _space spaces = something(findnext(_notspace, buf.data[i+1:buf.size], 1), 0) if delete_trailing && (spaces == 0 || buf.data[i+spaces] == _newline) edit_splice!(buf, i => (spaces == 0 ? buf.size : i+spaces-1)) else jump = spaces == 0 ? buf.size : i+spaces-1 seek(buf, jump) return false end end # align to multiples of 4: align = 4 - textwidth(String(buf.data[1+beginofline(buf, i):i])) % 4 edit_insert(buf, ' '^align) return true end function edit_abort(s::MIState, confirm::Bool=options(s).confirm_exit; key="^D") set_action!(s, :edit_abort) if !confirm || s.last_action === :edit_abort println(terminal(s)) return :abort else println("Type $key again to exit.\n") return refresh_line(s) end end const default_keymap = AnyDict( # Tab '\t' => (s::MIState,o...)->edit_tab(s, true), # Shift-tab "\e[Z" => (s::MIState,o...)->shift_tab_completion(s), # Enter '\r' => (s::MIState,o...)->begin if on_enter(s) || (eof(buffer(s)) && s.key_repeats > 1) commit_line(s) return :done else edit_insert_newline(s) end end, '\n' => KeyAlias('\r'), # Backspace/^H '\b' => (s::MIState,o...) -> is_region_active(s) ? edit_kill_region(s) : edit_backspace(s), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s::MIState,o...)->edit_delete_prev_word(s), "\e\x7f" => "\e\b", # ^D "^D" => (s::MIState,o...)->begin if buffer(s).size > 0 edit_delete(s) else edit_abort(s) end end, # Ctrl-Space "\0" => (s::MIState,o...)->setmark(s), "^G" => (s::MIState,o...)->(deactivate_region(s); refresh_line(s)), "^X^X" => (s::MIState,o...)->edit_exchange_point_and_mark(s), "^B" => (s::MIState,o...)->edit_move_left(s), "^F" => (s::MIState,o...)->edit_move_right(s), "^P" => (s::MIState,o...)->edit_move_up(s), "^N" => (s::MIState,o...)->edit_move_down(s), # Meta-Up "\e[1;3A" => (s::MIState,o...) -> edit_transpose_lines_up!(s), # Meta-Down "\e[1;3B" => (s::MIState,o...) -> edit_transpose_lines_down!(s), "\e[1;2D" => (s::MIState,o...)->edit_shift_move(s, edit_move_left), "\e[1;2C" => (s::MIState,o...)->edit_shift_move(s, edit_move_right), "\e[1;2A" => (s::MIState,o...)->edit_shift_move(s, edit_move_up), "\e[1;2B" => (s::MIState,o...)->edit_shift_move(s, edit_move_down), # Meta B "\eb" => (s::MIState,o...)->edit_move_word_left(s), # Meta F "\ef" => (s::MIState,o...)->edit_move_word_right(s), # Ctrl-Left Arrow "\e[1;5D" => "\eb", # Ctrl-Left Arrow on rxvt "\eOd" => "\eb", # Ctrl-Right Arrow "\e[1;5C" => "\ef", # Ctrl-Right Arrow on rxvt "\eOc" => "\ef", # Meta Enter "\e\r" => (s::MIState,o...)->edit_insert_newline(s), "\e." => (s::MIState,o...)->edit_insert_last_word(s), "\e\n" => "\e\r", "^_" => (s::MIState,o...)->edit_undo!(s), "\e_" => (s::MIState,o...)->edit_redo!(s), # Simply insert it into the buffer by default "*" => (s::MIState,data,c::StringLike)->(edit_insert(s, c)), "^U" => (s::MIState,o...)->edit_kill_line_backwards(s), "^K" => (s::MIState,o...)->edit_kill_line_forwards(s), "^Y" => (s::MIState,o...)->edit_yank(s), "\ey" => (s::MIState,o...)->edit_yank_pop(s), "\ew" => (s::MIState,o...)->edit_copy_region(s), "\eW" => (s::MIState,o...)->edit_kill_region(s), "^A" => (s::MIState,o...)->(move_line_start(s); refresh_line(s)), "^E" => (s::MIState,o...)->(move_line_end(s); refresh_line(s)), # Try to catch all Home/End keys "\e[H" => (s::MIState,o...)->(move_input_start(s); refresh_line(s)), "\e[F" => (s::MIState,o...)->(move_input_end(s); refresh_line(s)), "^L" => (s::MIState,o...)->(Terminals.clear(terminal(s)); refresh_line(s)), "^W" => (s::MIState,o...)->edit_werase(s), # Meta D "\ed" => (s::MIState,o...)->edit_delete_next_word(s), "^C" => (s::MIState,o...)->begin try # raise the debugger if present ccall(:jl_raise_debugger, Int, ()) catch end cancel_beep(s) move_input_end(s) refresh_line(s) print(terminal(s), "^C\n\n") transition(s, :reset) refresh_line(s) end, "^Z" => (s::MIState,o...)->(return :suspend), # Right Arrow "\e[C" => (s::MIState,o...)->edit_move_right(s), # Left Arrow "\e[D" => (s::MIState,o...)->edit_move_left(s), # Up Arrow "\e[A" => (s::MIState,o...)->edit_move_up(s), # Down Arrow "\e[B" => (s::MIState,o...)->edit_move_down(s), # Meta-Right Arrow "\e[1;3C" => (s::MIState,o...) -> edit_indent_right(s, 1), # Meta-Left Arrow "\e[1;3D" => (s::MIState,o...) -> edit_indent_left(s, 1), # Delete "\e[3~" => (s::MIState,o...)->edit_delete(s), # Bracketed Paste Mode "\e[200~" => (s::MIState,o...)->begin input = bracketed_paste(s) edit_insert(s, input) end, "^T" => (s::MIState,o...)->edit_transpose_chars(s), "\et" => (s::MIState,o...)->edit_transpose_words(s), "\eu" => (s::MIState,o...)->edit_upper_case(s), "\el" => (s::MIState,o...)->edit_lower_case(s), "\ec" => (s::MIState,o...)->edit_title_case(s), "\ee" => (s::MIState,o...) -> edit_input(s), "\em" => (s::MIState, o...) -> activate_module(s) ) const history_keymap = AnyDict( "^P" => (s::MIState,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), "^N" => (s::MIState,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), "\ep" => (s::MIState,o...)->(history_prev(s, mode(s).hist)), "\en" => (s::MIState,o...)->(history_next(s, mode(s).hist)), # Up Arrow "\e[A" => (s::MIState,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), # Down Arrow "\e[B" => (s::MIState,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), # Page Up "\e[5~" => (s::MIState,o...)->(history_prev(s, mode(s).hist)), # Page Down "\e[6~" => (s::MIState,o...)->(history_next(s, mode(s).hist)), "\e<" => (s::MIState,o...)->(history_first(s, mode(s).hist)), "\e>" => (s::MIState,o...)->(history_last(s, mode(s).hist)), ) const prefix_history_keymap = merge!( AnyDict( "^P" => (s::MIState,data::ModeState,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), "^N" => (s::MIState,data::ModeState,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), # Up Arrow "\e[A" => (s::MIState,data::ModeState,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), # Down Arrow "\e[B" => (s::MIState,data::ModeState,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), # by default, pass through to the parent mode "*" => (s::MIState,data::ModeState,c::StringLike)->begin accept_result(s, data.histprompt); ps = state(s, mode(s)) map = keymap(ps, mode(s)) match_input(map, s, IOBuffer(c))(s, keymap_data(ps, mode(s))) end, # match escape sequences for pass through "^x*" => "*", "\em*" => "*", "\e*" => "*", "\e[*" => "*", "\eO*" => "*", "\e[1;5*" => "*", # Ctrl-Arrow "\e[1;2*" => "*", # Shift-Arrow "\e[1;3*" => "*", # Meta-Arrow "\e[200~" => "*" ), # VT220 editing commands AnyDict("\e[$(n)~" => "*" for n in 1:8), # set mode commands AnyDict("\e[$(c)h" => "*" for c in 1:20), # reset mode commands AnyDict("\e[$(c)l" => "*" for c in 1:20) ) function setup_prefix_keymap(hp::HistoryProvider, parent_prompt::Prompt) p = PrefixHistoryPrompt(hp, parent_prompt) p.keymap_dict = keymap([prefix_history_keymap]) pkeymap = AnyDict( "^P" => (s::MIState,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), "^N" => (s::MIState,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), # Up Arrow "\e[A" => (s::MIState,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), # Down Arrow "\e[B" => (s::MIState,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), ) return (p, pkeymap) end function deactivate(p::TextInterface, s::ModeState, termbuf::AbstractTerminal, term::TextTerminal) clear_input_area(termbuf, s) return s end function activate(p::TextInterface, s::ModeState, termbuf::AbstractTerminal, term::TextTerminal) s.ias = InputAreaState(0, 0) refresh_line(s, termbuf) nothing end function activate(p::TextInterface, s::MIState, termbuf::AbstractTerminal, term::TextTerminal) @assert p == mode(s) activate(p, state(s), termbuf, term) nothing end activate(m::ModalInterface, s::MIState, termbuf::AbstractTerminal, term::TextTerminal) = activate(mode(s), s, termbuf, term) commit_changes(t::UnixTerminal, termbuf::TerminalBuffer) = (write(t, take!(termbuf.out_stream)); nothing) function transition(f::Function, s::MIState, newmode::Union{TextInterface,Symbol}) cancel_beep(s) if newmode === :abort s.aborted = true return end if newmode === :reset reset_state(s) return end if !haskey(s.mode_state, newmode) s.mode_state[newmode] = init_state(terminal(s), newmode) end termbuf = TerminalBuffer(IOBuffer()) t = terminal(s) s.mode_state[mode(s)] = deactivate(mode(s), state(s), termbuf, t) s.current_mode = newmode f() activate(newmode, state(s, newmode), termbuf, t) commit_changes(t, termbuf) nothing end transition(s::MIState, mode::Union{TextInterface,Symbol}) = transition((args...)->nothing, s, mode) function reset_state(s::PromptState) if s.input_buffer.size != 0 s.input_buffer.size = 0 s.input_buffer.ptr = 1 end empty_undo(s) deactivate_region(s) ias = InputAreaState(0, 0) s.ias = ias return ias end function reset_state(s::MIState) for (mode, state) in s.mode_state reset_state(state) end end const default_keymap_dict = keymap([default_keymap, escape_defaults]) function Prompt(prompt ; prompt_prefix = "", prompt_suffix = "", output_prefix = "", output_prefix_prefix = "", output_prefix_suffix = "", keymap_dict = default_keymap_dict, repl = nothing, complete = EmptyCompletionProvider(), on_enter = default_enter_cb, on_done = ()->nothing, hist = EmptyHistoryProvider(), sticky = false) return Prompt(prompt, prompt_prefix, prompt_suffix, output_prefix, output_prefix_prefix, output_prefix_suffix, keymap_dict, repl, complete, on_enter, on_done, hist, sticky) end run_interface(::Prompt) = nothing init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), :off, IOBuffer[], 1, InputAreaState(1, 1), #=indent(spaces)=# -1, Threads.SpinLock(), 0.0, -Inf, nothing) function init_state(terminal, m::ModalInterface) s = MIState(m, Main, m.modes[1], false, IdDict{Any,Any}()) for mode in m.modes s.mode_state[mode] = init_state(terminal, mode) end return s end function run_interface(terminal::TextTerminal, m::ModalInterface, s::MIState=init_state(terminal, m)) while !s.aborted buf, ok, suspend = prompt!(terminal, m, s) while suspend @static if Sys.isunix(); ccall(:jl_repl_raise_sigtstp, Cint, ()); end buf, ok, suspend = prompt!(terminal, m, s) end Base.invokelatest(mode(state(s)).on_done, s, buf, ok) end end buffer(s) = _buffer(s)::IOBuffer _buffer(s::PromptState) = s.input_buffer _buffer(s::SearchState) = s.query_buffer _buffer(s::PrefixSearchState) = s.response_buffer _buffer(s::IOBuffer) = s position(s::Union{MIState,ModeState}) = position(buffer(s)) function empty_undo(s::PromptState) empty!(s.undo_buffers) s.undo_idx = 1 nothing end empty_undo(s) = nothing function push_undo(s::PromptState, advance::Bool=true) resize!(s.undo_buffers, s.undo_idx) s.undo_buffers[end] = copy(s.input_buffer) advance && (s.undo_idx += 1) nothing end push_undo(s) = nothing # must be called after a push_undo function pop_undo(s::PromptState) pop!(s.undo_buffers) s.undo_idx -= 1 nothing end function edit_undo!(s::MIState) set_action!(s, :edit_undo!) s.last_action โˆ‰ (:edit_redo!, :edit_undo!) && push_undo(s, false) if !edit_undo!(state(s)) beep(s) return :ignore end return nothing end function edit_undo!(s::PromptState) s.undo_idx > 1 || return false s.input_buffer = s.undo_buffers[s.undo_idx -=1] refresh_line(s) return true end edit_undo!(s) = nothing function edit_redo!(s::MIState) set_action!(s, :edit_redo!) if s.last_action โˆ‰ (:edit_redo!, :edit_undo!) || !edit_redo!(state(s)) beep(s) return :ignore end return nothing end function edit_redo!(s::PromptState) s.undo_idx < length(s.undo_buffers) || return false s.input_buffer = s.undo_buffers[s.undo_idx += 1] refresh_line(s) return true end edit_redo!(s) = nothing keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.repl keymap(ms::MIState, m::ModalInterface) = keymap(state(ms), mode(ms)) keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_state(term, prompt)) Base.reseteof(term) raw!(term, true) enable_bracketed_paste(term) try activate(prompt, s, term, term) old_state = mode(s) while true kmap = keymap(s, prompt) fcn = match_input(kmap, s) kdata = keymap_data(s, prompt) s.current_action = :unknown # if the to-be-run action doesn't update this field, # :unknown will be recorded in the last_action field local status # errors in keymaps shouldn't cause the REPL to fail, so wrap in a # try/catch block try status = fcn(s, kdata) catch e @error "Error in the keymap" exception=e,catch_backtrace() # try to cleanup and get `s` back to its original state before returning transition(s, :reset) transition(s, old_state) status = :done end status !== :ignore && (s.last_action = s.current_action) if status === :abort s.aborted = true return buffer(s), false, false elseif status === :done return buffer(s), true, false elseif status === :suspend if Sys.isunix() return buffer(s), true, true end else @assert status โˆˆ (:ok, :ignore) end end finally raw!(term, false) && disable_bracketed_paste(term) end # unreachable end end # module w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/REPLCompletions.jl"ไ������# This file is a part of Julia. License is MIT: https://julialang.org/license module REPLCompletions export completions, shell_completions, bslash_completions, completion_text using Core: CodeInfo, MethodInstance, CodeInstance, Const const CC = Core.Compiler using Base.Meta using Base: propertynames, something, IdSet abstract type Completion end struct TextCompletion <: Completion text::String end struct KeywordCompletion <: Completion keyword::String end struct KeyvalCompletion <: Completion keyval::String end struct PathCompletion <: Completion path::String end struct ModuleCompletion <: Completion parent::Module mod::String end struct PackageCompletion <: Completion package::String end struct PropertyCompletion <: Completion value property::Symbol end struct FieldCompletion <: Completion typ::DataType field::Symbol end struct MethodCompletion <: Completion tt # may be used by an external consumer to infer return type, etc. method::Method MethodCompletion(@nospecialize(tt), method::Method) = new(tt, method) end struct BslashCompletion <: Completion bslash::String end struct ShellCompletion <: Completion text::String end struct DictCompletion <: Completion dict::AbstractDict key::String end struct KeywordArgumentCompletion <: Completion kwarg::String end # interface definition function Base.getproperty(c::Completion, name::Symbol) if name === :text return getfield(c, :text)::String elseif name === :keyword return getfield(c, :keyword)::String elseif name === :path return getfield(c, :path)::String elseif name === :parent return getfield(c, :parent)::Module elseif name === :mod return getfield(c, :mod)::String elseif name === :package return getfield(c, :package)::String elseif name === :property return getfield(c, :property)::Symbol elseif name === :field return getfield(c, :field)::Symbol elseif name === :method return getfield(c, :method)::Method elseif name === :bslash return getfield(c, :bslash)::String elseif name === :text return getfield(c, :text)::String elseif name === :key return getfield(c, :key)::String elseif name === :kwarg return getfield(c, :kwarg)::String end return getfield(c, name) end _completion_text(c::TextCompletion) = c.text _completion_text(c::KeywordCompletion) = c.keyword _completion_text(c::KeyvalCompletion) = c.keyval _completion_text(c::PathCompletion) = c.path _completion_text(c::ModuleCompletion) = c.mod _completion_text(c::PackageCompletion) = c.package _completion_text(c::PropertyCompletion) = sprint(Base.show_sym, c.property) _completion_text(c::FieldCompletion) = sprint(Base.show_sym, c.field) _completion_text(c::MethodCompletion) = repr(c.method) _completion_text(c::BslashCompletion) = c.bslash _completion_text(c::ShellCompletion) = c.text _completion_text(c::DictCompletion) = c.key _completion_text(c::KeywordArgumentCompletion) = c.kwarg*'=' completion_text(c) = _completion_text(c)::String const Completions = Tuple{Vector{Completion}, UnitRange{Int}, Bool} function completes_global(x, name) return startswith(x, name) && !('#' in x) end function appendmacro!(syms, macros, needle, endchar) for macsym in macros s = String(macsym) if endswith(s, needle) from = nextind(s, firstindex(s)) to = prevind(s, sizeof(s)-sizeof(needle)+1) push!(syms, s[from:to]*endchar) end end end function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool = false, imported::Bool = false) ssyms = names(mod, all = all, imported = imported) filter!(ffunc, ssyms) macros = filter(x -> startswith(String(x), "@" * name), ssyms) syms = String[sprint((io,s)->Base.show_sym(io, s; allow_macroname=true), s) for s in ssyms if completes_global(String(s), name)] appendmacro!(syms, macros, "_str", "\"") appendmacro!(syms, macros, "_cmd", "`") return [ModuleCompletion(mod, sym) for sym in syms] end # REPL Symbol Completions function complete_symbol(@nospecialize(ex), name::String, @nospecialize(ffunc), context_module::Module=Main) mod = context_module lookup_module = true t = Union{} val = nothing if ex !== nothing res = repl_eval_ex(ex, context_module) res === nothing && return Completion[] if res isa Const val = res.val if isa(val, Module) mod = val lookup_module = true else lookup_module = false t = typeof(val) end else lookup_module = false t = CC.widenconst(res) end end suggestions = Completion[] if lookup_module # We will exclude the results that the user does not want, as well # as excluding Main.Main.Main, etc., because that's most likely not what # the user wants p = let mod=mod, modname=nameof(mod) s->(!Base.isdeprecated(mod, s) && s != modname && ffunc(mod, s)::Bool) end # Looking for a binding in a module if mod == context_module # Also look in modules we got through `using` mods = ccall(:jl_module_usings, Any, (Any,), context_module)::Vector for m in mods append!(suggestions, filtered_mod_names(p, m::Module, name)) end append!(suggestions, filtered_mod_names(p, mod, name, true, true)) else append!(suggestions, filtered_mod_names(p, mod, name, true, false)) end elseif val !== nothing # looking for a property of an instance for property in propertynames(val, false) # TODO: support integer arguments (#36872) if property isa Symbol && startswith(string(property), name) push!(suggestions, PropertyCompletion(val, property)) end end else # Looking for a member of a type if t isa DataType && t != Any # Check for cases like Type{typeof(+)} if Base.isType(t) t = typeof(t.parameters[1]) end # Only look for fields if this is a concrete type if isconcretetype(t) fields = fieldnames(t) for field in fields isa(field, Symbol) || continue # Tuple type has ::Int field name s = string(field) if startswith(s, name) push!(suggestions, FieldCompletion(t, field)) end end end end end suggestions end function complete_from_list(T::Type, list::Vector{String}, s::Union{String,SubString{String}}) r = searchsorted(list, s) i = first(r) n = length(list) while i <= n && startswith(list[i],s) r = first(r):i i += 1 end Completion[T(kw) for kw in list[r]] end const sorted_keywords = [ "abstract type", "baremodule", "begin", "break", "catch", "ccall", "const", "continue", "do", "else", "elseif", "end", "export", "finally", "for", "function", "global", "if", "import", "let", "local", "macro", "module", "mutable struct", "primitive type", "quote", "return", "struct", "try", "using", "while"] complete_keyword(s::Union{String,SubString{String}}) = complete_from_list(KeywordCompletion, sorted_keywords, s) const sorted_keyvals = ["false", "true"] complete_keyval(s::Union{String,SubString{String}}) = complete_from_list(KeyvalCompletion, sorted_keyvals, s) function do_raw_escape(s) # escape_raw_string with delim='`' and ignoring the rule for the ending \ return replace(s, r"(\\+)`" => s"\1\\`") end function do_shell_escape(s) return Base.shell_escape_posixly(s) end function do_string_escape(s) return escape_string(s, ('\"','$')) end function complete_path(path::AbstractString; use_envpath=false, shell_escape=false, raw_escape=false, string_escape=false) @assert !(shell_escape && string_escape) if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) # if the path is just "~", don't consider the expanded username as a prefix if path == "~" dir, prefix = homedir(), "" else dir, prefix = splitdir(homedir() * path[2:end]) end else dir, prefix = splitdir(path) end files = try if isempty(dir) readdir() elseif isdir(dir) readdir(dir) else return Completion[], dir, false end catch ex ex isa Base.IOError || rethrow() return Completion[], dir, false end matches = Set{String}() for file in files if startswith(file, prefix) p = joinpath(dir, file) is_dir = try isdir(p) catch ex; ex isa Base.IOError ? false : rethrow() end push!(matches, is_dir ? file * "/" : file) end end if use_envpath && isempty(dir) # Look for files in PATH as well pathdirs = split(ENV["PATH"], @static Sys.iswindows() ? ";" : ":") for pathdir in pathdirs actualpath = try realpath(pathdir) catch ex ex isa Base.IOError || rethrow() # Bash doesn't expect every folder in PATH to exist, so neither shall we continue end if actualpath != pathdir && in(actualpath, pathdirs) # Remove paths which (after resolving links) are in the env path twice. # Many distros eg. point /bin to /usr/bin but have both in the env path. continue end filesinpath = try readdir(pathdir) catch e # Bash allows dirs in PATH that can't be read, so we should as well. if isa(e, Base.IOError) || isa(e, Base.ArgumentError) continue else # We only handle IOError and ArgumentError here rethrow() end end for file in filesinpath # In a perfect world, we would filter on whether the file is executable # here, or even on whether the current user can execute the file in question. if startswith(file, prefix) && isfile(joinpath(pathdir, file)) push!(matches, file) end end end end matches = ((shell_escape ? do_shell_escape(s) : string_escape ? do_string_escape(s) : s) for s in matches) matches = ((raw_escape ? do_raw_escape(s) : s) for s in matches) matches = Completion[PathCompletion(s) for s in matches] return matches, dir, !isempty(matches) end function complete_path(path::AbstractString, pos::Int; use_envpath=false, shell_escape=false, string_escape=false) ## TODO: enable this depwarn once Pkg is fixed #Base.depwarn("complete_path with pos argument is deprecated because the return value [2] is incorrect to use", :complete_path) paths, dir, success = complete_path(path; use_envpath, shell_escape, string_escape) if Base.Sys.isunix() && occursin(r"^~(?:/|$)", path) # if the path is just "~", don't consider the expanded username as a prefix if path == "~" dir, prefix = homedir(), "" else dir, prefix = splitdir(homedir() * path[2:end]) end else dir, prefix = splitdir(path) end startpos = pos - lastindex(prefix) + 1 Sys.iswindows() && map!(paths, paths) do c::PathCompletion # emulation for unnecessarily complicated return value, since / is a # perfectly acceptable path character which does not require quoting # but is required by Pkg's awkward parser handling return endswith(c.path, "/") ? PathCompletion(chop(c.path) * "\\\\") : c end return paths, startpos:pos, success end function complete_expanduser(path::AbstractString, r) expanded = try expanduser(path) catch e e isa ArgumentError || rethrow() path end return Completion[PathCompletion(expanded)], r, path != expanded end # Returns a range that includes the method name in front of the first non # closed start brace from the end of the string. function find_start_brace(s::AbstractString; c_start='(', c_end=')') braces = 0 r = reverse(s) i = firstindex(r) in_single_quotes = false in_double_quotes = false in_back_ticks = false in_comment = 0 while i <= ncodeunits(r) c, i = iterate(r, i) if c == '#' && i <= ncodeunits(r) && iterate(r, i)[1] == '=' c, i = iterate(r, i) # consume '=' new_comments = 1 # handle #=#=#=#, by counting =# pairs while i <= ncodeunits(r) && iterate(r, i)[1] == '#' c, i = iterate(r, i) # consume '#' iterate(r, i)[1] == '=' || break c, i = iterate(r, i) # consume '=' new_comments += 1 end if c == '=' in_comment += new_comments else in_comment -= new_comments end elseif !in_single_quotes && !in_double_quotes && !in_back_ticks && in_comment == 0 if c == c_start braces += 1 elseif c == c_end braces -= 1 elseif c == '\'' in_single_quotes = true elseif c == '"' in_double_quotes = true elseif c == '`' in_back_ticks = true end else if in_single_quotes && c == '\'' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\' in_single_quotes = false elseif in_double_quotes && c == '"' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\' in_double_quotes = false elseif in_back_ticks && c == '`' && i <= ncodeunits(r) && iterate(r, i)[1] != '\\' in_back_ticks = false elseif in_comment > 0 && c == '=' && i <= ncodeunits(r) && iterate(r, i)[1] == '#' # handle =#=#=#=, by counting #= pairs c, i = iterate(r, i) # consume '#' old_comments = 1 while i <= ncodeunits(r) && iterate(r, i)[1] == '=' c, i = iterate(r, i) # consume '=' iterate(r, i)[1] == '#' || break c, i = iterate(r, i) # consume '#' old_comments += 1 end if c == '#' in_comment -= old_comments else in_comment += old_comments end end end braces == 1 && break end braces != 1 && return 0:-1, -1 method_name_end = reverseind(s, i) startind = nextind(s, something(findprev(in(non_identifier_chars), s, method_name_end), 0))::Int return (startind:lastindex(s), method_name_end) end struct REPLInterpreterCache dict::IdDict{MethodInstance,CodeInstance} end REPLInterpreterCache() = REPLInterpreterCache(IdDict{MethodInstance,CodeInstance}()) const REPL_INTERPRETER_CACHE = REPLInterpreterCache() function get_code_cache() # XXX Avoid storing analysis results into the cache that persists across precompilation, # as [sys|pkg]image currently doesn't support serializing externally created `CodeInstance`. # Otherwise, `CodeInstance`s created by `REPLInterpreter`, that are much less optimized # that those produced by `NativeInterpreter`, will leak into the native code cache, # potentially causing runtime slowdown. # (see https://github.com/JuliaLang/julia/issues/48453). if (@ccall jl_generating_output()::Cint) == 1 return REPLInterpreterCache() else return REPL_INTERPRETER_CACHE end end struct REPLInterpreter <: CC.AbstractInterpreter repl_frame::CC.InferenceResult world::UInt inf_params::CC.InferenceParams opt_params::CC.OptimizationParams inf_cache::Vector{CC.InferenceResult} code_cache::REPLInterpreterCache function REPLInterpreter(repl_frame::CC.InferenceResult; world::UInt = Base.get_world_counter(), inf_params::CC.InferenceParams = CC.InferenceParams(), opt_params::CC.OptimizationParams = CC.OptimizationParams(), inf_cache::Vector{CC.InferenceResult} = CC.InferenceResult[], code_cache::REPLInterpreterCache = get_code_cache()) return new(repl_frame, world, inf_params, opt_params, inf_cache, code_cache) end end CC.InferenceParams(interp::REPLInterpreter) = interp.inf_params CC.OptimizationParams(interp::REPLInterpreter) = interp.opt_params CC.get_world_counter(interp::REPLInterpreter) = interp.world CC.get_inference_cache(interp::REPLInterpreter) = interp.inf_cache CC.code_cache(interp::REPLInterpreter) = CC.WorldView(interp.code_cache, CC.WorldRange(interp.world)) CC.get(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance, default) = get(wvc.cache.dict, mi, default) CC.getindex(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = getindex(wvc.cache.dict, mi) CC.haskey(wvc::CC.WorldView{REPLInterpreterCache}, mi::MethodInstance) = haskey(wvc.cache.dict, mi) CC.setindex!(wvc::CC.WorldView{REPLInterpreterCache}, ci::CodeInstance, mi::MethodInstance) = setindex!(wvc.cache.dict, ci, mi) # REPLInterpreter is only used for type analysis, so it should disable optimization entirely CC.may_optimize(::REPLInterpreter) = false # REPLInterpreter analyzes a top-level frame, so better to not bail out from it CC.bail_out_toplevel_call(::REPLInterpreter, ::CC.InferenceLoopState, ::CC.InferenceState) = false # `REPLInterpreter` aggressively resolves global bindings to enable reasonable completions # for lines like `Mod.a.|` (where `|` is the cursor position). # Aggressive binding resolution poses challenges for the inference cache validation # (until https://github.com/JuliaLang/julia/issues/40399 is implemented). # To avoid the cache validation issues, `REPLInterpreter` only allows aggressive binding # resolution for top-level frame representing REPL input code (`repl_frame`) and for child # `getproperty` frames that are constant propagated from the `repl_frame`. This works, since # a.) these frames are never cached, and # b.) their results are only observed by the non-cached `repl_frame`. # # `REPLInterpreter` also aggressively concrete evaluate `:inconsistent` calls within # `repl_frame` to provide reasonable completions for lines like `Ref(Some(42))[].|`. # Aggressive concrete evaluation allows us to get accurate type information about complex # expressions that otherwise can not be constant folded, in a safe way, i.e. it still # doesn't evaluate effectful expressions like `pop!(xs)`. # Similarly to the aggressive binding resolution, aggressive concrete evaluation doesn't # present any cache validation issues because `repl_frame` is never cached. is_repl_frame(interp::REPLInterpreter, sv::CC.InferenceState) = interp.repl_frame === sv.result # aggressive global binding resolution within `repl_frame` function CC.abstract_eval_globalref(interp::REPLInterpreter, g::GlobalRef, sv::CC.InferenceState) if is_repl_frame(interp, sv) if CC.isdefined_globalref(g) return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) end return Union{} end return @invoke CC.abstract_eval_globalref(interp::CC.AbstractInterpreter, g::GlobalRef, sv::CC.InferenceState) end function is_repl_frame_getproperty(interp::REPLInterpreter, sv::CC.InferenceState) def = sv.linfo.def def isa Method || return false def.name === :getproperty || return false sv.cached && return false return is_repl_frame(interp, sv.parent) end # aggressive global binding resolution for `getproperty(::Module, ::Symbol)` calls within `repl_frame` function CC.builtin_tfunction(interp::REPLInterpreter, @nospecialize(f), argtypes::Vector{Any}, sv::CC.InferenceState) if f === Core.getglobal && is_repl_frame_getproperty(interp, sv) if length(argtypes) == 2 a1, a2 = argtypes if isa(a1, Const) && isa(a2, Const) a1val, a2val = a1.val, a2.val if isa(a1val, Module) && isa(a2val, Symbol) g = GlobalRef(a1val, a2val) if CC.isdefined_globalref(g) return Const(ccall(:jl_get_globalref_value, Any, (Any,), g)) end return Union{} end end end end return @invoke CC.builtin_tfunction(interp::CC.AbstractInterpreter, f::Any, argtypes::Vector{Any}, sv::CC.InferenceState) end # aggressive concrete evaluation for `:inconsistent` frames within `repl_frame` function CC.concrete_eval_eligible(interp::REPLInterpreter, @nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.InferenceState) if is_repl_frame(interp, sv) neweffects = CC.Effects(result.effects; consistent=CC.ALWAYS_TRUE) result = CC.MethodCallResult(result.rt, result.edgecycle, result.edgelimited, result.edge, neweffects) end return @invoke CC.concrete_eval_eligible(interp::CC.AbstractInterpreter, f::Any, result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.InferenceState) end function resolve_toplevel_symbols!(mod::Module, src::Core.CodeInfo) newsrc = copy(src) @ccall jl_resolve_globals_in_ir( #=jl_array_t *stmts=# newsrc.code::Any, #=jl_module_t *m=# mod::Any, #=jl_svec_t *sparam_vals=# Core.svec()::Any, #=int binding_effects=# 0::Int)::Cvoid return newsrc end # lower `ex` and run type inference on the resulting top-level expression function repl_eval_ex(@nospecialize(ex), context_module::Module) lwr = try Meta.lower(context_module, ex) catch # macro expansion failed, etc. return nothing end if lwr isa Symbol return isdefined(context_module, lwr) ? Const(getfield(context_module, lwr)) : nothing end lwr isa Expr || return Const(lwr) # `ex` is literal isexpr(lwr, :thunk) || return nothing # lowered to `Expr(:error, ...)` or similar src = lwr.args[1]::Core.CodeInfo # construct top-level `MethodInstance` mi = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()); mi.specTypes = Tuple{} mi.def = context_module src = resolve_toplevel_symbols!(context_module, src) @atomic mi.uninferred = src result = CC.InferenceResult(mi) interp = REPLInterpreter(result) frame = CC.InferenceState(result, src, #=cache=#:no, interp)::CC.InferenceState # NOTE Use the fixed world here to make `REPLInterpreter` robust against # potential invalidations of `Core.Compiler` methods. Base.invoke_in_world(COMPLETION_WORLD[], CC.typeinf, interp, frame) result = frame.result.result result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead return result end # `COMPLETION_WORLD[]` will be initialized within `__init__` # (to allow us to potentially remove REPL from the sysimage in the future). # Note that inference from the `code_typed` call below will use the current world age # rather than `typemax(UInt)`, since `Base.invoke_in_world` uses the current world age # when the given world age is higher than the current one. const COMPLETION_WORLD = Ref{UInt}(typemax(UInt)) # Generate code cache for `REPLInterpreter` now: # This code cache will be available at the world of `COMPLETION_WORLD`, # assuming no invalidation will happen before initializing REPL. # Once REPL is loaded, `REPLInterpreter` will be resilient against future invalidations. code_typed(CC.typeinf, (REPLInterpreter, CC.InferenceState)) # Method completion on function call expression that look like :(max(1)) MAX_METHOD_COMPLETIONS::Int = 40 function _complete_methods(ex_org::Expr, context_module::Module, shift::Bool) funct = repl_eval_ex(ex_org.args[1], context_module) funct === nothing && return 2, nothing, [], Set{Symbol}() funct = CC.widenconst(funct) args_ex, kwargs_ex, kwargs_flag = complete_methods_args(ex_org, context_module, true, true) return kwargs_flag, funct, args_ex, kwargs_ex end function complete_methods(ex_org::Expr, context_module::Module=Main, shift::Bool=false) kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex_org, context_module, shift)::Tuple{Int, Any, Vector{Any}, Set{Symbol}} out = Completion[] kwargs_flag == 2 && return out # one of the kwargs is invalid kwargs_flag == 0 && push!(args_ex, Vararg{Any}) # allow more arguments if there is no semicolon complete_methods!(out, funct, args_ex, kwargs_ex, shift ? -2 : MAX_METHOD_COMPLETIONS, kwargs_flag == 1) return out end MAX_ANY_METHOD_COMPLETIONS::Int = 10 function recursive_explore_names!(seen::IdSet, callee_module::Module, initial_module::Module, exploredmodules::IdSet{Module}=IdSet{Module}()) push!(exploredmodules, callee_module) for name in names(callee_module; all=true, imported=true) if !Base.isdeprecated(callee_module, name) && !startswith(string(name), '#') && isdefined(initial_module, name) func = getfield(callee_module, name) if !isa(func, Module) funct = Core.Typeof(func) push!(seen, funct) elseif isa(func, Module) && func โˆ‰ exploredmodules recursive_explore_names!(seen, func, initial_module, exploredmodules) end end end end function recursive_explore_names(callee_module::Module, initial_module::Module) seen = IdSet{Any}() recursive_explore_names!(seen, callee_module, initial_module) seen end function complete_any_methods(ex_org::Expr, callee_module::Module, context_module::Module, moreargs::Bool, shift::Bool) out = Completion[] args_ex, kwargs_ex, kwargs_flag = try # this may throw, since we set default_any to false complete_methods_args(ex_org, context_module, false, false) catch ex ex isa ArgumentError || rethrow() return out end kwargs_flag == 2 && return out # one of the kwargs is invalid # moreargs determines whether to accept more args, independently of the presence of a # semicolon for the ".?(" syntax moreargs && push!(args_ex, Vararg{Any}) for seen_name in recursive_explore_names(callee_module, callee_module) complete_methods!(out, seen_name, args_ex, kwargs_ex, MAX_ANY_METHOD_COMPLETIONS, false) end if !shift # Filter out methods where all arguments are `Any` filter!(out) do c isa(c, TextCompletion) && return false isa(c, MethodCompletion) || return true sig = Base.unwrap_unionall(c.method.sig)::DataType return !all(T -> T === Any || T === Vararg{Any}, sig.parameters[2:end]) end end return out end function detect_invalid_kwarg!(kwargs_ex::Vector{Symbol}, @nospecialize(x), kwargs_flag::Int, possible_splat::Bool) n = isexpr(x, :kw) ? x.args[1] : x if n isa Symbol push!(kwargs_ex, n) return kwargs_flag end possible_splat && isexpr(x, :...) && return kwargs_flag return 2 # The kwarg is invalid end function detect_args_kwargs(funargs::Vector{Any}, context_module::Module, default_any::Bool, broadcasting::Bool) args_ex = Any[] kwargs_ex = Symbol[] kwargs_flag = 0 # kwargs_flag is: # * 0 if there is no semicolon and no invalid kwarg # * 1 if there is a semicolon and no invalid kwarg # * 2 if there are two semicolons or more, or if some kwarg is invalid, which # means that it is not of the form "bar=foo", "bar" or "bar..." for i in (1+!broadcasting):length(funargs) ex = funargs[i] if isexpr(ex, :parameters) kwargs_flag = ifelse(kwargs_flag == 0, 1, 2) # there should be at most one :parameters for x in ex.args kwargs_flag = detect_invalid_kwarg!(kwargs_ex, x, kwargs_flag, true) end elseif isexpr(ex, :kw) kwargs_flag = detect_invalid_kwarg!(kwargs_ex, ex, kwargs_flag, false) else if broadcasting # handle broadcasting, but only handle number of arguments instead of # argument types push!(args_ex, Any) else argt = repl_eval_ex(ex, context_module) if argt !== nothing push!(args_ex, CC.widenconst(argt)) elseif default_any push!(args_ex, Any) else throw(ArgumentError("argument not found")) end end end end return args_ex, Set{Symbol}(kwargs_ex), kwargs_flag end is_broadcasting_expr(ex::Expr) = ex.head === :. && isexpr(ex.args[2], :tuple) function complete_methods_args(ex::Expr, context_module::Module, default_any::Bool, allow_broadcasting::Bool) if allow_broadcasting && is_broadcasting_expr(ex) return detect_args_kwargs((ex.args[2]::Expr).args, context_module, default_any, true) end return detect_args_kwargs(ex.args, context_module, default_any, false) end function complete_methods!(out::Vector{Completion}, @nospecialize(funct), args_ex::Vector{Any}, kwargs_ex::Set{Symbol}, max_method_completions::Int, exact_nargs::Bool) # Input types and number of arguments t_in = Tuple{funct, args_ex...} m = Base._methods_by_ftype(t_in, nothing, max_method_completions, Base.get_world_counter(), #=ambig=# true, Ref(typemin(UInt)), Ref(typemax(UInt)), Ptr{Int32}(C_NULL)) if !isa(m, Vector) push!(out, TextCompletion(sprint(Base.show_signature_function, funct) * "( too many methods, use SHIFT-TAB to show )")) return end for match in m # TODO: if kwargs_ex, filter out methods without kwargs? push!(out, MethodCompletion(match.spec_types, match.method)) end # TODO: filter out methods with wrong number of arguments if `exact_nargs` is set end include("latex_symbols.jl") include("emoji_symbols.jl") const non_identifier_chars = [" \t\n\r\"\\'`\$><=:;|&{}()[],+-*/?%^~"...] const whitespace_chars = [" \t\n\r"...] # "\"'`"... is added to whitespace_chars as non of the bslash_completions # characters contain any of these characters. It prohibits the # bslash_completions function to try and complete on escaped characters in strings const bslash_separators = [whitespace_chars..., "\"'`"...] const subscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\_") && length(k)==3) const subscript_regex = Regex("^\\\\_[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(subscripts)) * "]+\\z") const superscripts = Dict(k[3]=>v[1] for (k,v) in latex_symbols if startswith(k, "\\^") && length(k)==3) const superscript_regex = Regex("^\\\\\\^[" * join(isdigit(k) || isletter(k) ? "$k" : "\\$k" for k in keys(superscripts)) * "]+\\z") # Aux function to detect whether we're right after a # using or import keyword function afterusing(string::String, startpos::Int) (isempty(string) || startpos == 0) && return false str = string[1:prevind(string,startpos)] isempty(str) && return false rstr = reverse(str) r = findfirst(r"\s(gnisu|tropmi)\b", rstr) r === nothing && return false fr = reverseind(str, last(r)) return occursin(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end function close_path_completion(dir, paths, str, pos) length(paths) == 1 || return false # Only close if there's a single choice... path = (paths[1]::PathCompletion).path path = unescape_string(replace(path, "\\\$"=>"\$")) path = joinpath(dir, path) # ...except if it's a directory... try isdir(path) catch e e isa Base.IOError || rethrow() # `path` cannot be determined to be a file end && return false # ...and except if there's already a " at the cursor. return lastindex(str) <= pos || str[nextind(str, pos)] != '"' end function bslash_completions(string::String, pos::Int) slashpos = something(findprev(isequal('\\'), string, pos), 0) if (something(findprev(in(bslash_separators), string, pos), 0) < slashpos && !(1 < slashpos && (string[prevind(string, slashpos)]=='\\'))) # latex / emoji symbol substitution s = string[slashpos:pos] latex = get(latex_symbols, s, "") if !isempty(latex) # complete an exact match return (true, (Completion[BslashCompletion(latex)], slashpos:pos, true)) elseif occursin(subscript_regex, s) sub = map(c -> subscripts[c], s[3:end]) return (true, (Completion[BslashCompletion(sub)], slashpos:pos, true)) elseif occursin(superscript_regex, s) sup = map(c -> superscripts[c], s[3:end]) return (true, (Completion[BslashCompletion(sup)], slashpos:pos, true)) end emoji = get(emoji_symbols, s, "") if !isempty(emoji) return (true, (Completion[BslashCompletion(emoji)], slashpos:pos, true)) end # return possible matches; these cannot be mixed with regular # Julian completions as only latex / emoji symbols contain the leading \ if startswith(s, "\\:") # emoji namelist = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols)) else # latex namelist = Iterators.filter(k -> startswith(k, s), keys(latex_symbols)) end return (true, (Completion[BslashCompletion(name) for name in sort!(collect(namelist))], slashpos:pos, true)) end return (false, (Completion[], 0:-1, false)) end function dict_identifier_key(str::String, tag::Symbol, context_module::Module=Main) if tag === :string str_close = str*"\"" elseif tag === :cmd str_close = str*"`" else str_close = str end frange, end_of_identifier = find_start_brace(str_close, c_start='[', c_end=']') isempty(frange) && return (nothing, nothing, nothing) obj = context_module for name in split(str[frange[1]:end_of_identifier], '.') Base.isidentifier(name) || return (nothing, nothing, nothing) sym = Symbol(name) isdefined(obj, sym) || return (nothing, nothing, nothing) obj = getfield(obj, sym) end (isa(obj, AbstractDict) && length(obj)::Int < 1_000_000) || return (nothing, nothing, nothing) begin_of_key = something(findnext(!isspace, str, nextind(str, end_of_identifier) + 1), # +1 for [ lastindex(str)+1) return (obj::AbstractDict, str[begin_of_key:end], begin_of_key) end # This needs to be a separate non-inlined function, see #19441 @noinline function find_dict_matches(identifier::AbstractDict, partial_key) matches = String[] for key in keys(identifier) rkey = repr(key) startswith(rkey,partial_key) && push!(matches,rkey) end return matches end # Identify an argument being completed in a method call. If the argument is empty, method # suggestions will be provided instead of argument completions. function identify_possible_method_completion(partial, last_idx) fail = 0:-1, Expr(:nothing), 0:-1, 0 # First, check that the last punctuation is either ',', ';' or '(' idx_last_punct = something(findprev(x -> ispunct(x) && x != '_' && x != '!', partial, last_idx), 0)::Int idx_last_punct == 0 && return fail last_punct = partial[idx_last_punct] last_punct == ',' || last_punct == ';' || last_punct == '(' || return fail # Then, check that `last_punct` is only followed by an identifier or nothing before_last_word_start = something(findprev(in(non_identifier_chars), partial, last_idx), 0) before_last_word_start == 0 && return fail all(isspace, @view partial[nextind(partial, idx_last_punct):before_last_word_start]) || return fail # Check that `last_punct` is either the last '(' or placed after a previous '(' frange, method_name_end = find_start_brace(@view partial[1:idx_last_punct]) method_name_end โˆˆ frange || return fail # Strip the preceding ! operators, if any, and close the expression with a ')' s = replace(partial[frange], r"\G\!+([^=\(]+)" => s"\1"; count=1) * ')' ex = Meta.parse(s, raise=false, depwarn=false) isa(ex, Expr) || return fail # `wordrange` is the position of the last argument to complete wordrange = nextind(partial, before_last_word_start):last_idx return frange, ex, wordrange, method_name_end end # Provide completion for keyword arguments in function calls function complete_keyword_argument(partial, last_idx, context_module) frange, ex, wordrange, = identify_possible_method_completion(partial, last_idx) fail = Completion[], 0:-1, frange ex.head === :call || is_broadcasting_expr(ex) || return fail kwargs_flag, funct, args_ex, kwargs_ex = _complete_methods(ex, context_module, true)::Tuple{Int, Any, Vector{Any}, Set{Symbol}} kwargs_flag == 2 && return fail # one of the previous kwargs is invalid methods = Completion[] complete_methods!(methods, funct, Any[Vararg{Any}], kwargs_ex, -1, kwargs_flag == 1) # TODO: use args_ex instead of Any[Vararg{Any}] and only provide kwarg completion for # method calls compatible with the current arguments. # For each method corresponding to the function call, provide completion suggestions # for each keyword that starts like the last word and that is not already used # previously in the expression. The corresponding suggestion is "kwname=". # If the keyword corresponds to an existing name, also include "kwname" as a suggestion # since the syntax "foo(; kwname)" is equivalent to "foo(; kwname=kwname)". last_word = partial[wordrange] # the word to complete kwargs = Set{String}() for m in methods m::MethodCompletion possible_kwargs = Base.kwarg_decl(m.method) current_kwarg_candidates = String[] for _kw in possible_kwargs kw = String(_kw) if !endswith(kw, "...") && startswith(kw, last_word) && _kw โˆ‰ kwargs_ex push!(current_kwarg_candidates, kw) end end union!(kwargs, current_kwarg_candidates) end suggestions = Completion[KeywordArgumentCompletion(kwarg) for kwarg in kwargs] append!(suggestions, complete_symbol(nothing, last_word, Returns(true), context_module)) append!(suggestions, complete_keyval(last_word)) return sort!(suggestions, by=completion_text), wordrange end function project_deps_get_completion_candidates(pkgstarts::String, project_file::String) loading_candidates = String[] d = Base.parsed_toml(project_file) pkg = get(d, "name", nothing)::Union{String, Nothing} if pkg !== nothing && startswith(pkg, pkgstarts) push!(loading_candidates, pkg) end deps = get(d, "deps", nothing)::Union{Dict{String, Any}, Nothing} if deps !== nothing for (pkg, _) in deps startswith(pkg, pkgstarts) && push!(loading_candidates, pkg) end end return Completion[PackageCompletion(name) for name in loading_candidates] end function complete_identifiers!(suggestions::Vector{Completion}, @nospecialize(ffunc), context_module::Module, string::String, name::String, pos::Int, dotpos::Int, startpos::Int; comp_keywords=false) ex = nothing if comp_keywords append!(suggestions, complete_keyword(name)) append!(suggestions, complete_keyval(name)) end if dotpos > 1 && string[dotpos] == '.' s = string[1:prevind(string, dotpos)] # First see if the whole string up to `pos` is a valid expression. If so, use it. ex = Meta.parse(s, raise=false, depwarn=false) if isexpr(ex, :incomplete) s = string[startpos:pos] # Heuristic to find the start of the expression. TODO: This would be better # done with a proper error-recovering parser. if 0 < startpos <= lastindex(string) && string[startpos] == '.' i = prevind(string, startpos) while 0 < i c = string[i] if c in (')', ']') if c == ')' c_start = '(' c_end = ')' elseif c == ']' c_start = '[' c_end = ']' end frange, end_of_identifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) isempty(frange) && break # unbalanced parens startpos = first(frange) i = prevind(string, startpos) elseif c in ('\'', '\"', '\`') s = "$c$c"*string[startpos:pos] break else break end s = string[startpos:pos] end end if something(findlast(in(non_identifier_chars), s), 0) < something(findlast(isequal('.'), s), 0) lookup_name, name = rsplit(s, ".", limit=2) name = String(name) ex = Meta.parse(lookup_name, raise=false, depwarn=false) end isexpr(ex, :incomplete) && (ex = nothing) elseif isexpr(ex, (:using, :import)) arg1 = ex.args[1] if isexpr(arg1, :.) # We come here for cases like: # - `string`: "using Mod1.Mod2.M" # - `ex`: :(using Mod1.Mod2) # - `name`: "M" # Now we transform `ex` to `:(Mod1.Mod2)` to allow `complete_symbol` to # complete for inner modules whose name starts with `M`. # Note that `ffunc` is set to `module_filter` within `completions` ex = nothing firstdot = true for arg = arg1.args if arg === :. # override `context_module` if multiple `.` accessors are used if firstdot firstdot = false else context_module = parentmodule(context_module) end elseif arg isa Symbol if ex === nothing ex = arg else ex = Expr(:., ex, QuoteNode(arg)) end else # invalid expression ex = nothing break end end end elseif isexpr(ex, :call) && length(ex.args) > 1 isinfix = s[end] != ')' # A complete call expression that does not finish with ')' is an infix call. if !isinfix # Handle infix call argument completion of the form bar + foo(qux). frange, end_of_identifier = find_start_brace(@view s[1:prevind(s, end)]) isinfix = Meta.parse(@view(s[frange[1]:end]), raise=false, depwarn=false) == ex.args[end] end if isinfix ex = ex.args[end] end elseif isexpr(ex, :macrocall) && length(ex.args) > 1 # allow symbol completions within potentially incomplete macrocalls if s[end] โ‰  '`' && s[end] โ‰  ')' ex = ex.args[end] end end end append!(suggestions, complete_symbol(ex, name, ffunc, context_module)) return sort!(unique(suggestions), by=completion_text), (dotpos+1):pos, true end function completions(string::String, pos::Int, context_module::Module=Main, shift::Bool=true) # First parse everything up to the current position partial = string[1:pos] inc_tag = Base.incomplete_tag(Meta.parse(partial, raise=false, depwarn=false)) # ?(x, y)TAB lists methods you can call with these objects # ?(x, y TAB lists methods that take these objects as the first two arguments # MyModule.?(x, y)TAB restricts the search to names in MyModule rexm = match(r"(\w+\.|)\?\((.*)$", partial) if rexm !== nothing # Get the module scope if isempty(rexm.captures[1]) callee_module = context_module else modname = Symbol(rexm.captures[1][1:end-1]) if isdefined(context_module, modname) callee_module = getfield(context_module, modname) if !isa(callee_module, Module) callee_module = context_module end else callee_module = context_module end end moreargs = !endswith(rexm.captures[2], ')') callstr = "_(" * rexm.captures[2] if moreargs callstr *= ')' end ex_org = Meta.parse(callstr, raise=false, depwarn=false) if isa(ex_org, Expr) return complete_any_methods(ex_org, callee_module::Module, context_module, moreargs, shift), (0:length(rexm.captures[1])+1) .+ rexm.offset, false end end # if completing a key in a Dict identifier, partial_key, loc = dict_identifier_key(partial, inc_tag, context_module) if identifier !== nothing matches = find_dict_matches(identifier, partial_key) length(matches)==1 && (lastindex(string) <= pos || string[nextind(string,pos)] != ']') && (matches[1]*=']') length(matches)>0 && return Completion[DictCompletion(identifier, match) for match in sort!(matches)], loc::Int:pos, true end ffunc = Returns(true) suggestions = Completion[] # Check if this is a var"" string macro that should be completed like # an identifier rather than a string. # TODO: It would be nice for the parser to give us more information here # so that we can lookup the macro by identity rather than pattern matching # its invocation. varrange = findprev("var\"", string, pos) if varrange !== nothing ok, ret = bslash_completions(string, pos) ok && return ret startpos = first(varrange) + 4 dotpos = something(findprev(isequal('.'), string, first(varrange)-1), 0) name = string[startpos:pos] return complete_identifiers!(Completion[], ffunc, context_module, string, name, pos, dotpos, startpos) elseif inc_tag === :cmd # TODO: should this call shell_completions instead of partially reimplementing it? let m = match(r"[\t\n\r\"`><=*?|]| (?!\\)", reverse(partial)) # fuzzy shell_parse in reverse startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos scs::String = string[r] expanded = complete_expanduser(scs, r) expanded[3] && return expanded # If user expansion available, return it path::String = replace(scs, r"(\\+)\g1(\\?)`" => "\1\2`") # fuzzy unescape_raw_string: match an even number of \ before ` and replace with half as many # This expansion with "\\ "=>' ' replacement and shell_escape=true # assumes the path isn't further quoted within the cmd backticks. path = replace(path, r"\\ " => " ", r"\$" => "\$") # fuzzy shell_parse (reversed by shell_escape_posixly) paths, dir, success = complete_path(path, shell_escape=true, raw_escape=true) if success && !isempty(dir) let dir = do_raw_escape(do_shell_escape(dir)) # if escaping of dir matches scs prefix, remove that from the completions # otherwise make it the whole completion if endswith(dir, "/") && startswith(scs, dir) r = (startpos + sizeof(dir)):pos elseif startswith(scs, dir * "/") r = nextind(string, startpos + sizeof(dir)):pos else map!(paths, paths) do c::PathCompletion return PathCompletion(dir * "/" * c.path) end end end end return sort!(paths, by=p->p.path), r, success end elseif inc_tag === :string # Find first non-escaped quote let m = match(r"\"(?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos scs::String = string[r] expanded = complete_expanduser(scs, r) expanded[3] && return expanded # If user expansion available, return it path = try unescape_string(replace(scs, "\\\$"=>"\$")) catch ex ex isa ArgumentError || rethrow() nothing end if !isnothing(path) paths, dir, success = complete_path(path::String, string_escape=true) if close_path_completion(dir, paths, path, pos) paths[1] = PathCompletion((paths[1]::PathCompletion).path * "\"") end if success && !isempty(dir) let dir = do_string_escape(dir) # if escaping of dir matches scs prefix, remove that from the completions # otherwise make it the whole completion if endswith(dir, "/") && startswith(scs, dir) r = (startpos + sizeof(dir)):pos elseif startswith(scs, dir * "/") r = nextind(string, startpos + sizeof(dir)):pos else map!(paths, paths) do c::PathCompletion return PathCompletion(dir * "/" * c.path) end end end end # Fallthrough allowed so that Latex symbols can be completed in strings success && return sort!(paths, by=p->p.path), r, success end end end ok, ret = bslash_completions(string, pos) ok && return ret # Make sure that only bslash_completions is working on strings inc_tag === :string && return Completion[], 0:-1, false if inc_tag === :other frange, ex, wordrange, method_name_end = identify_possible_method_completion(partial, pos) if last(frange) != -1 && all(isspace, @view partial[wordrange]) # no last argument to complete if ex.head === :call return complete_methods(ex, context_module, shift), first(frange):method_name_end, false elseif is_broadcasting_expr(ex) return complete_methods(ex, context_module, shift), first(frange):(method_name_end - 1), false end end elseif inc_tag === :comment return Completion[], 0:-1, false end # Check whether we can complete a keyword argument in a function call kwarg_completion, wordrange = complete_keyword_argument(partial, pos, context_module) isempty(wordrange) || return kwarg_completion, wordrange, !isempty(kwarg_completion) dotpos = something(findprev(isequal('.'), string, pos), 0) startpos = nextind(string, something(findprev(in(non_identifier_chars), string, pos), 0)) # strip preceding ! operator if (m = match(r"\G\!+", partial, startpos)) isa RegexMatch startpos += length(m.match) end name = string[max(startpos, dotpos+1):pos] comp_keywords = !isempty(name) && startpos > dotpos if afterusing(string, startpos) # We're right after using or import. Let's look only for packages # and modules we can reach from here # If there's no dot, we're in toplevel, so we should # also search for packages s = string[startpos:pos] if dotpos <= startpos for dir in Base.load_path() if basename(dir) in Base.project_names && isfile(dir) append!(suggestions, project_deps_get_completion_candidates(s, dir)) end isdir(dir) || continue for pname in readdir(dir) if pname[1] != '.' && pname != "METADATA" && pname != "REQUIRE" && startswith(pname, s) # Valid file paths are # <Mod>.jl # <Mod>/src/<Mod>.jl # <Mod>.jl/src/<Mod>.jl if isfile(joinpath(dir, pname)) endswith(pname, ".jl") && push!(suggestions, PackageCompletion(pname[1:prevind(pname, end-2)])) else mod_name = if endswith(pname, ".jl") pname[1:prevind(pname, end-2)] else pname end if isfile(joinpath(dir, pname, "src", "$mod_name.jl")) push!(suggestions, PackageCompletion(mod_name)) end end end end end end ffunc = module_filter comp_keywords = false end startpos == 0 && (pos = -1) dotpos < startpos && (dotpos = startpos - 1) return complete_identifiers!(suggestions, ffunc, context_module, string, name, pos, dotpos, startpos; comp_keywords) end module_filter(mod::Module, x::Symbol) = Base.isbindingresolved(mod, x) && isdefined(mod, x) && isa(getglobal(mod, x), Module) function shell_completions(string, pos) # First parse everything up to the current position scs = string[1:pos] args, last_arg_start = try Base.shell_parse(scs, true)::Tuple{Expr,Int} catch ex ex isa ArgumentError || ex isa ErrorException || rethrow() return Completion[], 0:-1, false end ex = args.args[end]::Expr # Now look at the last thing we parsed isempty(ex.args) && return Completion[], 0:-1, false lastarg = ex.args[end] # As Base.shell_parse throws away trailing spaces (unless they are escaped), # we need to special case here. # If the last char was a space, but shell_parse ignored it search on "". if isexpr(lastarg, :incomplete) || isexpr(lastarg, :error) partial = string[last_arg_start:pos] ret, range = completions(partial, lastindex(partial)) range = range .+ (last_arg_start - 1) return ret, range, true elseif endswith(scs, ' ') && !endswith(scs, "\\ ") r = pos+1:pos paths, dir, success = complete_path("", use_envpath=false, shell_escape=true) return paths, r, success elseif all(arg -> arg isa AbstractString, ex.args) # Join these and treat this as a path path::String = join(ex.args) r = last_arg_start:pos # Also try looking into the env path if the user wants to complete the first argument use_envpath = length(args.args) < 2 # TODO: call complete_expanduser here? paths, dir, success = complete_path(path, use_envpath=use_envpath, shell_escape=true) if success && !isempty(dir) let dir = do_shell_escape(dir) # if escaping of dir matches scs prefix, remove that from the completions # otherwise make it the whole completion partial = string[last_arg_start:pos] if endswith(dir, "/") && startswith(partial, dir) r = (last_arg_start + sizeof(dir)):pos elseif startswith(partial, dir * "/") r = nextind(string, last_arg_start + sizeof(dir)):pos else map!(paths, paths) do c::PathCompletion return PathCompletion(dir * "/" * c.path) end end end end return paths, r, success end return Completion[], 0:-1, false end function UndefVarError_hint(io::IO, ex::UndefVarError) var = ex.var if var === :or print(io, "\nsuggestion: Use `||` for short-circuiting boolean OR.") elseif var === :and print(io, "\nsuggestion: Use `&&` for short-circuiting boolean AND.") elseif var === :help println(io) # Show friendly help message when user types help or help() and help is undefined show(io, MIME("text/plain"), Base.Docs.parsedoc(Base.Docs.keywords[:help])) elseif var === :quit print(io, "\nsuggestion: To exit Julia, use Ctrl-D, or type exit() and press enter.") end end function __init__() Base.Experimental.register_error_hint(UndefVarError_hint, UndefVarError) COMPLETION_WORLD[] = Base.get_world_counter() nothing end end # module u���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/latex_symbols.jl�����# This file is a part of Julia. License is MIT: https://julialang.org/license # Mapping from LaTeX math symbol to the corresponding Unicode codepoint. # This is used for tab substitution in the REPL. # The initial symbol listing was generated from the W3C symbol mapping file: # http://www.w3.org/Math/characters/unicode.xml # by the following Julia script: #= import REPL using LightXML xdoc = parse_file("unicode.xml") latexsym = [] Ls = Set() for c in child_nodes(root(xdoc)) if name(c) == "character" && is_elementnode(c) ce = XMLElement(c) latex = nothing for el in ("AMS", "IEEE", "mathlatex", "latex") latex = find_element(ce, el) latex !== nothing && break end if latex !== nothing L = strip(content(latex)) id = attribute(ce, "id") U = string(map(s -> Char(parse(Int, s, base = 16)), split(id[2:end], "-"))...) if occursin(r"^\\[A-Za-z]+$", L) && !isa(U,String) if L in Ls println("# duplicated symbol $L ($id)") else if U[1] == '\u22a5' # unicode.xml incorrectly uses \perp for \bot L = "\\bot" elseif U[1] == '\u21be' L = "\\upharpoonright" elseif U[1] == '\u21bf' L = "\\upharpoonleft" end push!(latexsym, (L, U)) push!(Ls, L) end end end end end println("# ", length(latexsym), " symbols generated from unicode.xml") for (L, U) in latexsym println(" \"$(escape_string(L))\" => \"$(escape_string(U))\",") end =# # Additional symbols were generated from the unicode-math LaTeX package # symbol listing, using the following script: #= fname = "unicode-math-table.tex" isfile(fname) || download("http://mirror.math.ku.edu/tex-archive/macros/latex/contrib/unicode-math/$fname", fname) const latex_strings = Set(values(REPL.REPLCompletions.latex_symbols)) open(fname) do f for L in eachline(f) x = map(s -> rstrip(s, [' ','\t','\n']), split(replace(L, r"[{}\"]+" => "\t"), "\t")) c = Char(parse(Int, x[2], base = 16)) if (Base.is_id_char(c) || Base.isoperator(Symbol(c))) && string(c) โˆ‰ latex_strings && !isascii(c) tabcomname = escape_string(x[3]) if startswith(tabcomname, "\\\\math") tabcomname = string("\\\\", tabcomname[7:end]) end println(" \"", tabcomname, "\" => \"", escape_string("$c"), "\", # ", x[5]) end end end =# # Finally, we also add some symbols manually (at the top) as needed, # and edited others for consistency (e.g. #21646 and #14751). # When a symbol has several completions, a canonical reverse mapping is # specified at the bottom of this file. The complete reverse mapping is # generated lazily in docview.jl. # "font" prefixes const bold = "\\bf" const italic = "\\it" const bolditalic = "\\bi" const blackboard = "\\bb" const italicblackboard = "\\bbi" const script = "\\scr" const boldscript = "\\bscr" const sans = "\\sans" const boldsans = "\\bsans" const italicsans = "\\isans" const bolditalicsans = "\\bisans" const frak = "\\frak" const boldfrak = "\\bfrak" const mono = "\\tt" const latex_symbols = Dict( # manual additions: "\\sqrt" => "\u221A", "\\cbrt" => "\u221B", "\\female" => "โ™€", "\\mars" => "โ™‚", "\\pprime" => "โ€ณ", "\\ppprime" => "โ€ด", "\\pppprime" => "โ—", "\\backpprime" => "โ€ถ", "\\backppprime" => "โ€ท", "\\xor" => "โŠป", "\\nand" => "โŠผ", "\\nor" => "โŠฝ", "\\iff" => "โŸบ", "\\implies" => "โŸน", "\\impliedby" => "โŸธ", "\\to" => "โ†’", "\\euler" => "โ„ฏ", "\\ohm" => "โ„ฆ", # Superscripts "\\^0" => "โฐ", "\\^1" => "ยน", "\\^2" => "ยฒ", "\\^3" => "ยณ", "\\^4" => "โด", "\\^5" => "โต", "\\^6" => "โถ", "\\^7" => "โท", "\\^8" => "โธ", "\\^9" => "โน", "\\^+" => "โบ", "\\^-" => "โป", "\\^=" => "โผ", "\\^(" => "โฝ", "\\^)" => "โพ", "\\^a" => "แตƒ", "\\^b" => "แต‡", "\\^c" => "แถœ", "\\^d" => "แตˆ", "\\^e" => "แต‰", "\\^f" => "แถ ", "\\^g" => "แต", "\\^h" => "สฐ", "\\^i" => "โฑ", "\\^j" => "สฒ", "\\^k" => "แต", "\\^l" => "หก", "\\^m" => "แต", "\\^n" => "โฟ", "\\^o" => "แต’", "\\^p" => "แต–", "\\^r" => "สณ", "\\^s" => "หข", "\\^t" => "แต—", "\\^u" => "แต˜", "\\^v" => "แต›", "\\^w" => "สท", "\\^x" => "หฃ", "\\^y" => "สธ", "\\^z" => "แถป", "\\^A" => "แดฌ", "\\^B" => "แดฎ", "\\^D" => "แดฐ", "\\^E" => "แดฑ", "\\^G" => "แดณ", "\\^H" => "แดด", "\\^I" => "แดต", "\\^J" => "แดถ", "\\^K" => "แดท", "\\^L" => "แดธ", "\\^M" => "แดน", "\\^N" => "แดบ", "\\^O" => "แดผ", "\\^P" => "แดพ", "\\^R" => "แดฟ", "\\^T" => "แต€", "\\^U" => "แต", "\\^V" => "โฑฝ", "\\^W" => "แต‚", "\\^alpha" => "แต…", "\\^beta" => "แต", "\\^gamma" => "แตž", "\\^delta" => "แตŸ", "\\^epsilon" => "แต‹", "\\^theta" => "แถฟ", "\\^iota" => "แถฅ", "\\^phi" => "แต ", "\\^chi" => "แตก", "\\^ltphi" => "แถฒ", "\\^uparrow" => "๊œ›", "\\^downarrow" => "๊œœ", "\\^!" => "๊œ", # Subscripts "\\_0" => "โ‚€", "\\_1" => "โ‚", "\\_2" => "โ‚‚", "\\_3" => "โ‚ƒ", "\\_4" => "โ‚„", "\\_5" => "โ‚…", "\\_6" => "โ‚†", "\\_7" => "โ‚‡", "\\_8" => "โ‚ˆ", "\\_9" => "โ‚‰", "\\_+" => "โ‚Š", "\\_-" => "โ‚‹", "\\_=" => "โ‚Œ", "\\_(" => "โ‚", "\\_)" => "โ‚Ž", "\\_a" => "โ‚", "\\_e" => "โ‚‘", "\\_h" => "โ‚•", "\\_i" => "แตข", "\\_j" => "โฑผ", "\\_k" => "โ‚–", "\\_l" => "โ‚—", "\\_m" => "โ‚˜", "\\_n" => "โ‚™", "\\_o" => "โ‚’", "\\_p" => "โ‚š", "\\_r" => "แตฃ", "\\_s" => "โ‚›", "\\_t" => "โ‚œ", "\\_u" => "แตค", "\\_v" => "แตฅ", "\\_x" => "โ‚“", "\\_schwa" => "โ‚”", "\\_beta" => "แตฆ", "\\_gamma" => "แตง", "\\_rho" => "แตจ", "\\_phi" => "แตฉ", "\\_chi" => "แตช", # Misc. Math and Physics "\\ldots" => "โ€ฆ", "\\hbar" => "ฤง", "\\del" => "โˆ‡", "\\sout" => "ฬถ",# ulem package, same as Elzbar "\\euro" => "โ‚ฌ", # 732 symbols generated from unicode.xml "\\exclamdown" => "ยก", "\\sterling" => "ยฃ", "\\yen" => "ยฅ", "\\brokenbar" => "ยฆ", "\\S" => "ยง", "\\copyright" => "ยฉ", "\\ordfeminine" => "ยช", "\\neg" => "ยฌ", "\\circledR" => "ยฎ", "\\highminus" => "ยฏ", # APL "high minus", or non-combining macron above "\\degree" => "ยฐ", "\\pm" => "ยฑ", "\\P" => "ยถ", "\\cdotp" => "ยท", "\\ordmasculine" => "ยบ", "\\questiondown" => "ยฟ", "\\AA" => "ร…", "\\AE" => "ร†", "\\DH" => "ร", "\\times" => "ร—", "\\O" => "ร˜", "\\TH" => "รž", "\\ss" => "รŸ", "\\aa" => "รฅ", "\\ae" => "รฆ", "\\eth" => "รฐ", "\\dh" => "รฐ", "\\div" => "รท", "\\o" => "รธ", "\\th" => "รพ", "\\DJ" => "ฤ", "\\dj" => "ฤ‘", "\\imath" => "ฤฑ", "\\jmath" => "ศท", "\\L" => "ล", "\\l" => "ล‚", "\\NG" => "ลŠ", "\\ng" => "ล‹", "\\OE" => "ล’", "\\oe" => "ล“", "\\hvlig" => "ฦ•", "\\nrleg" => "ฦž", "\\doublepipe" => "ว‚", "\\trna" => "ษ", "\\trnsa" => "ษ’", "\\openo" => "ษ”", "\\rtld" => "ษ–", "\\schwa" => "ษ™", "\\varepsilon" => "ฮต", "\\pgamma" => "ษฃ", "\\pbgam" => "ษค", "\\trnh" => "ษฅ", "\\btdl" => "ษฌ", "\\rtll" => "ษญ", "\\trnm" => "ษฏ", "\\trnmlr" => "ษฐ", "\\ltlmr" => "ษฑ", "\\ltln" => "ษฒ", "\\rtln" => "ษณ", "\\clomeg" => "ษท", "\\ltphi" => "ษธ", # latin ฯ• "\\trnr" => "ษน", "\\trnrl" => "ษบ", "\\rttrnr" => "ษป", "\\rl" => "ษผ", "\\rtlr" => "ษฝ", "\\fhr" => "ษพ", "\\rtls" => "ส‚", "\\esh" => "สƒ", "\\trnt" => "ส‡", "\\rtlt" => "สˆ", "\\pupsil" => "สŠ", "\\pscrv" => "ส‹", "\\invv" => "สŒ", "\\invw" => "ส", "\\trny" => "สŽ", "\\rtlz" => "ส", "\\yogh" => "ส’", "\\glst" => "ส”", "\\reglst" => "ส•", "\\inglst" => "ส–", "\\turnk" => "สž", "\\dyogh" => "สค", "\\tesh" => "สง", "\\rasp" => "สผ", "\\verts" => "หˆ", "\\verti" => "หŒ", "\\lmrk" => "ห", "\\hlmrk" => "ห‘", "\\sbrhr" => "ห’", "\\sblhr" => "ห“", "\\rais" => "ห”", "\\low" => "ห•", "\\u" => "ห˜", "\\tildelow" => "หœ", "\\grave" => "ฬ€", "\\acute" => "ฬ", "\\hat" => "ฬ‚", "\\tilde" => "ฬƒ", "\\bar" => "ฬ„", "\\breve" => "ฬ†", "\\dot" => "ฬ‡", "\\ddot" => "ฬˆ", "\\ocirc" => "ฬŠ", "\\H" => "ฬ‹", "\\check" => "ฬŒ", "\\palh" => "ฬก", "\\rh" => "ฬข", "\\c" => "ฬง", "\\k" => "ฬจ", "\\sbbrg" => "ฬช", "\\strike" => "ฬถ", "\\Alpha" => "ฮ‘", "\\Beta" => "ฮ’", "\\Gamma" => "ฮ“", "\\Delta" => "ฮ”", "\\Epsilon" => "ฮ•", "\\Zeta" => "ฮ–", "\\Eta" => "ฮ—", "\\Theta" => "ฮ˜", "\\Iota" => "ฮ™", "\\Kappa" => "ฮš", "\\Lambda" => "ฮ›", "\\Xi" => "ฮž", "\\Pi" => "ฮ ", "\\Rho" => "ฮก", "\\Sigma" => "ฮฃ", "\\Tau" => "ฮค", "\\Upsilon" => "ฮฅ", "\\Phi" => "ฮฆ", "\\Chi" => "ฮง", "\\Psi" => "ฮจ", "\\Omega" => "ฮฉ", "\\alpha" => "ฮฑ", "\\beta" => "ฮฒ", "\\gamma" => "ฮณ", "\\delta" => "ฮด", "\\zeta" => "ฮถ", "\\eta" => "ฮท", "\\theta" => "ฮธ", "\\iota" => "ฮน", "\\kappa" => "ฮบ", "\\lambda" => "ฮป", "\\mu" => "ฮผ", "\\nu" => "ฮฝ", "\\xi" => "ฮพ", "\\pi" => "ฯ€", "\\rho" => "ฯ", "\\varsigma" => "ฯ‚", "\\sigma" => "ฯƒ", "\\tau" => "ฯ„", "\\upsilon" => "ฯ…", "\\varphi" => "ฯ†", "\\chi" => "ฯ‡", "\\psi" => "ฯˆ", "\\omega" => "ฯ‰", "\\vartheta" => "ฯ‘", "\\phi" => "ฯ•", "\\varpi" => "ฯ–", "\\Stigma" => "ฯš", "\\Digamma" => "ฯœ", "\\digamma" => "ฯ", "\\Koppa" => "ฯž", "\\Sampi" => "ฯ ", "\\varkappa" => "ฯฐ", "\\varrho" => "ฯฑ", "\\varTheta" => "ฯด", "\\epsilon" => "ฯต", "\\backepsilon" => "ฯถ", "\\enspace" => "โ€‚", "\\quad" => "โ€ƒ", "\\thickspace" => "โ€…", "\\thinspace" => "โ€‰", "\\hspace" => "โ€Š", "\\endash" => "โ€“", "\\emdash" => "โ€”", "\\Vert" => "โ€–", "\\lq" => "โ€˜", "\\rq" => "โ€™", "\\reapos" => "โ€›", "\\ldq" => "โ€œ", "\\rdq" => "โ€", "\\dagger" => "โ€ ", "\\ddagger" => "โ€ก", "\\bullet" => "โ€ข", "\\dots" => "โ€ฆ", "\\perthousand" => "โ€ฐ", "\\pertenthousand" => "โ€ฑ", "\\prime" => "โ€ฒ", "\\backprime" => "โ€ต", "\\guilsinglleft" => "โ€น", "\\guilsinglright" => "โ€บ", "\\nolinebreak" => "\u2060", "\\pes" => "โ‚ง", "\\dddot" => "โƒ›", "\\ddddot" => "โƒœ", "\\hslash" => "โ„", "\\Im" => "โ„‘", "\\ell" => "โ„“", "\\numero" => "โ„–", "\\wp" => "โ„˜", "\\Re" => "โ„œ", "\\xrat" => "โ„ž", "\\trademark" => "โ„ข", "\\mho" => "โ„ง", "\\aleph" => "โ„ต", "\\beth" => "โ„ถ", "\\gimel" => "โ„ท", "\\daleth" => "โ„ธ", blackboard*"Pi" => "โ„ฟ", "\\bbsum" => "โ…€", "\\Game" => "โ…", "\\leftarrow" => "โ†", "\\uparrow" => "โ†‘", "\\rightarrow" => "โ†’", "\\downarrow" => "โ†“", "\\leftrightarrow" => "โ†”", "\\updownarrow" => "โ†•", "\\nwarrow" => "โ†–", "\\nearrow" => "โ†—", "\\searrow" => "โ†˜", "\\swarrow" => "โ†™", "\\nleftarrow" => "โ†š", "\\nrightarrow" => "โ†›", "\\twoheadleftarrow" => "โ†ž", "\\twoheadrightarrow" => "โ† ", "\\leftarrowtail" => "โ†ข", "\\rightarrowtail" => "โ†ฃ", "\\mapsto" => "โ†ฆ", "\\hookleftarrow" => "โ†ฉ", "\\hookrightarrow" => "โ†ช", "\\looparrowleft" => "โ†ซ", "\\looparrowright" => "โ†ฌ", "\\leftrightsquigarrow" => "โ†ญ", "\\nleftrightarrow" => "โ†ฎ", "\\Lsh" => "โ†ฐ", "\\Rsh" => "โ†ฑ", "\\curvearrowleft" => "โ†ถ", "\\curvearrowright" => "โ†ท", "\\circlearrowleft" => "โ†บ", "\\circlearrowright" => "โ†ป", "\\leftharpoonup" => "โ†ผ", "\\leftharpoondown" => "โ†ฝ", "\\upharpoonright" => "โ†พ", "\\upharpoonleft" => "โ†ฟ", "\\rightharpoonup" => "โ‡€", "\\rightharpoondown" => "โ‡", "\\downharpoonright" => "โ‡‚", "\\downharpoonleft" => "โ‡ƒ", "\\rightleftarrows" => "โ‡„", "\\dblarrowupdown" => "โ‡…", "\\leftrightarrows" => "โ‡†", "\\leftleftarrows" => "โ‡‡", "\\upuparrows" => "โ‡ˆ", "\\rightrightarrows" => "โ‡‰", "\\downdownarrows" => "โ‡Š", "\\leftrightharpoons" => "โ‡‹", "\\rightleftharpoons" => "โ‡Œ", "\\nLeftarrow" => "โ‡", "\\nRightarrow" => "โ‡", "\\Leftarrow" => "โ‡", "\\Uparrow" => "โ‡‘", "\\Rightarrow" => "โ‡’", "\\Downarrow" => "โ‡“", "\\Leftrightarrow" => "โ‡”", "\\Updownarrow" => "โ‡•", "\\Lleftarrow" => "โ‡š", "\\Rrightarrow" => "โ‡›", "\\DownArrowUpArrow" => "โ‡ต", "\\leftarrowtriangle" => "โ‡ฝ", "\\rightarrowtriangle" => "โ‡พ", "\\forall" => "โˆ€", "\\complement" => "โˆ", "\\partial" => "โˆ‚", "\\exists" => "โˆƒ", "\\nexists" => "โˆ„", "\\varnothing" => "โˆ…", "\\emptyset" => "โˆ…", "\\nabla" => "โˆ‡", "\\in" => "โˆˆ", "\\notin" => "โˆ‰", "\\ni" => "โˆ‹", "\\prod" => "โˆ", "\\coprod" => "โˆ", "\\sum" => "โˆ‘", "\\minus" => "โˆ’", "\\mp" => "โˆ“", "\\dotplus" => "โˆ”", "\\setminus" => "โˆ–", "\\ast" => "โˆ—", "\\circ" => "โˆ˜", blackboard*"semi" => "โจŸ", "\\surd" => "โˆš", "\\propto" => "โˆ", "\\infty" => "โˆž", "\\rightangle" => "โˆŸ", "\\angle" => "โˆ ", "\\measuredangle" => "โˆก", "\\sphericalangle" => "โˆข", "\\mid" => "โˆฃ", "\\nmid" => "โˆค", "\\parallel" => "โˆฅ", "\\nparallel" => "โˆฆ", "\\wedge" => "โˆง", "\\vee" => "โˆจ", "\\cap" => "โˆฉ", "\\cup" => "โˆช", "\\int" => "โˆซ", "\\iint" => "โˆฌ", "\\iiint" => "โˆญ", "\\oint" => "โˆฎ", "\\oiint" => "โˆฏ", "\\oiiint" => "โˆฐ", "\\clwintegral" => "โˆฑ", "\\therefore" => "โˆด", "\\because" => "โˆต", "\\Colon" => "โˆท", "\\dotminus" => "โˆธ", "\\kernelcontraction" => "โˆป", "\\sim" => "โˆผ", "\\backsim" => "โˆฝ", "\\lazysinv" => "โˆพ", "\\wr" => "โ‰€", "\\nsim" => "โ‰", "\\eqsim" => "โ‰‚", "\\neqsim" => "โ‰‚ฬธ", "\\simeq" => "โ‰ƒ", "\\nsime" => "โ‰„", "\\cong" => "โ‰…", "\\approxnotequal" => "โ‰†", "\\ncong" => "โ‰‡", "\\approx" => "โ‰ˆ", "\\napprox" => "โ‰‰", "\\approxeq" => "โ‰Š", "\\tildetrpl" => "โ‰‹", "\\allequal" => "โ‰Œ", "\\asymp" => "โ‰", "\\Bumpeq" => "โ‰Ž", "\\nBumpeq" => "โ‰Žฬธ", "\\bumpeq" => "โ‰", "\\nbumpeq" => "โ‰ฬธ", "\\doteq" => "โ‰", "\\Doteq" => "โ‰‘", "\\fallingdotseq" => "โ‰’", "\\risingdotseq" => "โ‰“", "\\coloneq" => "โ‰”", "\\eqcolon" => "โ‰•", "\\eqcirc" => "โ‰–", "\\circeq" => "โ‰—", "\\wedgeq" => "โ‰™", "\\starequal" => "โ‰›", "\\triangleq" => "โ‰œ", "\\questeq" => "โ‰Ÿ", "\\ne" => "โ‰ ", "\\neq" => "โ‰ ", "\\equiv" => "โ‰ก", "\\nequiv" => "โ‰ข", "\\le" => "โ‰ค", "\\leq" => "โ‰ค", "\\ge" => "โ‰ฅ", "\\geq" => "โ‰ฅ", "\\leqq" => "โ‰ฆ", "\\geqq" => "โ‰ง", "\\lneqq" => "โ‰จ", "\\lvertneqq" => "โ‰จ๏ธ€", "\\gneqq" => "โ‰ฉ", "\\gvertneqq" => "โ‰ฉ๏ธ€", "\\ll" => "โ‰ช", "\\NotLessLess" => "โ‰ชฬธ", "\\gg" => "โ‰ซ", "\\NotGreaterGreater" => "โ‰ซฬธ", "\\between" => "โ‰ฌ", "\\nless" => "โ‰ฎ", "\\ngtr" => "โ‰ฏ", "\\nleq" => "โ‰ฐ", "\\ngeq" => "โ‰ฑ", "\\lesssim" => "โ‰ฒ", "\\gtrsim" => "โ‰ณ", "\\lessgtr" => "โ‰ถ", "\\gtrless" => "โ‰ท", "\\notlessgreater" => "โ‰ธ", "\\notgreaterless" => "โ‰น", "\\prec" => "โ‰บ", "\\succ" => "โ‰ป", "\\preccurlyeq" => "โ‰ผ", "\\succcurlyeq" => "โ‰ฝ", "\\precsim" => "โ‰พ", "\\nprecsim" => "โ‰พฬธ", "\\succsim" => "โ‰ฟ", "\\nsuccsim" => "โ‰ฟฬธ", "\\nprec" => "โŠ€", "\\nsucc" => "โЁ", "\\subset" => "โŠ‚", "\\supset" => "โŠƒ", "\\nsubset" => "โŠ„", "\\nsupset" => "โŠ…", "\\subseteq" => "โІ", "\\supseteq" => "โЇ", "\\nsubseteq" => "โŠˆ", "\\nsupseteq" => "โЉ", "\\subsetneq" => "โŠŠ", "\\varsubsetneqq" => "โŠŠ๏ธ€", "\\supsetneq" => "โŠ‹", "\\varsupsetneq" => "โŠ‹๏ธ€", "\\cupdot" => "โŠ", "\\uplus" => "โŠŽ", "\\sqsubset" => "โŠ", "\\NotSquareSubset" => "โŠฬธ", "\\sqsupset" => "โА", "\\NotSquareSuperset" => "โАฬธ", "\\sqsubseteq" => "โŠ‘", "\\sqsupseteq" => "โŠ’", "\\sqcap" => "โŠ“", "\\sqcup" => "โŠ”", "\\oplus" => "โŠ•", "\\ominus" => "โŠ–", "\\otimes" => "โŠ—", "\\oslash" => "โŠ˜", "\\odot" => "โŠ™", "\\circledcirc" => "โŠš", "\\circledast" => "โŠ›", "\\circleddash" => "โŠ", "\\boxplus" => "โŠž", "\\boxminus" => "โŠŸ", "\\boxtimes" => "โŠ ", "\\boxdot" => "โŠก", "\\vdash" => "โŠข", "\\dashv" => "โŠฃ", "\\top" => "โŠค", "\\bot" => "โŠฅ", "\\Top" => "โซช", "\\Bot" => "โซซ", "\\indep" => "โซซ", "\\models" => "โŠง", "\\vDash" => "โŠจ", "\\downvDash" => "โซช", "\\upvDash" => "โซซ", "\\Vdash" => "โŠฉ", "\\Vvdash" => "โŠช", "\\VDash" => "โŠซ", "\\nvdash" => "โŠฌ", "\\nvDash" => "โŠญ", "\\nVdash" => "โŠฎ", "\\nVDash" => "โŠฏ", "\\vartriangleleft" => "โŠฒ", "\\vartriangleright" => "โŠณ", "\\trianglelefteq" => "โŠด", "\\trianglerighteq" => "โŠต", "\\original" => "โŠถ", "\\image" => "โŠท", "\\multimap" => "โŠธ", "\\hermitconjmatrix" => "โŠน", "\\intercal" => "โŠบ", "\\veebar" => "โŠป", "\\rightanglearc" => "โŠพ", "\\bigwedge" => "โ‹€", "\\bigvee" => "โ‹", "\\bigcap" => "โ‹‚", "\\bigcup" => "โ‹ƒ", "\\diamond" => "โ‹„", "\\cdot" => "โ‹…", "\\star" => "โ‹†", "\\divideontimes" => "โ‹‡", "\\bowtie" => "โ‹ˆ", "\\ltimes" => "โ‹‰", "\\rtimes" => "โ‹Š", "\\leftthreetimes" => "โ‹‹", "\\rightthreetimes" => "โ‹Œ", "\\backsimeq" => "โ‹", "\\curlyvee" => "โ‹Ž", "\\curlywedge" => "โ‹", "\\Subset" => "โ‹", "\\Supset" => "โ‹‘", "\\Cap" => "โ‹’", "\\Cup" => "โ‹“", "\\pitchfork" => "โ‹”", "\\lessdot" => "โ‹–", "\\gtrdot" => "โ‹—", "\\verymuchless" => "โ‹˜", "\\ggg" => "โ‹™", "\\lesseqgtr" => "โ‹š", "\\gtreqless" => "โ‹›", "\\curlyeqprec" => "โ‹ž", "\\curlyeqsucc" => "โ‹Ÿ", "\\lnsim" => "โ‹ฆ", "\\gnsim" => "โ‹ง", "\\precnsim" => "โ‹จ", "\\succnsim" => "โ‹ฉ", "\\ntriangleleft" => "โ‹ช", "\\ntriangleright" => "โ‹ซ", "\\ntrianglelefteq" => "โ‹ฌ", "\\ntrianglerighteq" => "โ‹ญ", "\\vdots" => "โ‹ฎ", "\\cdots" => "โ‹ฏ", "\\adots" => "โ‹ฐ", "\\ddots" => "โ‹ฑ", "\\lceil" => "โŒˆ", "\\rceil" => "โŒ‰", "\\lfloor" => "โŒŠ", "\\rfloor" => "โŒ‹", "\\recorder" => "โŒ•", "\\ulcorner" => "โŒœ", "\\urcorner" => "โŒ", "\\llcorner" => "โŒž", "\\lrcorner" => "โŒŸ", "\\frown" => "โŒข", "\\smile" => "โŒฃ", "\\langle" => "โŸจ", "\\rangle" => "โŸฉ", "\\obar" => "โŒฝ", "\\dlcorn" => "โŽฃ", "\\lmoustache" => "โŽฐ", "\\rmoustache" => "โŽฑ", "\\visiblespace" => "โฃ", "\\circledS" => "โ“ˆ", "\\dshfnc" => "โ”†", "\\sqfnw" => "โ”™", "\\diagup" => "โ•ฑ", "\\diagdown" => "โ•ฒ", "\\blacksquare" => "โ– ", "\\square" => "โ–ก", "\\vrecto" => "โ–ฏ", "\\bigtriangleup" => "โ–ณ", "\\blacktriangle" => "โ–ด", "\\vartriangle" => "โ–ต", "\\bigtriangledown" => "โ–ฝ", "\\blacktriangledown" => "โ–พ", "\\triangledown" => "โ–ฟ", "\\lozenge" => "โ—Š", "\\bigcirc" => "โ—‹", "\\cirfl" => "โ—", "\\cirfr" => "โ—‘", "\\cirfb" => "โ—’", "\\rvbull" => "โ—˜", "\\sqfl" => "โ—ง", "\\sqfr" => "โ—จ", "\\sqfse" => "โ—ช", "\\bigstar" => "โ˜…", "\\mercury" => "โ˜ฟ", "\\venus" => "โ™€", "\\male" => "โ™‚", "\\jupiter" => "โ™ƒ", "\\saturn" => "โ™„", "\\uranus" => "โ™…", "\\neptune" => "โ™†", "\\pluto" => "โ™‡", "\\aries" => "โ™ˆ", "\\taurus" => "โ™‰", "\\gemini" => "โ™Š", "\\cancer" => "โ™‹", "\\leo" => "โ™Œ", "\\virgo" => "โ™", "\\libra" => "โ™Ž", "\\scorpio" => "โ™", "\\sagittarius" => "โ™", "\\capricornus" => "โ™‘", "\\aquarius" => "โ™’", "\\pisces" => "โ™“", "\\spadesuit" => "โ™ ", "\\heartsuit" => "โ™ก", "\\diamondsuit" => "โ™ข", "\\clubsuit" => "โ™ฃ", "\\quarternote" => "โ™ฉ", "\\eighthnote" => "โ™ช", "\\flat" => "โ™ญ", "\\natural" => "โ™ฎ", "\\sharp" => "โ™ฏ", "\\checkmark" => "โœ“", "\\maltese" => "โœ ", "\\longleftarrow" => "โŸต", "\\longrightarrow" => "โŸถ", "\\longleftrightarrow" => "โŸท", "\\Longleftarrow" => "โŸธ", "\\Longrightarrow" => "โŸน", "\\Longleftrightarrow" => "โŸบ", "\\longmapsto" => "โŸผ", "\\Mapsfrom" => "โค†", "\\Mapsto" => "โค‡", "\\Uuparrow" => "โคŠ", "\\Ddownarrow" => "โค‹", "\\bkarow" => "โค", "\\dbkarow" => "โค", "\\drbkarrow" => "โค", "\\UpArrowBar" => "โค’", "\\DownArrowBar" => "โค“", "\\twoheadrightarrowtail" => "โค–", "\\hksearow" => "โคฅ", "\\hkswarow" => "โคฆ", "\\tona" => "โคง", "\\toea" => "โคจ", "\\tosa" => "โคฉ", "\\towa" => "โคช", "\\rdiagovfdiag" => "โคซ", "\\fdiagovrdiag" => "โคฌ", "\\seovnearrow" => "โคญ", "\\neovsearrow" => "โคฎ", "\\fdiagovnearrow" => "โคฏ", "\\rdiagovsearrow" => "โคฐ", "\\neovnwarrow" => "โคฑ", "\\nwovnearrow" => "โคฒ", "\\Rlarr" => "โฅ‚", "\\rLarr" => "โฅ„", "\\rarrx" => "โฅ‡", "\\LeftRightVector" => "โฅŽ", "\\RightUpDownVector" => "โฅ", "\\DownLeftRightVector" => "โฅ", "\\LeftUpDownVector" => "โฅ‘", "\\LeftVectorBar" => "โฅ’", "\\RightVectorBar" => "โฅ“", "\\RightUpVectorBar" => "โฅ”", "\\RightDownVectorBar" => "โฅ•", "\\DownLeftVectorBar" => "โฅ–", "\\DownRightVectorBar" => "โฅ—", "\\LeftUpVectorBar" => "โฅ˜", "\\LeftDownVectorBar" => "โฅ™", "\\LeftTeeVector" => "โฅš", "\\RightTeeVector" => "โฅ›", "\\RightUpTeeVector" => "โฅœ", "\\RightDownTeeVector" => "โฅ", "\\DownLeftTeeVector" => "โฅž", "\\DownRightTeeVector" => "โฅŸ", "\\LeftUpTeeVector" => "โฅ ", "\\LeftDownTeeVector" => "โฅก", "\\UpEquilibrium" => "โฅฎ", "\\ReverseUpEquilibrium" => "โฅฏ", "\\RoundImplies" => "โฅฐ", "\\Vvert" => "โฆ€", "\\Elroang" => "โฆ†", "\\ddfnc" => "โฆ™", "\\Angle" => "โฆœ", "\\lpargt" => "โฆ ", "\\obslash" => "โฆธ", "\\boxdiag" => "โง„", "\\boxbslash" => "โง…", "\\boxast" => "โง†", "\\boxcircle" => "โง‡", "\\Lap" => "โงŠ", "\\defas" => "โง‹", "\\LeftTriangleBar" => "โง", "\\NotLeftTriangleBar" => "โงฬธ", "\\RightTriangleBar" => "โง", "\\NotRightTriangleBar" => "โงฬธ", "\\dualmap" => "โงŸ", "\\shuffle" => "โงข", "\\blacklozenge" => "โงซ", "\\RuleDelayed" => "โงด", "\\bigodot" => "โจ€", "\\bigoplus" => "โจ", "\\bigotimes" => "โจ‚", "\\bigcupdot" => "โจƒ", "\\biguplus" => "โจ„", "\\bigsqcap" => "โจ…", "\\bigsqcup" => "โจ†", "\\conjquant" => "โจ‡", "\\disjquant" => "โจˆ", "\\bigtimes" => "โจ‰", "\\iiiint" => "โจŒ", "\\intbar" => "โจ", "\\intBar" => "โจŽ", "\\clockoint" => "โจ", "\\sqrint" => "โจ–", "\\intx" => "โจ˜", "\\intcap" => "โจ™", "\\intcup" => "โจš", "\\upint" => "โจ›", "\\lowint" => "โจœ", "\\plusdot" => "โจฅ", "\\minusdot" => "โจช", "\\Times" => "โจฏ", "\\btimes" => "โจฒ", "\\intprod" => "โจผ", "\\intprodr" => "โจฝ", "\\amalg" => "โจฟ", "\\And" => "โฉ“", "\\Or" => "โฉ”", "\\ElOr" => "โฉ–", "\\perspcorrespond" => "โฉž", "\\minhat" => "โฉŸ", "\\Equal" => "โฉต", "\\ddotseq" => "โฉท", "\\leqslant" => "โฉฝ", "\\nleqslant" => "โฉฝฬธ", "\\geqslant" => "โฉพ", "\\ngeqslant" => "โฉพฬธ", "\\lessapprox" => "โช…", "\\gtrapprox" => "โช†", "\\lneq" => "โช‡", "\\gneq" => "โชˆ", "\\lnapprox" => "โช‰", "\\gnapprox" => "โชŠ", "\\lesseqqgtr" => "โช‹", "\\gtreqqless" => "โชŒ", "\\eqslantless" => "โช•", "\\eqslantgtr" => "โช–", "\\NestedLessLess" => "โชก", "\\NotNestedLessLess" => "โชกฬธ", "\\NestedGreaterGreater" => "โชข", "\\NotNestedGreaterGreater" => "โชขฬธ", "\\partialmeetcontraction" => "โชฃ", "\\bumpeqq" => "โชฎ", "\\preceq" => "โชฏ", "\\npreceq" => "โชฏฬธ", "\\succeq" => "โชฐ", "\\nsucceq" => "โชฐฬธ", "\\precneqq" => "โชต", "\\succneqq" => "โชถ", "\\precapprox" => "โชท", "\\succapprox" => "โชธ", "\\precnapprox" => "โชน", "\\succnapprox" => "โชบ", "\\subseteqq" => "โซ…", "\\nsubseteqq" => "โซ…ฬธ", "\\supseteqq" => "โซ†", "\\nsupseteqq" => "โซ†ฬธ", "\\subsetneqq" => "โซ‹", "\\supsetneqq" => "โซŒ", "\\mlcp" => "โซ›", "\\forks" => "โซœ", "\\forksnot" => "โซ", "\\dashV" => "โซฃ", "\\Dashv" => "โซค", "\\interleave" => "โซด", "\\tdcol" => "โซถ", "\\openbracketleft" => "โŸฆ", "\\llbracket" => "โŸฆ", "\\openbracketright" => "โŸง", "\\rrbracket" => "โŸง", "\\overbrace" => "โž", "\\underbrace" => "โŸ", # 1607 symbols generated from unicode-math-table.tex: "\\Zbar" => "ฦต", # impedance (latin capital letter z with stroke) "\\overbar" => "ฬ…", # overbar embellishment "\\ovhook" => "ฬ‰", # combining hook above "\\candra" => "ฬ", # candrabindu (non-spacing) "\\oturnedcomma" => "ฬ’", # combining turned comma above "\\ocommatopright" => "ฬ•", # combining comma above right "\\droang" => "ฬš", # left angle above (non-spacing) "\\wideutilde" => "ฬฐ", # under tilde accent (multiple characters and non-spacing) "\\not" => "ฬธ", # combining long solidus overlay "\\upMu" => "ฮœ", # capital mu, greek "\\upNu" => "ฮ", # capital nu, greek "\\upOmicron" => "ฮŸ", # capital omicron, greek "\\upepsilon" => "ฮต", # rounded small epsilon, greek "\\upomicron" => "ฮฟ", # small omicron, greek "\\upvarbeta" => "ฯ", # rounded small beta, greek "\\upoldKoppa" => "ฯ˜", # greek letter archaic koppa "\\upoldkoppa" => "ฯ™", # greek small letter archaic koppa "\\upstigma" => "ฯ›", # greek small letter stigma "\\upkoppa" => "ฯŸ", # greek small letter koppa "\\upsampi" => "ฯก", # greek small letter sampi "\\tieconcat" => "โ€", # character tie, z notation sequence concatenation "\\leftharpoonaccent" => "โƒ", # combining left harpoon above "\\rightharpoonaccent" => "โƒ‘", # combining right harpoon above "\\vertoverlay" => "โƒ’", # combining long vertical line overlay "\\overleftarrow" => "โƒ–", # combining left arrow above "\\vec" => "โƒ—", # combining right arrow above "\\enclosecircle" => "โƒ", # combining enclosing circle "\\enclosesquare" => "โƒž", # combining enclosing square "\\enclosediamond" => "โƒŸ", # combining enclosing diamond "\\overleftrightarrow" => "โƒก", # combining left right arrow above "\\enclosetriangle" => "โƒค", # combining enclosing upward pointing triangle "\\annuity" => "โƒง", # combining annuity symbol "\\threeunderdot" => "โƒจ", # combining triple underdot "\\widebridgeabove" => "โƒฉ", # combining wide bridge above "\\underrightharpoondown" => "\u20ec", # combining rightwards harpoon with barb downwards "\\underleftharpoondown" => "\u20ed", # combining leftwards harpoon with barb downwards "\\underleftarrow" => "\u20ee", # combining left arrow below "\\underrightarrow" => "\u20ef", # combining right arrow below "\\asteraccent" => "\u20f0", # combining asterisk above blackboard*"C" => "โ„‚", # /bbb c, open face c "\\eulermascheroni" => "โ„‡", # euler-mascheroni constant U+2107 script*"g" => "โ„Š", # /scr g, script letter g script*"H" => "โ„‹", # hamiltonian (script capital h) frak*"H" => "โ„Œ", # /frak h, upper case h blackboard*"H" => "โ„", # /bbb h, open face h "\\planck" => "โ„Ž", # planck constant script*"I" => "โ„", # /scr i, script letter i script*"L" => "โ„’", # lagrangian (script capital l) blackboard*"N" => "โ„•", # /bbb n, open face n blackboard*"P" => "โ„™", # /bbb p, open face p blackboard*"Q" => "โ„š", # /bbb q, open face q script*"R" => "โ„›", # /scr r, script letter r blackboard*"R" => "โ„", # /bbb r, open face r blackboard*"Z" => "โ„ค", # /bbb z, open face z frak*"Z" => "โ„จ", # /frak z, upper case z "\\turnediota" => "โ„ฉ", # turned iota "\\Angstrom" => "โ„ซ", # angstrom capital a, ring script*"B" => "โ„ฌ", # bernoulli function (script capital b) frak*"C" => "โ„ญ", # black-letter capital c script*"e" => "โ„ฏ", # /scr e, script letter e script*"E" => "โ„ฐ", # /scr e, script letter e script*"F" => "โ„ฑ", # /scr f, script letter f "\\Finv" => "โ„ฒ", # turned capital f script*"M" => "โ„ณ", # physics m-matrix (script capital m) script*"o" => "โ„ด", # order of (script small o) blackboard*"pi" => "\u213c", # double-struck small pi blackboard*"gamma" => "โ„ฝ", # double-struck small gamma blackboard*"Gamma" => "โ„พ", # double-struck capital gamma "\\sansLturned" => "โ…‚", # turned sans-serif capital l "\\sansLmirrored" => "โ…ƒ", # reversed sans-serif capital l "\\Yup" => "โ…„", # turned sans-serif capital y italicblackboard*"D" => "โ……", # double-struck italic capital d italicblackboard*"d" => "โ…†", # double-struck italic small d italicblackboard*"e" => "โ…‡", # double-struck italic small e italicblackboard*"i" => "โ…ˆ", # double-struck italic small i italicblackboard*"j" => "โ…‰", # double-struck italic small j "\\PropertyLine" => "โ…Š", # property line "\\upand" => "โ…‹", # turned ampersand "\\twoheaduparrow" => "โ†Ÿ", # up two-headed arrow "\\twoheaddownarrow" => "โ†ก", # down two-headed arrow "\\mapsfrom" => "โ†ค", # maps to, leftward "\\mapsup" => "โ†ฅ", # maps to, upward "\\mapsdown" => "โ†ง", # maps to, downward "\\updownarrowbar" => "โ†จ", # up down arrow with base (perpendicular) "\\downzigzagarrow" => "โ†ฏ", # downwards zigzag arrow "\\Ldsh" => "โ†ฒ", # left down angled arrow "\\Rdsh" => "โ†ณ", # right down angled arrow "\\linefeed" => "โ†ด", # rightwards arrow with corner downwards "\\carriagereturn" => "โ†ต", # downwards arrow with corner leftward = carriage return "\\barovernorthwestarrow" => "โ†ธ", # north west arrow to long bar "\\barleftarrowrightarrowbar" => "โ†น", # leftwards arrow to bar over rightwards arrow to bar "\\nLeftrightarrow" => "โ‡Ž", # not left and right double arrows "\\Nwarrow" => "โ‡–", # nw pointing double arrow "\\Nearrow" => "โ‡—", # ne pointing double arrow "\\Searrow" => "โ‡˜", # se pointing double arrow "\\Swarrow" => "โ‡™", # sw pointing double arrow "\\leftsquigarrow" => "โ‡œ", # leftwards squiggle arrow "\\rightsquigarrow" => "โ‡", # rightwards squiggle arrow "\\nHuparrow" => "โ‡ž", # upwards arrow with double stroke "\\nHdownarrow" => "โ‡Ÿ", # downwards arrow with double stroke "\\leftdasharrow" => "โ‡ ", # leftwards dashed arrow "\\updasharrow" => "โ‡ก", # upwards dashed arrow "\\rightdasharrow" => "โ‡ข", # rightwards dashed arrow "\\downdasharrow" => "โ‡ฃ", # downwards dashed arrow "\\barleftarrow" => "โ‡ค", # leftwards arrow to bar "\\rightarrowbar" => "โ‡ฅ", # rightwards arrow to bar "\\leftwhitearrow" => "โ‡ฆ", # leftwards white arrow "\\upwhitearrow" => "โ‡ง", # upwards white arrow "\\rightwhitearrow" => "โ‡จ", # rightwards white arrow "\\downwhitearrow" => "โ‡ฉ", # downwards white arrow "\\whitearrowupfrombar" => "โ‡ช", # upwards white arrow from bar "\\circleonrightarrow" => "โ‡ด", # right arrow with small circle "\\rightthreearrows" => "โ‡ถ", # three rightwards arrows "\\nvleftarrow" => "โ‡ท", # leftwards arrow with vertical stroke "\\nvrightarrow" => "โ‡ธ", # rightwards arrow with vertical stroke "\\nvleftrightarrow" => "โ‡น", # left right arrow with vertical stroke "\\nVleftarrow" => "โ‡บ", # leftwards arrow with double vertical stroke "\\nVrightarrow" => "โ‡ป", # rightwards arrow with double vertical stroke "\\nVleftrightarrow" => "โ‡ผ", # left right arrow with double vertical stroke "\\leftrightarrowtriangle" => "โ‡ฟ", # left right open-headed arrow "\\increment" => "โˆ†", # laplacian (delta; nabla\string^2) "\\smallin" => "โˆŠ", # set membership (small set membership) "\\nni" => "โˆŒ", # negated contains, variant "\\smallni" => "โˆ", # /ni /owns r: contains (small contains as member) "\\QED" => "โˆŽ", # end of proof "\\vysmblkcircle" => "โˆ™", # bullet operator "\\fourthroot" => "โˆœ", # fourth root "\\varointclockwise" => "โˆฒ", # contour integral, clockwise "\\ointctrclockwise" => "โˆณ", # contour integral, anticlockwise "\\dotsminusdots" => "โˆบ", # minus with four dots, geometric properties "\\sinewave" => "โˆฟ", # sine wave "\\arceq" => "โ‰˜", # arc, equals; corresponds to "\\veeeq" => "โ‰š", # logical or, equals "\\eqdef" => "โ‰", # equals by definition "\\measeq" => "โ‰ž", # measured by (m over equals) "\\Equiv" => "โ‰ฃ", # strict equivalence (4 lines) "\\nasymp" => "โ‰ญ", # not asymptotically equal to "\\nlesssim" => "โ‰ด", # not less, similar "\\ngtrsim" => "โ‰ต", # not greater, similar "\\circledequal" => "โŠœ", # equal in circle "\\prurel" => "โŠฐ", # element precedes under relation "\\scurel" => "โŠฑ", # succeeds under relation "\\barwedge" => "โŠผ", # bar, wedge (large wedge) "\\barvee" => "โŠฝ", # bar, vee (large vee) "\\varlrtriangle" => "โŠฟ", # right triangle "\\equalparallel" => "โ‹•", # parallel, equal; equal or parallel "\\eqless" => "โ‹œ", # equal-or-less "\\eqgtr" => "โ‹", # equal-or-greater "\\npreccurlyeq" => "โ‹ ", # not precedes, curly equals "\\nsucccurlyeq" => "โ‹ก", # not succeeds, curly equals "\\nsqsubseteq" => "โ‹ข", # not, square subset, equals "\\nsqsupseteq" => "โ‹ฃ", # not, square superset, equals "\\sqsubsetneq" => "โ‹ค", # square subset, not equals "\\sqsupsetneq" => "โ‹ฅ", # square superset, not equals "\\disin" => "โ‹ฒ", # element of with long horizontal stroke "\\varisins" => "โ‹ณ", # element of with vertical bar at end of horizontal stroke "\\isins" => "โ‹ด", # small element of with vertical bar at end of horizontal stroke "\\isindot" => "โ‹ต", # element of with dot above "\\varisinobar" => "โ‹ถ", # element of with overbar "\\isinobar" => "โ‹ท", # small element of with overbar "\\isinvb" => "โ‹ธ", # element of with underbar "\\isinE" => "โ‹น", # element of with two horizontal strokes "\\nisd" => "โ‹บ", # contains with long horizontal stroke "\\varnis" => "โ‹ป", # contains with vertical bar at end of horizontal stroke "\\nis" => "โ‹ผ", # small contains with vertical bar at end of horizontal stroke "\\varniobar" => "โ‹ฝ", # contains with overbar "\\niobar" => "โ‹พ", # small contains with overbar "\\bagmember" => "โ‹ฟ", # z notation bag membership "\\diameter" => "โŒ€", # diameter sign "\\house" => "โŒ‚", # house "\\vardoublebarwedge" => "โŒ†", # /doublebarwedge b: logical and, double bar above [perspective (double bar over small wedge)] "\\invnot" => "โŒ", # reverse not "\\sqlozenge" => "โŒ‘", # square lozenge "\\profline" => "โŒ’", # profile of a line "\\profsurf" => "โŒ“", # profile of a surface "\\viewdata" => "โŒ—", # viewdata square "\\turnednot" => "โŒ™", # turned not sign "\\varhexagonlrbonds" => "โŒฌ", # six carbon ring, corner down, double bonds lower right etc "\\conictaper" => "โŒฒ", # conical taper "\\topbot" => "โŒถ", # top and bottom "\\notslash" => "โŒฟ", # solidus, bar through (apl functional symbol slash bar) "\\notbackslash" => "โ€", # apl functional symbol backslash bar "\\boxupcaret" => "โ“", # boxed up caret "\\boxquestion" => "โฐ", # boxed question mark "\\hexagon" => "โŽ”", # horizontal benzene ring [hexagon flat open] "\\overbracket" => "โŽด", # top square bracket "\\underbracket" => "โŽต", # bottom square bracket "\\bbrktbrk" => "โŽถ", # bottom square bracket over top square bracket "\\sqrtbottom" => "โŽท", # radical symbol bottom "\\lvboxline" => "โŽธ", # left vertical box line "\\rvboxline" => "โŽน", # right vertical box line "\\varcarriagereturn" => "โŽ", # return symbol "\\trapezium" => "\u23e2", # white trapezium "\\benzenr" => "\u23e3", # benzene ring with circle "\\strns" => "\u23e4", # straightness "\\fltns" => "\u23e5", # flatness "\\accurrent" => "\u23e6", # ac current "\\elinters" => "\u23e7", # electrical intersection "\\blanksymbol" => "โข", # blank symbol "\\blockuphalf" => "โ–€", # upper half block "\\blocklowhalf" => "โ–„", # lower half block "\\blockfull" => "โ–ˆ", # full block "\\blocklefthalf" => "โ–Œ", # left half block "\\blockrighthalf" => "โ–", # right half block "\\blockqtrshaded" => "โ–‘", # 25\% shaded block "\\blockhalfshaded" => "โ–’", # 50\% shaded block "\\blockthreeqtrshaded" => "โ–“", # 75\% shaded block "\\squoval" => "โ–ข", # white square with rounded corners "\\blackinwhitesquare" => "โ–ฃ", # white square containing black small square "\\squarehfill" => "โ–ค", # square, horizontal rule filled "\\squarevfill" => "โ–ฅ", # square, vertical rule filled "\\squarehvfill" => "โ–ฆ", # square with orthogonal crosshatch fill "\\squarenwsefill" => "โ–ง", # square, nw-to-se rule filled "\\squareneswfill" => "โ–จ", # square, ne-to-sw rule filled "\\squarecrossfill" => "โ–ฉ", # square with diagonal crosshatch fill "\\smblksquare" => "โ–ช", # /blacksquare - sq bullet, filled "\\smwhtsquare" => "โ–ซ", # white small square "\\hrectangleblack" => "โ–ฌ", # black rectangle "\\hrectangle" => "โ–ญ", # horizontal rectangle, open "\\vrectangleblack" => "โ–ฎ", # black vertical rectangle "\\parallelogramblack" => "โ–ฐ", # black parallelogram "\\parallelogram" => "โ–ฑ", # parallelogram, open "\\bigblacktriangleup" => "โ–ฒ", # 0x25b2 6 6d black up-pointing triangle "\\blacktriangleright" => "โ–ถ", # (large) right triangle, filled "\\blackpointerright" => "โ–บ", # black right-pointing pointer "\\whitepointerright" => "โ–ป", # white right-pointing pointer "\\bigblacktriangledown" => "โ–ผ", # big down triangle, filled "\\blacktriangleleft" => "โ—€", # (large) left triangle, filled "\\blackpointerleft" => "โ—„", # black left-pointing pointer "\\whitepointerleft" => "โ—…", # white left-pointing pointer "\\mdlgblkdiamond" => "โ—†", # black diamond "\\mdlgwhtdiamond" => "โ—‡", # white diamond; diamond, open "\\blackinwhitediamond" => "โ—ˆ", # white diamond containing black small diamond "\\fisheye" => "โ—‰", # fisheye "\\dottedcircle" => "โ—Œ", # dotted circle "\\circlevertfill" => "โ—", # circle with vertical fill "\\bullseye" => "โ—Ž", # bullseye "\\mdlgblkcircle" => "โ—", # circle, filled "\\circletophalfblack" => "โ—“", # circle, filled top half "\\circleurquadblack" => "โ—”", # circle with upper right quadrant black "\\blackcircleulquadwhite" => "โ—•", # circle with all but upper left quadrant black "\\blacklefthalfcircle" => "โ—–", # left half black circle "\\blackrighthalfcircle" => "โ——", # right half black circle "\\inversewhitecircle" => "โ—™", # inverse white circle "\\invwhiteupperhalfcircle" => "โ—š", # upper half inverse white circle "\\invwhitelowerhalfcircle" => "โ—›", # lower half inverse white circle "\\ularc" => "โ—œ", # upper left quadrant circular arc "\\urarc" => "โ—", # upper right quadrant circular arc "\\lrarc" => "โ—ž", # lower right quadrant circular arc "\\llarc" => "โ—Ÿ", # lower left quadrant circular arc "\\topsemicircle" => "โ— ", # upper half circle "\\botsemicircle" => "โ—ก", # lower half circle "\\lrblacktriangle" => "โ—ข", # lower right triangle, filled "\\llblacktriangle" => "โ—ฃ", # lower left triangle, filled "\\ulblacktriangle" => "โ—ค", # upper left triangle, filled "\\urblacktriangle" => "โ—ฅ", # upper right triangle, filled "\\smwhtcircle" => "โ—ฆ", # white bullet "\\squareulblack" => "โ—ฉ", # square, filled top left corner "\\boxbar" => "โ—ซ", # vertical bar in box "\\trianglecdot" => "โ—ฌ", # triangle with centered dot "\\triangleleftblack" => "โ—ญ", # up-pointing triangle with left half black "\\trianglerightblack" => "โ—ฎ", # up-pointing triangle with right half black "\\lgwhtcircle" => "โ—ฏ", # large circle "\\squareulquad" => "โ—ฐ", # white square with upper left quadrant "\\squarellquad" => "โ—ฑ", # white square with lower left quadrant "\\squarelrquad" => "โ—ฒ", # white square with lower right quadrant "\\squareurquad" => "โ—ณ", # white square with upper right quadrant "\\circleulquad" => "โ—ด", # white circle with upper left quadrant "\\circlellquad" => "โ—ต", # white circle with lower left quadrant "\\circlelrquad" => "โ—ถ", # white circle with lower right quadrant "\\circleurquad" => "โ—ท", # white circle with upper right quadrant "\\ultriangle" => "โ—ธ", # upper left triangle "\\urtriangle" => "โ—น", # upper right triangle "\\lltriangle" => "โ—บ", # lower left triangle "\\mdwhtsquare" => "โ—ป", # white medium square "\\mdblksquare" => "โ—ผ", # black medium square "\\mdsmwhtsquare" => "โ—ฝ", # white medium small square "\\mdsmblksquare" => "โ—พ", # black medium small square "\\lrtriangle" => "โ—ฟ", # lower right triangle "\\bigwhitestar" => "โ˜†", # star, open "\\astrosun" => "โ˜‰", # sun "\\danger" => "โ˜ก", # dangerous bend (caution sign) "\\blacksmiley" => "โ˜ป", # black smiling face "\\sun" => "โ˜ผ", # white sun with rays "\\rightmoon" => "โ˜ฝ", # first quarter moon "\\varspadesuit" => "โ™ค", # spade, white (card suit) "\\varheartsuit" => "โ™ฅ", # filled heart (card suit) "\\vardiamondsuit" => "โ™ฆ", # filled diamond (card suit) "\\varclubsuit" => "โ™ง", # club, white (card suit) "\\twonotes" => "โ™ซ", # beamed eighth notes "\\acidfree" => "\u267e", # permanent paper sign "\\dicei" => "โš€", # die face-1 "\\diceii" => "โš", # die face-2 "\\diceiii" => "โš‚", # die face-3 "\\diceiv" => "โšƒ", # die face-4 "\\dicev" => "โš„", # die face-5 "\\dicevi" => "โš…", # die face-6 "\\circledrightdot" => "โš†", # white circle with dot right "\\circledtwodots" => "โš‡", # white circle with two dots "\\blackcircledrightdot" => "โšˆ", # black circle with white dot right "\\blackcircledtwodots" => "โš‰", # black circle with two white dots "\\hermaphrodite" => "\u26a5", # male and female sign "\\mdwhtcircle" => "\u26aa", # medium white circle "\\mdblkcircle" => "\u26ab", # medium black circle "\\mdsmwhtcircle" => "\u26ac", # medium small white circle "\\neuter" => "\u26b2", # neuter "\\circledstar" => "โœช", # circled white star "\\varstar" => "โœถ", # six pointed black star "\\dingasterisk" => "โœฝ", # heavy teardrop-spoked asterisk "\\draftingarrow" => "โž›", # right arrow with bold head (drafting) "\\threedangle" => "\u27c0", # three dimensional angle "\\whiteinwhitetriangle" => "\u27c1", # white triangle containing small white triangle "\\perp" => "\u27c2", # perpendicular "\\bsolhsub" => "\u27c8", # reverse solidus preceding subset "\\suphsol" => "\u27c9", # superset preceding solidus "\\wedgedot" => "โŸ‘", # and with dot "\\veedot" => "โŸ‡", # or with dot "\\upin" => "โŸ’", # element of opening upwards "\\bigbot" => "โŸ˜", # large up tack "\\bigtop" => "โŸ™", # large down tack "\\UUparrow" => "โŸฐ", # upwards quadruple arrow "\\DDownarrow" => "โŸฑ", # downwards quadruple arrow "\\longmapsfrom" => "โŸป", # long leftwards arrow from bar "\\Longmapsfrom" => "โŸฝ", # long leftwards double arrow from bar "\\Longmapsto" => "โŸพ", # long rightwards double arrow from bar "\\longrightsquigarrow" => "โŸฟ", # long rightwards squiggle arrow "\\nvtwoheadrightarrow" => "โค€", # rightwards two-headed arrow with vertical stroke "\\nVtwoheadrightarrow" => "โค", # rightwards two-headed arrow with double vertical stroke "\\nvLeftarrow" => "โค‚", # leftwards double arrow with vertical stroke "\\nvRightarrow" => "โคƒ", # rightwards double arrow with vertical stroke "\\nvLeftrightarrow" => "โค„", # left right double arrow with vertical stroke "\\twoheadmapsto" => "โค…", # rightwards two-headed arrow from bar "\\downarrowbarred" => "โคˆ", # downwards arrow with horizontal stroke "\\uparrowbarred" => "โค‰", # upwards arrow with horizontal stroke "\\leftbkarrow" => "โคŒ", # leftwards double dash arrow "\\leftdbkarrow" => "โคŽ", # leftwards triple dash arrow "\\rightdotarrow" => "โค‘", # rightwards arrow with dotted stem "\\nvrightarrowtail" => "โค”", # rightwards arrow with tail with vertical stroke "\\nVrightarrowtail" => "โค•", # rightwards arrow with tail with double vertical stroke "\\nvtwoheadrightarrowtail" => "โค—", # rightwards two-headed arrow with tail with vertical stroke "\\nVtwoheadrightarrowtail" => "โค˜", # rightwards two-headed arrow with tail with double vertical stroke "\\diamondleftarrow" => "โค", # leftwards arrow to black diamond "\\rightarrowdiamond" => "โคž", # rightwards arrow to black diamond "\\diamondleftarrowbar" => "โคŸ", # leftwards arrow from bar to black diamond "\\barrightarrowdiamond" => "โค ", # rightwards arrow from bar to black diamond "\\rightarrowplus" => "โฅ…", # rightwards arrow with plus below "\\leftarrowplus" => "โฅ†", # leftwards arrow with plus below "\\leftrightarrowcircle" => "โฅˆ", # left right arrow through small circle "\\twoheaduparrowcircle" => "โฅ‰", # upwards two-headed arrow from small circle "\\leftrightharpoonupdown" => "โฅŠ", # left barb up right barb down harpoon "\\leftrightharpoondownup" => "โฅ‹", # left barb down right barb up harpoon "\\updownharpoonrightleft" => "โฅŒ", # up barb right down barb left harpoon "\\updownharpoonleftright" => "โฅ", # up barb left down barb right harpoon "\\leftharpoonsupdown" => "โฅข", # leftwards harpoon with barb up above leftwards harpoon with barb down "\\upharpoonsleftright" => "โฅฃ", # upwards harpoon with barb left beside upwards harpoon with barb right "\\rightharpoonsupdown" => "โฅค", # rightwards harpoon with barb up above rightwards harpoon with barb down "\\downharpoonsleftright" => "โฅฅ", # downwards harpoon with barb left beside downwards harpoon with barb right "\\leftrightharpoonsup" => "โฅฆ", # leftwards harpoon with barb up above rightwards harpoon with barb up "\\leftrightharpoonsdown" => "โฅง", # leftwards harpoon with barb down above rightwards harpoon with barb down "\\rightleftharpoonsup" => "โฅจ", # rightwards harpoon with barb up above leftwards harpoon with barb up "\\rightleftharpoonsdown" => "โฅฉ", # rightwards harpoon with barb down above leftwards harpoon with barb down "\\leftharpoonupdash" => "โฅช", # leftwards harpoon with barb up above long dash "\\dashleftharpoondown" => "โฅซ", # leftwards harpoon with barb down below long dash "\\rightharpoonupdash" => "โฅฌ", # rightwards harpoon with barb up above long dash "\\dashrightharpoondown" => "โฅญ", # rightwards harpoon with barb down below long dash "\\measuredangleleft" => "โฆ›", # measured angle opening left "\\rightanglemdot" => "โฆ", # measured right angle with dot "\\angles" => "โฆž", # angle with s inside "\\angdnr" => "โฆŸ", # acute angle "\\sphericalangleup" => "โฆก", # spherical angle opening up "\\turnangle" => "โฆข", # turned angle "\\revangle" => "โฆฃ", # reversed angle "\\angleubar" => "โฆค", # angle with underbar "\\revangleubar" => "โฆฅ", # reversed angle with underbar "\\wideangledown" => "โฆฆ", # oblique angle opening up "\\wideangleup" => "โฆง", # oblique angle opening down "\\measanglerutone" => "โฆจ", # measured angle with open arm ending in arrow pointing up and right "\\measanglelutonw" => "โฆฉ", # measured angle with open arm ending in arrow pointing up and left "\\measanglerdtose" => "โฆช", # measured angle with open arm ending in arrow pointing down and right "\\measangleldtosw" => "โฆซ", # measured angle with open arm ending in arrow pointing down and left "\\measangleurtone" => "โฆฌ", # measured angle with open arm ending in arrow pointing right and up "\\measangleultonw" => "โฆญ", # measured angle with open arm ending in arrow pointing left and up "\\measangledrtose" => "โฆฎ", # measured angle with open arm ending in arrow pointing right and down "\\measangledltosw" => "โฆฏ", # measured angle with open arm ending in arrow pointing left and down "\\revemptyset" => "โฆฐ", # reversed empty set "\\emptysetobar" => "โฆฑ", # empty set with overbar "\\emptysetocirc" => "โฆฒ", # empty set with small circle above "\\emptysetoarr" => "โฆณ", # empty set with right arrow above "\\emptysetoarrl" => "โฆด", # empty set with left arrow above "\\circledparallel" => "โฆท", # circled parallel "\\odotslashdot" => "โฆผ", # circled anticlockwise-rotated division sign "\\circledwhitebullet" => "โฆพ", # circled white bullet "\\circledbullet" => "โฆฟ", # circled bullet "\\olessthan" => "โง€", # circled less-than "\\ogreaterthan" => "โง", # circled greater-than "\\lrtriangleeq" => "โงก", # increases as "\\eparsl" => "โงฃ", # equals sign and slanted parallel "\\smeparsl" => "โงค", # equals sign and slanted parallel with tilde above "\\eqvparsl" => "โงฅ", # identical to and slanted parallel "\\dsol" => "โงถ", # solidus with overbar "\\rsolbar" => "โงท", # reverse solidus with horizontal stroke "\\doubleplus" => "โงบ", # double plus "\\tripleplus" => "โงป", # triple plus "\\modtwosum" => "โจŠ", # modulo two sum "\\sumint" => "โจ‹", # summation with integral "\\cirfnint" => "โจ", # circulation function "\\awint" => "โจ‘", # anticlockwise integration "\\rppolint" => "โจ’", # line integration with rectangular path around pole "\\scpolint" => "โจ“", # line integration with semicircular path around pole "\\npolint" => "โจ”", # line integration not including the pole "\\pointint" => "โจ•", # integral around a point operator "\\ringplus" => "โจข", # plus sign with small circle above "\\plushat" => "โจฃ", # plus sign with circumflex accent above "\\simplus" => "โจค", # plus sign with tilde above "\\plussim" => "โจฆ", # plus sign with tilde below "\\plussubtwo" => "โจง", # plus sign with subscript two "\\plustrif" => "โจจ", # plus sign with black triangle "\\commaminus" => "โจฉ", # minus sign with comma above "\\minusfdots" => "โจซ", # minus sign with falling dots "\\minusrdots" => "โจฌ", # minus sign with rising dots "\\opluslhrim" => "โจญ", # plus sign in left half circle "\\oplusrhrim" => "โจฎ", # plus sign in right half circle "\\dottimes" => "โจฐ", # multiplication sign with dot above "\\timesbar" => "โจฑ", # multiplication sign with underbar "\\smashtimes" => "โจณ", # smash product "\\otimeslhrim" => "โจด", # multiplication sign in left half circle "\\otimesrhrim" => "โจต", # multiplication sign in right half circle "\\otimeshat" => "โจถ", # circled multiplication sign with circumflex accent "\\Otimes" => "โจท", # multiplication sign in double circle "\\odiv" => "โจธ", # circled division sign "\\triangleplus" => "โจน", # plus sign in triangle "\\triangleminus" => "โจบ", # minus sign in triangle "\\triangletimes" => "โจป", # multiplication sign in triangle "\\capdot" => "โฉ€", # intersection with dot "\\uminus" => "โฉ", # union with minus sign "\\barcup" => "โฉ‚", # union with overbar "\\barcap" => "โฉƒ", # intersection with overbar "\\capwedge" => "โฉ„", # intersection with logical and "\\cupvee" => "โฉ…", # union with logical or "\\twocups" => "โฉŠ", # union beside and joined with union "\\twocaps" => "โฉ‹", # intersection beside and joined with intersection "\\closedvarcup" => "โฉŒ", # closed union with serifs "\\closedvarcap" => "โฉ", # closed intersection with serifs "\\Sqcap" => "โฉŽ", # double square intersection "\\Sqcup" => "โฉ", # double square union "\\closedvarcupsmashprod" => "โฉ", # closed union with serifs and smash product "\\wedgeodot" => "โฉ‘", # logical and with dot above "\\veeodot" => "โฉ’", # logical or with dot above "\\wedgeonwedge" => "โฉ•", # two intersecting logical and "\\bigslopedvee" => "โฉ—", # sloping large or "\\bigslopedwedge" => "โฉ˜", # sloping large and "\\wedgemidvert" => "โฉš", # logical and with middle stem "\\veemidvert" => "โฉ›", # logical or with middle stem "\\midbarwedge" => "โฉœ", # ogical and with horizontal dash "\\midbarvee" => "โฉ", # logical or with horizontal dash "\\wedgedoublebar" => "โฉ ", # logical and with double underbar "\\varveebar" => "โฉก", # small vee with underbar "\\doublebarvee" => "โฉข", # logical or with double overbar "\\veedoublebar" => "โฉฃ", # logical or with double underbar "\\eqdot" => "โฉฆ", # equals sign with dot below "\\dotequiv" => "โฉง", # identical with dot above "\\dotsim" => "โฉช", # tilde operator with dot above "\\simrdots" => "โฉซ", # tilde operator with rising dots "\\simminussim" => "โฉฌ", # similar minus similar "\\congdot" => "โฉญ", # congruent with dot above "\\asteq" => "โฉฎ", # equals with asterisk "\\hatapprox" => "โฉฏ", # almost equal to with circumflex accent "\\approxeqq" => "โฉฐ", # approximately equal or equal to "\\eqqplus" => "โฉฑ", # equals sign above plus sign "\\pluseqq" => "โฉฒ", # plus sign above equals sign "\\eqqsim" => "โฉณ", # equals sign above tilde operator "\\Coloneq" => "โฉด", # double colon equal "\\eqeqeq" => "โฉถ", # three consecutive equals signs "\\equivDD" => "โฉธ", # equivalent with four dots above "\\ltcir" => "โฉน", # less-than with circle inside "\\gtcir" => "โฉบ", # greater-than with circle inside "\\ltquest" => "โฉป", # less-than with question mark above "\\gtquest" => "โฉผ", # greater-than with question mark above "\\lesdot" => "โฉฟ", # less-than or slanted equal to with dot inside "\\gesdot" => "โช€", # greater-than or slanted equal to with dot inside "\\lesdoto" => "โช", # less-than or slanted equal to with dot above "\\gesdoto" => "โช‚", # greater-than or slanted equal to with dot above "\\lesdotor" => "โชƒ", # less-than or slanted equal to with dot above right "\\gesdotol" => "โช„", # greater-than or slanted equal to with dot above left "\\lsime" => "โช", # less-than above similar or equal "\\gsime" => "โชŽ", # greater-than above similar or equal "\\lsimg" => "โช", # less-than above similar above greater-than "\\gsiml" => "โช", # greater-than above similar above less-than "\\lgE" => "โช‘", # less-than above greater-than above double-line equal "\\glE" => "โช’", # greater-than above less-than above double-line equal "\\lesges" => "โช“", # less-than above slanted equal above greater-than above slanted equal "\\gesles" => "โช”", # greater-than above slanted equal above less-than above slanted equal "\\elsdot" => "โช—", # slanted equal to or less-than with dot inside "\\egsdot" => "โช˜", # slanted equal to or greater-than with dot inside "\\eqqless" => "โช™", # double-line equal to or less-than "\\eqqgtr" => "โชš", # double-line equal to or greater-than "\\eqqslantless" => "โช›", # double-line slanted equal to or less-than "\\eqqslantgtr" => "โชœ", # double-line slanted equal to or greater-than "\\simless" => "โช", # similar or less-than "\\simgtr" => "โชž", # similar or greater-than "\\simlE" => "โชŸ", # similar above less-than above equals sign "\\simgE" => "โช ", # similar above greater-than above equals sign "\\glj" => "โชค", # greater-than overlapping less-than "\\gla" => "โชฅ", # greater-than beside less-than "\\ltcc" => "โชฆ", # less-than closed by curve "\\gtcc" => "โชง", # greater-than closed by curve "\\lescc" => "โชจ", # less-than closed by curve above slanted equal "\\gescc" => "โชฉ", # greater-than closed by curve above slanted equal "\\smt" => "โชช", # smaller than "\\lat" => "โชซ", # larger than "\\smte" => "โชฌ", # smaller than or equal to "\\late" => "โชญ", # larger than or equal to "\\precneq" => "โชฑ", # precedes above single-line not equal to "\\succneq" => "โชฒ", # succeeds above single-line not equal to "\\preceqq" => "โชณ", # precedes above equals sign "\\succeqq" => "โชด", # succeeds above equals sign "\\Prec" => "โชป", # double precedes "\\Succ" => "โชผ", # double succeeds "\\subsetdot" => "โชฝ", # subset with dot "\\supsetdot" => "โชพ", # superset with dot "\\subsetplus" => "โชฟ", # subset with plus sign below "\\supsetplus" => "โซ€", # superset with plus sign below "\\submult" => "โซ", # subset with multiplication sign below "\\supmult" => "โซ‚", # superset with multiplication sign below "\\subedot" => "โซƒ", # subset of or equal to with dot above "\\supedot" => "โซ„", # superset of or equal to with dot above "\\subsim" => "โซ‡", # subset of above tilde operator "\\supsim" => "โซˆ", # superset of above tilde operator "\\subsetapprox" => "โซ‰", # subset of above almost equal to "\\supsetapprox" => "โซŠ", # superset of above almost equal to "\\lsqhook" => "โซ", # square left open box operator "\\rsqhook" => "โซŽ", # square right open box operator "\\csub" => "โซ", # closed subset "\\csup" => "โซ", # closed superset "\\csube" => "โซ‘", # closed subset or equal to "\\csupe" => "โซ’", # closed superset or equal to "\\subsup" => "โซ“", # subset above superset "\\supsub" => "โซ”", # superset above subset "\\subsub" => "โซ•", # subset above subset "\\supsup" => "โซ–", # superset above superset "\\suphsub" => "โซ—", # superset beside subset "\\supdsub" => "โซ˜", # superset beside and joined by dash with subset "\\forkv" => "โซ™", # element of opening downwards "\\lllnest" => "โซท", # stacked very much less-than "\\gggnest" => "โซธ", # stacked very much greater-than "\\leqqslant" => "โซน", # double-line slanted less-than or equal to "\\geqqslant" => "โซบ", # double-line slanted greater-than or equal to "\\squaretopblack" => "\u2b12", # square with top half black "\\squarebotblack" => "\u2b13", # square with bottom half black "\\squareurblack" => "\u2b14", # square with upper right diagonal half black "\\squarellblack" => "\u2b15", # square with lower left diagonal half black "\\diamondleftblack" => "\u2b16", # diamond with left half black "\\diamondrightblack" => "\u2b17", # diamond with right half black "\\diamondtopblack" => "\u2b18", # diamond with top half black "\\diamondbotblack" => "\u2b19", # diamond with bottom half black "\\dottedsquare" => "\u2b1a", # dotted square "\\lgblksquare" => "\u2b1b", # black large square "\\lgwhtsquare" => "\u2b1c", # white large square "\\vysmblksquare" => "\u2b1d", # black very small square "\\vysmwhtsquare" => "\u2b1e", # white very small square "\\pentagonblack" => "\u2b1f", # black pentagon "\\pentagon" => "\u2b20", # white pentagon "\\varhexagon" => "\u2b21", # white hexagon "\\varhexagonblack" => "\u2b22", # black hexagon "\\hexagonblack" => "\u2b23", # horizontal black hexagon "\\lgblkcircle" => "\u2b24", # black large circle "\\mdblkdiamond" => "\u2b25", # black medium diamond "\\mdwhtdiamond" => "\u2b26", # white medium diamond "\\mdblklozenge" => "\u2b27", # black medium lozenge "\\mdwhtlozenge" => "\u2b28", # white medium lozenge "\\smblkdiamond" => "\u2b29", # black small diamond "\\smblklozenge" => "\u2b2a", # black small lozenge "\\smwhtlozenge" => "\u2b2b", # white small lozenge "\\blkhorzoval" => "\u2b2c", # black horizontal ellipse "\\whthorzoval" => "\u2b2d", # white horizontal ellipse "\\blkvertoval" => "\u2b2e", # black vertical ellipse "\\whtvertoval" => "\u2b2f", # white vertical ellipse "\\circleonleftarrow" => "\u2b30", # left arrow with small circle "\\leftthreearrows" => "\u2b31", # three leftwards arrows "\\leftarrowonoplus" => "\u2b32", # left arrow with circled plus "\\longleftsquigarrow" => "\u2b33", # long leftwards squiggle arrow "\\nvtwoheadleftarrow" => "\u2b34", # leftwards two-headed arrow with vertical stroke "\\nVtwoheadleftarrow" => "\u2b35", # leftwards two-headed arrow with double vertical stroke "\\twoheadmapsfrom" => "\u2b36", # leftwards two-headed arrow from bar "\\twoheadleftdbkarrow" => "\u2b37", # leftwards two-headed triple-dash arrow "\\leftdotarrow" => "\u2b38", # leftwards arrow with dotted stem "\\nvleftarrowtail" => "\u2b39", # leftwards arrow with tail with vertical stroke "\\nVleftarrowtail" => "\u2b3a", # leftwards arrow with tail with double vertical stroke "\\twoheadleftarrowtail" => "\u2b3b", # leftwards two-headed arrow with tail "\\nvtwoheadleftarrowtail" => "\u2b3c", # leftwards two-headed arrow with tail with vertical stroke "\\nVtwoheadleftarrowtail" => "\u2b3d", # leftwards two-headed arrow with tail with double vertical stroke "\\leftarrowx" => "\u2b3e", # leftwards arrow through x "\\leftcurvedarrow" => "\u2b3f", # wave arrow pointing directly left "\\equalleftarrow" => "\u2b40", # equals sign above leftwards arrow "\\bsimilarleftarrow" => "\u2b41", # reverse tilde operator above leftwards arrow "\\leftarrowbackapprox" => "\u2b42", # leftwards arrow above reverse almost equal to "\\rightarrowgtr" => "\u2b43", # rightwards arrow through greater-than "\\leftarrowless" => "\u2977", # leftwards arrow through less-than "\\rightarrowsupset" => "\u2b44", # rightwards arrow through superset "\\leftarrowsubset" => "\u297a", # leftwards arrow through subset "\\LLeftarrow" => "\u2b45", # leftwards quadruple arrow "\\RRightarrow" => "\u2b46", # rightwards quadruple arrow "\\bsimilarrightarrow" => "\u2b47", # reverse tilde operator above rightwards arrow "\\rightarrowbackapprox" => "\u2b48", # rightwards arrow above reverse almost equal to "\\similarleftarrow" => "\u2b49", # tilde operator above leftwards arrow "\\leftarrowapprox" => "\u2b4a", # leftwards arrow above almost equal to "\\leftarrowbsimilar" => "\u2b4b", # leftwards arrow above reverse tilde operator "\\rightarrowbsimilar" => "\u2b4c", # righttwards arrow above reverse tilde operator "\\medwhitestar" => "\u2b50", # white medium star "\\medblackstar" => "\u2b51", # black medium star "\\smwhitestar" => "\u2b52", # white small star "\\rightpentagonblack" => "\u2b53", # black right-pointing pentagon "\\rightpentagon" => "\u2b54", # white right-pointing pentagon "\\postalmark" => "ใ€’", # postal mark bold*"A" => "๐€", # mathematical bold capital a bold*"B" => "๐", # mathematical bold capital b bold*"C" => "๐‚", # mathematical bold capital c bold*"D" => "๐ƒ", # mathematical bold capital d bold*"E" => "๐„", # mathematical bold capital e bold*"F" => "๐…", # mathematical bold capital f bold*"G" => "๐†", # mathematical bold capital g bold*"H" => "๐‡", # mathematical bold capital h bold*"I" => "๐ˆ", # mathematical bold capital i bold*"J" => "๐‰", # mathematical bold capital j bold*"K" => "๐Š", # mathematical bold capital k bold*"L" => "๐‹", # mathematical bold capital l bold*"M" => "๐Œ", # mathematical bold capital m bold*"N" => "๐", # mathematical bold capital n bold*"O" => "๐Ž", # mathematical bold capital o bold*"P" => "๐", # mathematical bold capital p bold*"Q" => "๐", # mathematical bold capital q bold*"R" => "๐‘", # mathematical bold capital r bold*"S" => "๐’", # mathematical bold capital s bold*"T" => "๐“", # mathematical bold capital t bold*"U" => "๐”", # mathematical bold capital u bold*"V" => "๐•", # mathematical bold capital v bold*"W" => "๐–", # mathematical bold capital w bold*"X" => "๐—", # mathematical bold capital x bold*"Y" => "๐˜", # mathematical bold capital y bold*"Z" => "๐™", # mathematical bold capital z bold*"a" => "๐š", # mathematical bold small a bold*"b" => "๐›", # mathematical bold small b bold*"c" => "๐œ", # mathematical bold small c bold*"d" => "๐", # mathematical bold small d bold*"e" => "๐ž", # mathematical bold small e bold*"f" => "๐Ÿ", # mathematical bold small f bold*"g" => "๐ ", # mathematical bold small g bold*"h" => "๐ก", # mathematical bold small h bold*"i" => "๐ข", # mathematical bold small i bold*"j" => "๐ฃ", # mathematical bold small j bold*"k" => "๐ค", # mathematical bold small k bold*"l" => "๐ฅ", # mathematical bold small l bold*"m" => "๐ฆ", # mathematical bold small m bold*"n" => "๐ง", # mathematical bold small n bold*"o" => "๐จ", # mathematical bold small o bold*"p" => "๐ฉ", # mathematical bold small p bold*"q" => "๐ช", # mathematical bold small q bold*"r" => "๐ซ", # mathematical bold small r bold*"s" => "๐ฌ", # mathematical bold small s bold*"t" => "๐ญ", # mathematical bold small t bold*"u" => "๐ฎ", # mathematical bold small u bold*"v" => "๐ฏ", # mathematical bold small v bold*"w" => "๐ฐ", # mathematical bold small w bold*"x" => "๐ฑ", # mathematical bold small x bold*"y" => "๐ฒ", # mathematical bold small y bold*"z" => "๐ณ", # mathematical bold small z italic*"A" => "๐ด", # mathematical italic capital a italic*"B" => "๐ต", # mathematical italic capital b italic*"C" => "๐ถ", # mathematical italic capital c italic*"D" => "๐ท", # mathematical italic capital d italic*"E" => "๐ธ", # mathematical italic capital e italic*"F" => "๐น", # mathematical italic capital f italic*"G" => "๐บ", # mathematical italic capital g italic*"H" => "๐ป", # mathematical italic capital h italic*"I" => "๐ผ", # mathematical italic capital i italic*"J" => "๐ฝ", # mathematical italic capital j italic*"K" => "๐พ", # mathematical italic capital k italic*"L" => "๐ฟ", # mathematical italic capital l italic*"M" => "๐‘€", # mathematical italic capital m italic*"N" => "๐‘", # mathematical italic capital n italic*"O" => "๐‘‚", # mathematical italic capital o italic*"P" => "๐‘ƒ", # mathematical italic capital p italic*"Q" => "๐‘„", # mathematical italic capital q italic*"R" => "๐‘…", # mathematical italic capital r italic*"S" => "๐‘†", # mathematical italic capital s italic*"T" => "๐‘‡", # mathematical italic capital t italic*"U" => "๐‘ˆ", # mathematical italic capital u italic*"V" => "๐‘‰", # mathematical italic capital v italic*"W" => "๐‘Š", # mathematical italic capital w italic*"X" => "๐‘‹", # mathematical italic capital x italic*"Y" => "๐‘Œ", # mathematical italic capital y italic*"Z" => "๐‘", # mathematical italic capital z italic*"a" => "๐‘Ž", # mathematical italic small a italic*"b" => "๐‘", # mathematical italic small b italic*"c" => "๐‘", # mathematical italic small c italic*"d" => "๐‘‘", # mathematical italic small d italic*"e" => "๐‘’", # mathematical italic small e italic*"f" => "๐‘“", # mathematical italic small f italic*"g" => "๐‘”", # mathematical italic small g italic*"h" => "โ„Ž", # mathematical italic small h (planck constant) italic*"i" => "๐‘–", # mathematical italic small i italic*"j" => "๐‘—", # mathematical italic small j italic*"k" => "๐‘˜", # mathematical italic small k italic*"l" => "๐‘™", # mathematical italic small l italic*"m" => "๐‘š", # mathematical italic small m italic*"n" => "๐‘›", # mathematical italic small n italic*"o" => "๐‘œ", # mathematical italic small o italic*"p" => "๐‘", # mathematical italic small p italic*"q" => "๐‘ž", # mathematical italic small q italic*"r" => "๐‘Ÿ", # mathematical italic small r italic*"s" => "๐‘ ", # mathematical italic small s italic*"t" => "๐‘ก", # mathematical italic small t italic*"u" => "๐‘ข", # mathematical italic small u italic*"v" => "๐‘ฃ", # mathematical italic small v italic*"w" => "๐‘ค", # mathematical italic small w italic*"x" => "๐‘ฅ", # mathematical italic small x italic*"y" => "๐‘ฆ", # mathematical italic small y italic*"z" => "๐‘ง", # mathematical italic small z bolditalic*"A" => "๐‘จ", # mathematical bold italic capital a bolditalic*"B" => "๐‘ฉ", # mathematical bold italic capital b bolditalic*"C" => "๐‘ช", # mathematical bold italic capital c bolditalic*"D" => "๐‘ซ", # mathematical bold italic capital d bolditalic*"E" => "๐‘ฌ", # mathematical bold italic capital e bolditalic*"F" => "๐‘ญ", # mathematical bold italic capital f bolditalic*"G" => "๐‘ฎ", # mathematical bold italic capital g bolditalic*"H" => "๐‘ฏ", # mathematical bold italic capital h bolditalic*"I" => "๐‘ฐ", # mathematical bold italic capital i bolditalic*"J" => "๐‘ฑ", # mathematical bold italic capital j bolditalic*"K" => "๐‘ฒ", # mathematical bold italic capital k bolditalic*"L" => "๐‘ณ", # mathematical bold italic capital l bolditalic*"M" => "๐‘ด", # mathematical bold italic capital m bolditalic*"N" => "๐‘ต", # mathematical bold italic capital n bolditalic*"O" => "๐‘ถ", # mathematical bold italic capital o bolditalic*"P" => "๐‘ท", # mathematical bold italic capital p bolditalic*"Q" => "๐‘ธ", # mathematical bold italic capital q bolditalic*"R" => "๐‘น", # mathematical bold italic capital r bolditalic*"S" => "๐‘บ", # mathematical bold italic capital s bolditalic*"T" => "๐‘ป", # mathematical bold italic capital t bolditalic*"U" => "๐‘ผ", # mathematical bold italic capital u bolditalic*"V" => "๐‘ฝ", # mathematical bold italic capital v bolditalic*"W" => "๐‘พ", # mathematical bold italic capital w bolditalic*"X" => "๐‘ฟ", # mathematical bold italic capital x bolditalic*"Y" => "๐’€", # mathematical bold italic capital y bolditalic*"Z" => "๐’", # mathematical bold italic capital z bolditalic*"a" => "๐’‚", # mathematical bold italic small a bolditalic*"b" => "๐’ƒ", # mathematical bold italic small b bolditalic*"c" => "๐’„", # mathematical bold italic small c bolditalic*"d" => "๐’…", # mathematical bold italic small d bolditalic*"e" => "๐’†", # mathematical bold italic small e bolditalic*"f" => "๐’‡", # mathematical bold italic small f bolditalic*"g" => "๐’ˆ", # mathematical bold italic small g bolditalic*"h" => "๐’‰", # mathematical bold italic small h bolditalic*"i" => "๐’Š", # mathematical bold italic small i bolditalic*"j" => "๐’‹", # mathematical bold italic small j bolditalic*"k" => "๐’Œ", # mathematical bold italic small k bolditalic*"l" => "๐’", # mathematical bold italic small l bolditalic*"m" => "๐’Ž", # mathematical bold italic small m bolditalic*"n" => "๐’", # mathematical bold italic small n bolditalic*"o" => "๐’", # mathematical bold italic small o bolditalic*"p" => "๐’‘", # mathematical bold italic small p bolditalic*"q" => "๐’’", # mathematical bold italic small q bolditalic*"r" => "๐’“", # mathematical bold italic small r bolditalic*"s" => "๐’”", # mathematical bold italic small s bolditalic*"t" => "๐’•", # mathematical bold italic small t bolditalic*"u" => "๐’–", # mathematical bold italic small u bolditalic*"v" => "๐’—", # mathematical bold italic small v bolditalic*"w" => "๐’˜", # mathematical bold italic small w bolditalic*"x" => "๐’™", # mathematical bold italic small x bolditalic*"y" => "๐’š", # mathematical bold italic small y bolditalic*"z" => "๐’›", # mathematical bold italic small z script*"A" => "๐’œ", # mathematical script capital a script*"C" => "๐’ž", # mathematical script capital c script*"D" => "๐’Ÿ", # mathematical script capital d script*"G" => "๐’ข", # mathematical script capital g script*"J" => "๐’ฅ", # mathematical script capital j script*"K" => "๐’ฆ", # mathematical script capital k script*"N" => "๐’ฉ", # mathematical script capital n script*"O" => "๐’ช", # mathematical script capital o script*"P" => "๐’ซ", # mathematical script capital p script*"Q" => "๐’ฌ", # mathematical script capital q script*"S" => "๐’ฎ", # mathematical script capital s script*"T" => "๐’ฏ", # mathematical script capital t script*"U" => "๐’ฐ", # mathematical script capital u script*"V" => "๐’ฑ", # mathematical script capital v script*"W" => "๐’ฒ", # mathematical script capital w script*"X" => "๐’ณ", # mathematical script capital x script*"Y" => "๐’ด", # mathematical script capital y script*"Z" => "๐’ต", # mathematical script capital z script*"a" => "๐’ถ", # mathematical script small a script*"b" => "๐’ท", # mathematical script small b script*"c" => "๐’ธ", # mathematical script small c script*"d" => "๐’น", # mathematical script small d script*"f" => "๐’ป", # mathematical script small f script*"h" => "๐’ฝ", # mathematical script small h script*"i" => "๐’พ", # mathematical script small i script*"j" => "๐’ฟ", # mathematical script small j script*"k" => "๐“€", # mathematical script small k script*"l" => "\U1d4c1", # mathematical script small l script*"m" => "๐“‚", # mathematical script small m script*"n" => "๐“ƒ", # mathematical script small n script*"p" => "๐“…", # mathematical script small p script*"q" => "๐“†", # mathematical script small q script*"r" => "๐“‡", # mathematical script small r script*"s" => "๐“ˆ", # mathematical script small s script*"t" => "๐“‰", # mathematical script small t script*"u" => "๐“Š", # mathematical script small u script*"v" => "๐“‹", # mathematical script small v script*"w" => "๐“Œ", # mathematical script small w script*"x" => "๐“", # mathematical script small x script*"y" => "๐“Ž", # mathematical script small y script*"z" => "๐“", # mathematical script small z boldscript*"A" => "๐“", # mathematical bold script capital a boldscript*"B" => "๐“‘", # mathematical bold script capital b boldscript*"C" => "๐“’", # mathematical bold script capital c boldscript*"D" => "๐““", # mathematical bold script capital d boldscript*"E" => "๐“”", # mathematical bold script capital e boldscript*"F" => "๐“•", # mathematical bold script capital f boldscript*"G" => "๐“–", # mathematical bold script capital g boldscript*"H" => "๐“—", # mathematical bold script capital h boldscript*"I" => "๐“˜", # mathematical bold script capital i boldscript*"J" => "๐“™", # mathematical bold script capital j boldscript*"K" => "๐“š", # mathematical bold script capital k boldscript*"L" => "๐“›", # mathematical bold script capital l boldscript*"M" => "๐“œ", # mathematical bold script capital m boldscript*"N" => "๐“", # mathematical bold script capital n boldscript*"O" => "๐“ž", # mathematical bold script capital o boldscript*"P" => "๐“Ÿ", # mathematical bold script capital p boldscript*"Q" => "๐“ ", # mathematical bold script capital q boldscript*"R" => "๐“ก", # mathematical bold script capital r boldscript*"S" => "๐“ข", # mathematical bold script capital s boldscript*"T" => "๐“ฃ", # mathematical bold script capital t boldscript*"U" => "๐“ค", # mathematical bold script capital u boldscript*"V" => "๐“ฅ", # mathematical bold script capital v boldscript*"W" => "๐“ฆ", # mathematical bold script capital w boldscript*"X" => "๐“ง", # mathematical bold script capital x boldscript*"Y" => "๐“จ", # mathematical bold script capital y boldscript*"Z" => "๐“ฉ", # mathematical bold script capital z boldscript*"a" => "๐“ช", # mathematical bold script small a boldscript*"b" => "๐“ซ", # mathematical bold script small b boldscript*"c" => "๐“ฌ", # mathematical bold script small c boldscript*"d" => "๐“ญ", # mathematical bold script small d boldscript*"e" => "๐“ฎ", # mathematical bold script small e boldscript*"f" => "๐“ฏ", # mathematical bold script small f boldscript*"g" => "๐“ฐ", # mathematical bold script small g boldscript*"h" => "๐“ฑ", # mathematical bold script small h boldscript*"i" => "๐“ฒ", # mathematical bold script small i boldscript*"j" => "๐“ณ", # mathematical bold script small j boldscript*"k" => "๐“ด", # mathematical bold script small k boldscript*"l" => "๐“ต", # mathematical bold script small l boldscript*"m" => "๐“ถ", # mathematical bold script small m boldscript*"n" => "๐“ท", # mathematical bold script small n boldscript*"o" => "๐“ธ", # mathematical bold script small o boldscript*"p" => "๐“น", # mathematical bold script small p boldscript*"q" => "๐“บ", # mathematical bold script small q boldscript*"r" => "๐“ป", # mathematical bold script small r boldscript*"s" => "๐“ผ", # mathematical bold script small s boldscript*"t" => "๐“ฝ", # mathematical bold script small t boldscript*"u" => "๐“พ", # mathematical bold script small u boldscript*"v" => "๐“ฟ", # mathematical bold script small v boldscript*"w" => "๐”€", # mathematical bold script small w boldscript*"x" => "๐”", # mathematical bold script small x boldscript*"y" => "๐”‚", # mathematical bold script small y boldscript*"z" => "๐”ƒ", # mathematical bold script small z frak*"A" => "๐”„", # mathematical fraktur capital a frak*"B" => "๐”…", # mathematical fraktur capital b frak*"D" => "๐”‡", # mathematical fraktur capital d frak*"E" => "๐”ˆ", # mathematical fraktur capital e frak*"F" => "๐”‰", # mathematical fraktur capital f frak*"G" => "๐”Š", # mathematical fraktur capital g frak*"I" => "โ„‘", # black-letter capital i (manual addition) frak*"J" => "๐”", # mathematical fraktur capital j frak*"K" => "๐”Ž", # mathematical fraktur capital k frak*"L" => "๐”", # mathematical fraktur capital l frak*"M" => "๐”", # mathematical fraktur capital m frak*"N" => "๐”‘", # mathematical fraktur capital n frak*"O" => "๐”’", # mathematical fraktur capital o frak*"P" => "๐”“", # mathematical fraktur capital p frak*"Q" => "๐””", # mathematical fraktur capital q frak*"R" => "โ„œ", # black-letter capital r (manual addition) frak*"S" => "๐”–", # mathematical fraktur capital s frak*"T" => "๐”—", # mathematical fraktur capital t frak*"U" => "๐”˜", # mathematical fraktur capital u frak*"V" => "๐”™", # mathematical fraktur capital v frak*"W" => "๐”š", # mathematical fraktur capital w frak*"X" => "๐”›", # mathematical fraktur capital x frak*"Y" => "๐”œ", # mathematical fraktur capital y frak*"a" => "๐”ž", # mathematical fraktur small a frak*"b" => "๐”Ÿ", # mathematical fraktur small b frak*"c" => "๐” ", # mathematical fraktur small c frak*"d" => "๐”ก", # mathematical fraktur small d frak*"e" => "๐”ข", # mathematical fraktur small e frak*"f" => "๐”ฃ", # mathematical fraktur small f frak*"g" => "๐”ค", # mathematical fraktur small g frak*"h" => "๐”ฅ", # mathematical fraktur small h frak*"i" => "๐”ฆ", # mathematical fraktur small i frak*"j" => "๐”ง", # mathematical fraktur small j frak*"k" => "๐”จ", # mathematical fraktur small k frak*"l" => "๐”ฉ", # mathematical fraktur small l frak*"m" => "๐”ช", # mathematical fraktur small m frak*"n" => "๐”ซ", # mathematical fraktur small n frak*"o" => "๐”ฌ", # mathematical fraktur small o frak*"p" => "๐”ญ", # mathematical fraktur small p frak*"q" => "๐”ฎ", # mathematical fraktur small q frak*"r" => "๐”ฏ", # mathematical fraktur small r frak*"s" => "๐”ฐ", # mathematical fraktur small s frak*"t" => "๐”ฑ", # mathematical fraktur small t frak*"u" => "๐”ฒ", # mathematical fraktur small u frak*"v" => "๐”ณ", # mathematical fraktur small v frak*"w" => "๐”ด", # mathematical fraktur small w frak*"x" => "๐”ต", # mathematical fraktur small x frak*"y" => "๐”ถ", # mathematical fraktur small y frak*"z" => "๐”ท", # mathematical fraktur small z blackboard*"A" => "๐”ธ", # mathematical double-struck capital a blackboard*"B" => "๐”น", # mathematical double-struck capital b blackboard*"D" => "๐”ป", # mathematical double-struck capital d blackboard*"E" => "๐”ผ", # mathematical double-struck capital e blackboard*"F" => "๐”ฝ", # mathematical double-struck capital f blackboard*"G" => "๐”พ", # mathematical double-struck capital g blackboard*"I" => "๐•€", # mathematical double-struck capital i blackboard*"J" => "๐•", # mathematical double-struck capital j blackboard*"K" => "๐•‚", # mathematical double-struck capital k blackboard*"L" => "๐•ƒ", # mathematical double-struck capital l blackboard*"M" => "๐•„", # mathematical double-struck capital m blackboard*"O" => "๐•†", # mathematical double-struck capital o blackboard*"S" => "๐•Š", # mathematical double-struck capital s blackboard*"T" => "๐•‹", # mathematical double-struck capital t blackboard*"U" => "๐•Œ", # mathematical double-struck capital u blackboard*"V" => "๐•", # mathematical double-struck capital v blackboard*"W" => "๐•Ž", # mathematical double-struck capital w blackboard*"X" => "๐•", # mathematical double-struck capital x blackboard*"Y" => "๐•", # mathematical double-struck capital y blackboard*"a" => "๐•’", # mathematical double-struck small a blackboard*"b" => "๐•“", # mathematical double-struck small b blackboard*"c" => "๐•”", # mathematical double-struck small c blackboard*"d" => "๐••", # mathematical double-struck small d blackboard*"e" => "๐•–", # mathematical double-struck small e blackboard*"f" => "๐•—", # mathematical double-struck small f blackboard*"g" => "๐•˜", # mathematical double-struck small g blackboard*"h" => "๐•™", # mathematical double-struck small h blackboard*"i" => "๐•š", # mathematical double-struck small i blackboard*"j" => "๐•›", # mathematical double-struck small j blackboard*"k" => "๐•œ", # mathematical double-struck small k blackboard*"l" => "๐•", # mathematical double-struck small l blackboard*"m" => "๐•ž", # mathematical double-struck small m blackboard*"n" => "๐•Ÿ", # mathematical double-struck small n blackboard*"o" => "๐• ", # mathematical double-struck small o blackboard*"p" => "๐•ก", # mathematical double-struck small p blackboard*"q" => "๐•ข", # mathematical double-struck small q blackboard*"r" => "๐•ฃ", # mathematical double-struck small r blackboard*"s" => "๐•ค", # mathematical double-struck small s blackboard*"t" => "๐•ฅ", # mathematical double-struck small t blackboard*"u" => "๐•ฆ", # mathematical double-struck small u blackboard*"v" => "๐•ง", # mathematical double-struck small v blackboard*"w" => "๐•จ", # mathematical double-struck small w blackboard*"x" => "๐•ฉ", # mathematical double-struck small x blackboard*"y" => "๐•ช", # mathematical double-struck small y blackboard*"z" => "๐•ซ", # mathematical double-struck small z boldfrak*"A" => "๐•ฌ", # mathematical bold fraktur capital a boldfrak*"B" => "๐•ญ", # mathematical bold fraktur capital b boldfrak*"C" => "๐•ฎ", # mathematical bold fraktur capital c boldfrak*"D" => "๐•ฏ", # mathematical bold fraktur capital d boldfrak*"E" => "๐•ฐ", # mathematical bold fraktur capital e boldfrak*"F" => "๐•ฑ", # mathematical bold fraktur capital f boldfrak*"G" => "๐•ฒ", # mathematical bold fraktur capital g boldfrak*"H" => "๐•ณ", # mathematical bold fraktur capital h boldfrak*"I" => "๐•ด", # mathematical bold fraktur capital i boldfrak*"J" => "๐•ต", # mathematical bold fraktur capital j boldfrak*"K" => "๐•ถ", # mathematical bold fraktur capital k boldfrak*"L" => "๐•ท", # mathematical bold fraktur capital l boldfrak*"M" => "๐•ธ", # mathematical bold fraktur capital m boldfrak*"N" => "๐•น", # mathematical bold fraktur capital n boldfrak*"O" => "๐•บ", # mathematical bold fraktur capital o boldfrak*"P" => "๐•ป", # mathematical bold fraktur capital p boldfrak*"Q" => "๐•ผ", # mathematical bold fraktur capital q boldfrak*"R" => "๐•ฝ", # mathematical bold fraktur capital r boldfrak*"S" => "๐•พ", # mathematical bold fraktur capital s boldfrak*"T" => "๐•ฟ", # mathematical bold fraktur capital t boldfrak*"U" => "๐–€", # mathematical bold fraktur capital u boldfrak*"V" => "๐–", # mathematical bold fraktur capital v boldfrak*"W" => "๐–‚", # mathematical bold fraktur capital w boldfrak*"X" => "๐–ƒ", # mathematical bold fraktur capital x boldfrak*"Y" => "๐–„", # mathematical bold fraktur capital y boldfrak*"Z" => "๐–…", # mathematical bold fraktur capital z boldfrak*"a" => "๐–†", # mathematical bold fraktur small a boldfrak*"b" => "๐–‡", # mathematical bold fraktur small b boldfrak*"c" => "๐–ˆ", # mathematical bold fraktur small c boldfrak*"d" => "๐–‰", # mathematical bold fraktur small d boldfrak*"e" => "๐–Š", # mathematical bold fraktur small e boldfrak*"f" => "๐–‹", # mathematical bold fraktur small f boldfrak*"g" => "๐–Œ", # mathematical bold fraktur small g boldfrak*"h" => "๐–", # mathematical bold fraktur small h boldfrak*"i" => "๐–Ž", # mathematical bold fraktur small i boldfrak*"j" => "๐–", # mathematical bold fraktur small j boldfrak*"k" => "๐–", # mathematical bold fraktur small k boldfrak*"l" => "๐–‘", # mathematical bold fraktur small l boldfrak*"m" => "๐–’", # mathematical bold fraktur small m boldfrak*"n" => "๐–“", # mathematical bold fraktur small n boldfrak*"o" => "๐–”", # mathematical bold fraktur small o boldfrak*"p" => "๐–•", # mathematical bold fraktur small p boldfrak*"q" => "๐––", # mathematical bold fraktur small q boldfrak*"r" => "๐–—", # mathematical bold fraktur small r boldfrak*"s" => "๐–˜", # mathematical bold fraktur small s boldfrak*"t" => "๐–™", # mathematical bold fraktur small t boldfrak*"u" => "๐–š", # mathematical bold fraktur small u boldfrak*"v" => "๐–›", # mathematical bold fraktur small v boldfrak*"w" => "๐–œ", # mathematical bold fraktur small w boldfrak*"x" => "๐–", # mathematical bold fraktur small x boldfrak*"y" => "๐–ž", # mathematical bold fraktur small y boldfrak*"z" => "๐–Ÿ", # mathematical bold fraktur small z sans*"A" => "๐– ", # mathematical sans-serif capital a sans*"B" => "๐–ก", # mathematical sans-serif capital b sans*"C" => "๐–ข", # mathematical sans-serif capital c sans*"D" => "๐–ฃ", # mathematical sans-serif capital d sans*"E" => "๐–ค", # mathematical sans-serif capital e sans*"F" => "๐–ฅ", # mathematical sans-serif capital f sans*"G" => "๐–ฆ", # mathematical sans-serif capital g sans*"H" => "๐–ง", # mathematical sans-serif capital h sans*"I" => "๐–จ", # mathematical sans-serif capital i sans*"J" => "๐–ฉ", # mathematical sans-serif capital j sans*"K" => "๐–ช", # mathematical sans-serif capital k sans*"L" => "๐–ซ", # mathematical sans-serif capital l sans*"M" => "๐–ฌ", # mathematical sans-serif capital m sans*"N" => "๐–ญ", # mathematical sans-serif capital n sans*"O" => "๐–ฎ", # mathematical sans-serif capital o sans*"P" => "๐–ฏ", # mathematical sans-serif capital p sans*"Q" => "๐–ฐ", # mathematical sans-serif capital q sans*"R" => "๐–ฑ", # mathematical sans-serif capital r sans*"S" => "๐–ฒ", # mathematical sans-serif capital s sans*"T" => "๐–ณ", # mathematical sans-serif capital t sans*"U" => "๐–ด", # mathematical sans-serif capital u sans*"V" => "๐–ต", # mathematical sans-serif capital v sans*"W" => "๐–ถ", # mathematical sans-serif capital w sans*"X" => "๐–ท", # mathematical sans-serif capital x sans*"Y" => "๐–ธ", # mathematical sans-serif capital y sans*"Z" => "๐–น", # mathematical sans-serif capital z sans*"a" => "๐–บ", # mathematical sans-serif small a sans*"b" => "๐–ป", # mathematical sans-serif small b sans*"c" => "๐–ผ", # mathematical sans-serif small c sans*"d" => "๐–ฝ", # mathematical sans-serif small d sans*"e" => "๐–พ", # mathematical sans-serif small e sans*"f" => "๐–ฟ", # mathematical sans-serif small f sans*"g" => "๐—€", # mathematical sans-serif small g sans*"h" => "๐—", # mathematical sans-serif small h sans*"i" => "๐—‚", # mathematical sans-serif small i sans*"j" => "๐—ƒ", # mathematical sans-serif small j sans*"k" => "๐—„", # mathematical sans-serif small k sans*"l" => "๐—…", # mathematical sans-serif small l sans*"m" => "๐—†", # mathematical sans-serif small m sans*"n" => "๐—‡", # mathematical sans-serif small n sans*"o" => "๐—ˆ", # mathematical sans-serif small o sans*"p" => "๐—‰", # mathematical sans-serif small p sans*"q" => "๐—Š", # mathematical sans-serif small q sans*"r" => "๐—‹", # mathematical sans-serif small r sans*"s" => "๐—Œ", # mathematical sans-serif small s sans*"t" => "๐—", # mathematical sans-serif small t sans*"u" => "๐—Ž", # mathematical sans-serif small u sans*"v" => "๐—", # mathematical sans-serif small v sans*"w" => "๐—", # mathematical sans-serif small w sans*"x" => "๐—‘", # mathematical sans-serif small x sans*"y" => "๐—’", # mathematical sans-serif small y sans*"z" => "๐—“", # mathematical sans-serif small z boldsans*"A" => "๐—”", # mathematical sans-serif bold capital a boldsans*"B" => "๐—•", # mathematical sans-serif bold capital b boldsans*"C" => "๐—–", # mathematical sans-serif bold capital c boldsans*"D" => "๐——", # mathematical sans-serif bold capital d boldsans*"E" => "๐—˜", # mathematical sans-serif bold capital e boldsans*"F" => "๐—™", # mathematical sans-serif bold capital f boldsans*"G" => "๐—š", # mathematical sans-serif bold capital g boldsans*"H" => "๐—›", # mathematical sans-serif bold capital h boldsans*"I" => "๐—œ", # mathematical sans-serif bold capital i boldsans*"J" => "๐—", # mathematical sans-serif bold capital j boldsans*"K" => "๐—ž", # mathematical sans-serif bold capital k boldsans*"L" => "๐—Ÿ", # mathematical sans-serif bold capital l boldsans*"M" => "๐— ", # mathematical sans-serif bold capital m boldsans*"N" => "๐—ก", # mathematical sans-serif bold capital n boldsans*"O" => "๐—ข", # mathematical sans-serif bold capital o boldsans*"P" => "๐—ฃ", # mathematical sans-serif bold capital p boldsans*"Q" => "๐—ค", # mathematical sans-serif bold capital q boldsans*"R" => "๐—ฅ", # mathematical sans-serif bold capital r boldsans*"S" => "๐—ฆ", # mathematical sans-serif bold capital s boldsans*"T" => "๐—ง", # mathematical sans-serif bold capital t boldsans*"U" => "๐—จ", # mathematical sans-serif bold capital u boldsans*"V" => "๐—ฉ", # mathematical sans-serif bold capital v boldsans*"W" => "๐—ช", # mathematical sans-serif bold capital w boldsans*"X" => "๐—ซ", # mathematical sans-serif bold capital x boldsans*"Y" => "๐—ฌ", # mathematical sans-serif bold capital y boldsans*"Z" => "๐—ญ", # mathematical sans-serif bold capital z boldsans*"a" => "๐—ฎ", # mathematical sans-serif bold small a boldsans*"b" => "๐—ฏ", # mathematical sans-serif bold small b boldsans*"c" => "๐—ฐ", # mathematical sans-serif bold small c boldsans*"d" => "๐—ฑ", # mathematical sans-serif bold small d boldsans*"e" => "๐—ฒ", # mathematical sans-serif bold small e boldsans*"f" => "๐—ณ", # mathematical sans-serif bold small f boldsans*"g" => "๐—ด", # mathematical sans-serif bold small g boldsans*"h" => "๐—ต", # mathematical sans-serif bold small h boldsans*"i" => "๐—ถ", # mathematical sans-serif bold small i boldsans*"j" => "๐—ท", # mathematical sans-serif bold small j boldsans*"k" => "๐—ธ", # mathematical sans-serif bold small k boldsans*"l" => "๐—น", # mathematical sans-serif bold small l boldsans*"m" => "๐—บ", # mathematical sans-serif bold small m boldsans*"n" => "๐—ป", # mathematical sans-serif bold small n boldsans*"o" => "๐—ผ", # mathematical sans-serif bold small o boldsans*"p" => "๐—ฝ", # mathematical sans-serif bold small p boldsans*"q" => "๐—พ", # mathematical sans-serif bold small q boldsans*"r" => "๐—ฟ", # mathematical sans-serif bold small r boldsans*"s" => "๐˜€", # mathematical sans-serif bold small s boldsans*"t" => "๐˜", # mathematical sans-serif bold small t boldsans*"u" => "๐˜‚", # mathematical sans-serif bold small u boldsans*"v" => "๐˜ƒ", # mathematical sans-serif bold small v boldsans*"w" => "๐˜„", # mathematical sans-serif bold small w boldsans*"x" => "๐˜…", # mathematical sans-serif bold small x boldsans*"y" => "๐˜†", # mathematical sans-serif bold small y boldsans*"z" => "๐˜‡", # mathematical sans-serif bold small z italicsans*"A" => "๐˜ˆ", # mathematical sans-serif italic capital a italicsans*"B" => "๐˜‰", # mathematical sans-serif italic capital b italicsans*"C" => "๐˜Š", # mathematical sans-serif italic capital c italicsans*"D" => "๐˜‹", # mathematical sans-serif italic capital d italicsans*"E" => "๐˜Œ", # mathematical sans-serif italic capital e italicsans*"F" => "๐˜", # mathematical sans-serif italic capital f italicsans*"G" => "๐˜Ž", # mathematical sans-serif italic capital g italicsans*"H" => "๐˜", # mathematical sans-serif italic capital h italicsans*"I" => "๐˜", # mathematical sans-serif italic capital i italicsans*"J" => "๐˜‘", # mathematical sans-serif italic capital j italicsans*"K" => "๐˜’", # mathematical sans-serif italic capital k italicsans*"L" => "๐˜“", # mathematical sans-serif italic capital l italicsans*"M" => "๐˜”", # mathematical sans-serif italic capital m italicsans*"N" => "๐˜•", # mathematical sans-serif italic capital n italicsans*"O" => "๐˜–", # mathematical sans-serif italic capital o italicsans*"P" => "๐˜—", # mathematical sans-serif italic capital p italicsans*"Q" => "๐˜˜", # mathematical sans-serif italic capital q italicsans*"R" => "๐˜™", # mathematical sans-serif italic capital r italicsans*"S" => "๐˜š", # mathematical sans-serif italic capital s italicsans*"T" => "๐˜›", # mathematical sans-serif italic capital t italicsans*"U" => "๐˜œ", # mathematical sans-serif italic capital u italicsans*"V" => "๐˜", # mathematical sans-serif italic capital v italicsans*"W" => "๐˜ž", # mathematical sans-serif italic capital w italicsans*"X" => "๐˜Ÿ", # mathematical sans-serif italic capital x italicsans*"Y" => "๐˜ ", # mathematical sans-serif italic capital y italicsans*"Z" => "๐˜ก", # mathematical sans-serif italic capital z italicsans*"a" => "๐˜ข", # mathematical sans-serif italic small a italicsans*"b" => "๐˜ฃ", # mathematical sans-serif italic small b italicsans*"c" => "๐˜ค", # mathematical sans-serif italic small c italicsans*"d" => "๐˜ฅ", # mathematical sans-serif italic small d italicsans*"e" => "๐˜ฆ", # mathematical sans-serif italic small e italicsans*"f" => "๐˜ง", # mathematical sans-serif italic small f italicsans*"g" => "๐˜จ", # mathematical sans-serif italic small g italicsans*"h" => "๐˜ฉ", # mathematical sans-serif italic small h italicsans*"i" => "๐˜ช", # mathematical sans-serif italic small i italicsans*"j" => "๐˜ซ", # mathematical sans-serif italic small j italicsans*"k" => "๐˜ฌ", # mathematical sans-serif italic small k italicsans*"l" => "๐˜ญ", # mathematical sans-serif italic small l italicsans*"m" => "๐˜ฎ", # mathematical sans-serif italic small m italicsans*"n" => "๐˜ฏ", # mathematical sans-serif italic small n italicsans*"o" => "๐˜ฐ", # mathematical sans-serif italic small o italicsans*"p" => "๐˜ฑ", # mathematical sans-serif italic small p italicsans*"q" => "๐˜ฒ", # mathematical sans-serif italic small q italicsans*"r" => "๐˜ณ", # mathematical sans-serif italic small r italicsans*"s" => "๐˜ด", # mathematical sans-serif italic small s italicsans*"t" => "๐˜ต", # mathematical sans-serif italic small t italicsans*"u" => "๐˜ถ", # mathematical sans-serif italic small u italicsans*"v" => "๐˜ท", # mathematical sans-serif italic small v italicsans*"w" => "๐˜ธ", # mathematical sans-serif italic small w italicsans*"x" => "๐˜น", # mathematical sans-serif italic small x italicsans*"y" => "๐˜บ", # mathematical sans-serif italic small y italicsans*"z" => "๐˜ป", # mathematical sans-serif italic small z bolditalicsans*"A" => "๐˜ผ", # mathematical sans-serif bold italic capital a bolditalicsans*"B" => "๐˜ฝ", # mathematical sans-serif bold italic capital b bolditalicsans*"C" => "๐˜พ", # mathematical sans-serif bold italic capital c bolditalicsans*"D" => "๐˜ฟ", # mathematical sans-serif bold italic capital d bolditalicsans*"E" => "๐™€", # mathematical sans-serif bold italic capital e bolditalicsans*"F" => "๐™", # mathematical sans-serif bold italic capital f bolditalicsans*"G" => "๐™‚", # mathematical sans-serif bold italic capital g bolditalicsans*"H" => "๐™ƒ", # mathematical sans-serif bold italic capital h bolditalicsans*"I" => "๐™„", # mathematical sans-serif bold italic capital i bolditalicsans*"J" => "๐™…", # mathematical sans-serif bold italic capital j bolditalicsans*"K" => "๐™†", # mathematical sans-serif bold italic capital k bolditalicsans*"L" => "๐™‡", # mathematical sans-serif bold italic capital l bolditalicsans*"M" => "๐™ˆ", # mathematical sans-serif bold italic capital m bolditalicsans*"N" => "๐™‰", # mathematical sans-serif bold italic capital n bolditalicsans*"O" => "๐™Š", # mathematical sans-serif bold italic capital o bolditalicsans*"P" => "๐™‹", # mathematical sans-serif bold italic capital p bolditalicsans*"Q" => "๐™Œ", # mathematical sans-serif bold italic capital q bolditalicsans*"R" => "๐™", # mathematical sans-serif bold italic capital r bolditalicsans*"S" => "๐™Ž", # mathematical sans-serif bold italic capital s bolditalicsans*"T" => "๐™", # mathematical sans-serif bold italic capital t bolditalicsans*"U" => "๐™", # mathematical sans-serif bold italic capital u bolditalicsans*"V" => "๐™‘", # mathematical sans-serif bold italic capital v bolditalicsans*"W" => "๐™’", # mathematical sans-serif bold italic capital w bolditalicsans*"X" => "๐™“", # mathematical sans-serif bold italic capital x bolditalicsans*"Y" => "๐™”", # mathematical sans-serif bold italic capital y bolditalicsans*"Z" => "๐™•", # mathematical sans-serif bold italic capital z bolditalicsans*"a" => "๐™–", # mathematical sans-serif bold italic small a bolditalicsans*"b" => "๐™—", # mathematical sans-serif bold italic small b bolditalicsans*"c" => "๐™˜", # mathematical sans-serif bold italic small c bolditalicsans*"d" => "๐™™", # mathematical sans-serif bold italic small d bolditalicsans*"e" => "๐™š", # mathematical sans-serif bold italic small e bolditalicsans*"f" => "๐™›", # mathematical sans-serif bold italic small f bolditalicsans*"g" => "๐™œ", # mathematical sans-serif bold italic small g bolditalicsans*"h" => "๐™", # mathematical sans-serif bold italic small h bolditalicsans*"i" => "๐™ž", # mathematical sans-serif bold italic small i bolditalicsans*"j" => "๐™Ÿ", # mathematical sans-serif bold italic small j bolditalicsans*"k" => "๐™ ", # mathematical sans-serif bold italic small k bolditalicsans*"l" => "๐™ก", # mathematical sans-serif bold italic small l bolditalicsans*"m" => "๐™ข", # mathematical sans-serif bold italic small m bolditalicsans*"n" => "๐™ฃ", # mathematical sans-serif bold italic small n bolditalicsans*"o" => "๐™ค", # mathematical sans-serif bold italic small o bolditalicsans*"p" => "๐™ฅ", # mathematical sans-serif bold italic small p bolditalicsans*"q" => "๐™ฆ", # mathematical sans-serif bold italic small q bolditalicsans*"r" => "๐™ง", # mathematical sans-serif bold italic small r bolditalicsans*"s" => "๐™จ", # mathematical sans-serif bold italic small s bolditalicsans*"t" => "๐™ฉ", # mathematical sans-serif bold italic small t bolditalicsans*"u" => "๐™ช", # mathematical sans-serif bold italic small u bolditalicsans*"v" => "๐™ซ", # mathematical sans-serif bold italic small v bolditalicsans*"w" => "๐™ฌ", # mathematical sans-serif bold italic small w bolditalicsans*"x" => "๐™ญ", # mathematical sans-serif bold italic small x bolditalicsans*"y" => "๐™ฎ", # mathematical sans-serif bold italic small y bolditalicsans*"z" => "๐™ฏ", # mathematical sans-serif bold italic small z mono*"A" => "๐™ฐ", # mathematical monospace capital a mono*"B" => "๐™ฑ", # mathematical monospace capital b mono*"C" => "๐™ฒ", # mathematical monospace capital c mono*"D" => "๐™ณ", # mathematical monospace capital d mono*"E" => "๐™ด", # mathematical monospace capital e mono*"F" => "๐™ต", # mathematical monospace capital f mono*"G" => "๐™ถ", # mathematical monospace capital g mono*"H" => "๐™ท", # mathematical monospace capital h mono*"I" => "๐™ธ", # mathematical monospace capital i mono*"J" => "๐™น", # mathematical monospace capital j mono*"K" => "๐™บ", # mathematical monospace capital k mono*"L" => "๐™ป", # mathematical monospace capital l mono*"M" => "๐™ผ", # mathematical monospace capital m mono*"N" => "๐™ฝ", # mathematical monospace capital n mono*"O" => "๐™พ", # mathematical monospace capital o mono*"P" => "๐™ฟ", # mathematical monospace capital p mono*"Q" => "๐š€", # mathematical monospace capital q mono*"R" => "๐š", # mathematical monospace capital r mono*"S" => "๐š‚", # mathematical monospace capital s mono*"T" => "๐šƒ", # mathematical monospace capital t mono*"U" => "๐š„", # mathematical monospace capital u mono*"V" => "๐š…", # mathematical monospace capital v mono*"W" => "๐š†", # mathematical monospace capital w mono*"X" => "๐š‡", # mathematical monospace capital x mono*"Y" => "๐šˆ", # mathematical monospace capital y mono*"Z" => "๐š‰", # mathematical monospace capital z mono*"a" => "๐šŠ", # mathematical monospace small a mono*"b" => "๐š‹", # mathematical monospace small b mono*"c" => "๐šŒ", # mathematical monospace small c mono*"d" => "๐š", # mathematical monospace small d mono*"e" => "๐šŽ", # mathematical monospace small e mono*"f" => "๐š", # mathematical monospace small f mono*"g" => "๐š", # mathematical monospace small g mono*"h" => "๐š‘", # mathematical monospace small h mono*"i" => "๐š’", # mathematical monospace small i mono*"j" => "๐š“", # mathematical monospace small j mono*"k" => "๐š”", # mathematical monospace small k mono*"l" => "๐š•", # mathematical monospace small l mono*"m" => "๐š–", # mathematical monospace small m mono*"n" => "๐š—", # mathematical monospace small n mono*"o" => "๐š˜", # mathematical monospace small o mono*"p" => "๐š™", # mathematical monospace small p mono*"q" => "๐šš", # mathematical monospace small q mono*"r" => "๐š›", # mathematical monospace small r mono*"s" => "๐šœ", # mathematical monospace small s mono*"t" => "๐š", # mathematical monospace small t mono*"u" => "๐šž", # mathematical monospace small u mono*"v" => "๐šŸ", # mathematical monospace small v mono*"w" => "๐š ", # mathematical monospace small w mono*"x" => "๐šก", # mathematical monospace small x mono*"y" => "๐šข", # mathematical monospace small y mono*"z" => "๐šฃ", # mathematical monospace small z italic*"imath" => "\U1d6a4", # mathematical italic small dotless i italic*"jmath" => "\U1d6a5", # mathematical italic small dotless j bold*"Alpha" => "๐šจ", # mathematical bold capital alpha bold*"Beta" => "๐šฉ", # mathematical bold capital beta bold*"Gamma" => "๐šช", # mathematical bold capital gamma bold*"Delta" => "๐šซ", # mathematical bold capital delta bold*"Epsilon" => "๐šฌ", # mathematical bold capital epsilon bold*"Zeta" => "๐šญ", # mathematical bold capital zeta bold*"Eta" => "๐šฎ", # mathematical bold capital eta bold*"Theta" => "๐šฏ", # mathematical bold capital theta bold*"Iota" => "๐šฐ", # mathematical bold capital iota bold*"Kappa" => "๐šฑ", # mathematical bold capital kappa bold*"Lambda" => "๐šฒ", # mathematical bold capital lambda bold*"Mu" => "๐šณ", # mathematical bold capital mu bold*"Nu" => "๐šด", # mathematical bold capital nu bold*"Xi" => "๐šต", # mathematical bold capital xi bold*"Omicron" => "๐šถ", # mathematical bold capital omicron bold*"Pi" => "๐šท", # mathematical bold capital pi bold*"Rho" => "๐šธ", # mathematical bold capital rho bold*"varTheta" => "๐šน", # mathematical bold capital theta symbol bold*"Sigma" => "๐šบ", # mathematical bold capital sigma bold*"Tau" => "๐šป", # mathematical bold capital tau bold*"Upsilon" => "๐šผ", # mathematical bold capital upsilon bold*"Phi" => "๐šฝ", # mathematical bold capital phi bold*"Chi" => "๐šพ", # mathematical bold capital chi bold*"Psi" => "๐šฟ", # mathematical bold capital psi bold*"Omega" => "๐›€", # mathematical bold capital omega bold*"nabla" => "๐›", # mathematical bold nabla bold*"alpha" => "๐›‚", # mathematical bold small alpha bold*"beta" => "๐›ƒ", # mathematical bold small beta bold*"gamma" => "๐›„", # mathematical bold small gamma bold*"delta" => "๐›…", # mathematical bold small delta bold*"varepsilon" => "๐›†", # mathematical bold small epsilon bold*"zeta" => "๐›‡", # mathematical bold small zeta bold*"eta" => "๐›ˆ", # mathematical bold small eta bold*"theta" => "๐›‰", # mathematical bold small theta bold*"iota" => "๐›Š", # mathematical bold small iota bold*"kappa" => "๐›‹", # mathematical bold small kappa bold*"lambda" => "๐›Œ", # mathematical bold small lambda bold*"mu" => "๐›", # mathematical bold small mu bold*"nu" => "๐›Ž", # mathematical bold small nu bold*"xi" => "๐›", # mathematical bold small xi bold*"omicron" => "๐›", # mathematical bold small omicron bold*"pi" => "๐›‘", # mathematical bold small pi bold*"rho" => "๐›’", # mathematical bold small rho bold*"varsigma" => "๐›“", # mathematical bold small final sigma bold*"sigma" => "๐›”", # mathematical bold small sigma bold*"tau" => "๐›•", # mathematical bold small tau bold*"upsilon" => "๐›–", # mathematical bold small upsilon bold*"varphi" => "๐›—", # mathematical bold small phi bold*"chi" => "๐›˜", # mathematical bold small chi bold*"psi" => "๐›™", # mathematical bold small psi bold*"omega" => "๐›š", # mathematical bold small omega bold*"partial" => "๐››", # mathematical bold partial differential bold*"epsilon" => "๐›œ", # mathematical bold epsilon symbol bold*"vartheta" => "๐›", # mathematical bold theta symbol bold*"varkappa" => "๐›ž", # mathematical bold kappa symbol bold*"phi" => "๐›Ÿ", # mathematical bold phi symbol bold*"varrho" => "๐› ", # mathematical bold rho symbol bold*"varpi" => "๐›ก", # mathematical bold pi symbol italic*"Alpha" => "๐›ข", # mathematical italic capital alpha italic*"Beta" => "๐›ฃ", # mathematical italic capital beta italic*"Gamma" => "๐›ค", # mathematical italic capital gamma italic*"Delta" => "๐›ฅ", # mathematical italic capital delta italic*"Epsilon" => "๐›ฆ", # mathematical italic capital epsilon italic*"Zeta" => "๐›ง", # mathematical italic capital zeta italic*"Eta" => "๐›จ", # mathematical italic capital eta italic*"Theta" => "๐›ฉ", # mathematical italic capital theta italic*"Iota" => "๐›ช", # mathematical italic capital iota italic*"Kappa" => "๐›ซ", # mathematical italic capital kappa italic*"Lambda" => "๐›ฌ", # mathematical italic capital lambda italic*"Mu" => "๐›ญ", # mathematical italic capital mu italic*"Nu" => "๐›ฎ", # mathematical italic capital nu italic*"Xi" => "๐›ฏ", # mathematical italic capital xi italic*"Omicron" => "๐›ฐ", # mathematical italic capital omicron italic*"Pi" => "๐›ฑ", # mathematical italic capital pi italic*"Rho" => "๐›ฒ", # mathematical italic capital rho italic*"varTheta" => "๐›ณ", # mathematical italic capital theta symbol italic*"Sigma" => "๐›ด", # mathematical italic capital sigma italic*"Tau" => "๐›ต", # mathematical italic capital tau italic*"Upsilon" => "๐›ถ", # mathematical italic capital upsilon italic*"Phi" => "๐›ท", # mathematical italic capital phi italic*"Chi" => "๐›ธ", # mathematical italic capital chi italic*"Psi" => "๐›น", # mathematical italic capital psi italic*"Omega" => "๐›บ", # mathematical italic capital omega italic*"nabla" => "๐›ป", # mathematical italic nabla italic*"alpha" => "๐›ผ", # mathematical italic small alpha italic*"beta" => "๐›ฝ", # mathematical italic small beta italic*"gamma" => "๐›พ", # mathematical italic small gamma italic*"delta" => "๐›ฟ", # mathematical italic small delta italic*"varepsilon" => "๐œ€", # mathematical italic small epsilon italic*"zeta" => "๐œ", # mathematical italic small zeta italic*"eta" => "๐œ‚", # mathematical italic small eta italic*"theta" => "๐œƒ", # mathematical italic small theta italic*"iota" => "๐œ„", # mathematical italic small iota italic*"kappa" => "๐œ…", # mathematical italic small kappa italic*"lambda" => "๐œ†", # mathematical italic small lambda italic*"mu" => "๐œ‡", # mathematical italic small mu italic*"nu" => "๐œˆ", # mathematical italic small nu italic*"xi" => "๐œ‰", # mathematical italic small xi italic*"omicron" => "๐œŠ", # mathematical italic small omicron italic*"pi" => "๐œ‹", # mathematical italic small pi italic*"rho" => "๐œŒ", # mathematical italic small rho italic*"varsigma" => "๐œ", # mathematical italic small final sigma italic*"sigma" => "๐œŽ", # mathematical italic small sigma italic*"tau" => "๐œ", # mathematical italic small tau italic*"upsilon" => "๐œ", # mathematical italic small upsilon italic*"varphi" => "๐œ‘", # mathematical italic small phi italic*"chi" => "๐œ’", # mathematical italic small chi italic*"psi" => "๐œ“", # mathematical italic small psi italic*"omega" => "๐œ”", # mathematical italic small omega italic*"partial" => "๐œ•", # mathematical italic partial differential italic*"epsilon" => "๐œ–", # mathematical italic epsilon symbol italic*"vartheta" => "๐œ—", # mathematical italic theta symbol italic*"varkappa" => "๐œ˜", # mathematical italic kappa symbol italic*"phi" => "๐œ™", # mathematical italic phi symbol italic*"varrho" => "๐œš", # mathematical italic rho symbol italic*"varpi" => "๐œ›", # mathematical italic pi symbol bolditalic*"Alpha" => "๐œœ", # mathematical bold italic capital alpha bolditalic*"Beta" => "๐œ", # mathematical bold italic capital beta bolditalic*"Gamma" => "๐œž", # mathematical bold italic capital gamma bolditalic*"Delta" => "๐œŸ", # mathematical bold italic capital delta bolditalic*"Epsilon" => "๐œ ", # mathematical bold italic capital epsilon bolditalic*"Zeta" => "๐œก", # mathematical bold italic capital zeta bolditalic*"Eta" => "๐œข", # mathematical bold italic capital eta bolditalic*"Theta" => "๐œฃ", # mathematical bold italic capital theta bolditalic*"Iota" => "๐œค", # mathematical bold italic capital iota bolditalic*"Kappa" => "๐œฅ", # mathematical bold italic capital kappa bolditalic*"Lambda" => "๐œฆ", # mathematical bold italic capital lambda bolditalic*"Mu" => "๐œง", # mathematical bold italic capital mu bolditalic*"Nu" => "๐œจ", # mathematical bold italic capital nu bolditalic*"Xi" => "๐œฉ", # mathematical bold italic capital xi bolditalic*"Omicron" => "๐œช", # mathematical bold italic capital omicron bolditalic*"Pi" => "๐œซ", # mathematical bold italic capital pi bolditalic*"Rho" => "๐œฌ", # mathematical bold italic capital rho bolditalic*"varTheta" => "๐œญ", # mathematical bold italic capital theta symbol bolditalic*"Sigma" => "๐œฎ", # mathematical bold italic capital sigma bolditalic*"Tau" => "๐œฏ", # mathematical bold italic capital tau bolditalic*"Upsilon" => "๐œฐ", # mathematical bold italic capital upsilon bolditalic*"Phi" => "๐œฑ", # mathematical bold italic capital phi bolditalic*"Chi" => "๐œฒ", # mathematical bold italic capital chi bolditalic*"Psi" => "๐œณ", # mathematical bold italic capital psi bolditalic*"Omega" => "๐œด", # mathematical bold italic capital omega bolditalic*"nabla" => "๐œต", # mathematical bold italic nabla bolditalic*"alpha" => "๐œถ", # mathematical bold italic small alpha bolditalic*"beta" => "๐œท", # mathematical bold italic small beta bolditalic*"gamma" => "๐œธ", # mathematical bold italic small gamma bolditalic*"delta" => "๐œน", # mathematical bold italic small delta bolditalic*"varepsilon" => "๐œบ", # mathematical bold italic small epsilon bolditalic*"zeta" => "๐œป", # mathematical bold italic small zeta bolditalic*"eta" => "๐œผ", # mathematical bold italic small eta bolditalic*"theta" => "๐œฝ", # mathematical bold italic small theta bolditalic*"iota" => "๐œพ", # mathematical bold italic small iota bolditalic*"kappa" => "๐œฟ", # mathematical bold italic small kappa bolditalic*"lambda" => "๐€", # mathematical bold italic small lambda bolditalic*"mu" => "๐", # mathematical bold italic small mu bolditalic*"nu" => "๐‚", # mathematical bold italic small nu bolditalic*"xi" => "๐ƒ", # mathematical bold italic small xi bolditalic*"omicron" => "๐„", # mathematical bold italic small omicron bolditalic*"pi" => "๐…", # mathematical bold italic small pi bolditalic*"rho" => "๐†", # mathematical bold italic small rho bolditalic*"varsigma" => "๐‡", # mathematical bold italic small final sigma bolditalic*"sigma" => "๐ˆ", # mathematical bold italic small sigma bolditalic*"tau" => "๐‰", # mathematical bold italic small tau bolditalic*"upsilon" => "๐Š", # mathematical bold italic small upsilon bolditalic*"varphi" => "๐‹", # mathematical bold italic small phi bolditalic*"chi" => "๐Œ", # mathematical bold italic small chi bolditalic*"psi" => "๐", # mathematical bold italic small psi bolditalic*"omega" => "๐Ž", # mathematical bold italic small omega bolditalic*"partial" => "๐", # mathematical bold italic partial differential bolditalic*"epsilon" => "๐", # mathematical bold italic epsilon symbol bolditalic*"vartheta" => "๐‘", # mathematical bold italic theta symbol bolditalic*"varkappa" => "๐’", # mathematical bold italic kappa symbol bolditalic*"phi" => "๐“", # mathematical bold italic phi symbol bolditalic*"varrho" => "๐”", # mathematical bold italic rho symbol bolditalic*"varpi" => "๐•", # mathematical bold italic pi symbol boldsans*"Alpha" => "๐–", # mathematical sans-serif bold capital alpha boldsans*"Beta" => "๐—", # mathematical sans-serif bold capital beta boldsans*"Gamma" => "๐˜", # mathematical sans-serif bold capital gamma boldsans*"Delta" => "๐™", # mathematical sans-serif bold capital delta boldsans*"Epsilon" => "๐š", # mathematical sans-serif bold capital epsilon boldsans*"Zeta" => "๐›", # mathematical sans-serif bold capital zeta boldsans*"Eta" => "๐œ", # mathematical sans-serif bold capital eta boldsans*"Theta" => "๐", # mathematical sans-serif bold capital theta boldsans*"Iota" => "๐ž", # mathematical sans-serif bold capital iota boldsans*"Kappa" => "๐Ÿ", # mathematical sans-serif bold capital kappa boldsans*"Lambda" => "๐ ", # mathematical sans-serif bold capital lambda boldsans*"Mu" => "๐ก", # mathematical sans-serif bold capital mu boldsans*"Nu" => "๐ข", # mathematical sans-serif bold capital nu boldsans*"Xi" => "๐ฃ", # mathematical sans-serif bold capital xi boldsans*"Omicron" => "๐ค", # mathematical sans-serif bold capital omicron boldsans*"Pi" => "๐ฅ", # mathematical sans-serif bold capital pi boldsans*"Rho" => "๐ฆ", # mathematical sans-serif bold capital rho boldsans*"varTheta" => "๐ง", # mathematical sans-serif bold capital theta symbol boldsans*"Sigma" => "๐จ", # mathematical sans-serif bold capital sigma boldsans*"Tau" => "๐ฉ", # mathematical sans-serif bold capital tau boldsans*"Upsilon" => "๐ช", # mathematical sans-serif bold capital upsilon boldsans*"Phi" => "๐ซ", # mathematical sans-serif bold capital phi boldsans*"Chi" => "๐ฌ", # mathematical sans-serif bold capital chi boldsans*"Psi" => "๐ญ", # mathematical sans-serif bold capital psi boldsans*"Omega" => "๐ฎ", # mathematical sans-serif bold capital omega boldsans*"nabla" => "๐ฏ", # mathematical sans-serif bold nabla boldsans*"alpha" => "๐ฐ", # mathematical sans-serif bold small alpha boldsans*"beta" => "๐ฑ", # mathematical sans-serif bold small beta boldsans*"gamma" => "๐ฒ", # mathematical sans-serif bold small gamma boldsans*"delta" => "๐ณ", # mathematical sans-serif bold small delta boldsans*"varepsilon" => "๐ด", # mathematical sans-serif bold small epsilon boldsans*"zeta" => "๐ต", # mathematical sans-serif bold small zeta boldsans*"eta" => "๐ถ", # mathematical sans-serif bold small eta boldsans*"theta" => "๐ท", # mathematical sans-serif bold small theta boldsans*"iota" => "๐ธ", # mathematical sans-serif bold small iota boldsans*"kappa" => "๐น", # mathematical sans-serif bold small kappa boldsans*"lambda" => "๐บ", # mathematical sans-serif bold small lambda boldsans*"mu" => "๐ป", # mathematical sans-serif bold small mu boldsans*"nu" => "๐ผ", # mathematical sans-serif bold small nu boldsans*"xi" => "๐ฝ", # mathematical sans-serif bold small xi boldsans*"omicron" => "๐พ", # mathematical sans-serif bold small omicron boldsans*"pi" => "๐ฟ", # mathematical sans-serif bold small pi boldsans*"rho" => "๐ž€", # mathematical sans-serif bold small rho boldsans*"varsigma" => "๐ž", # mathematical sans-serif bold small final sigma boldsans*"sigma" => "๐ž‚", # mathematical sans-serif bold small sigma boldsans*"tau" => "๐žƒ", # mathematical sans-serif bold small tau boldsans*"upsilon" => "๐ž„", # mathematical sans-serif bold small upsilon boldsans*"varphi" => "๐ž…", # mathematical sans-serif bold small phi boldsans*"chi" => "๐ž†", # mathematical sans-serif bold small chi boldsans*"psi" => "๐ž‡", # mathematical sans-serif bold small psi boldsans*"omega" => "๐žˆ", # mathematical sans-serif bold small omega boldsans*"partial" => "๐ž‰", # mathematical sans-serif bold partial differential boldsans*"epsilon" => "๐žŠ", # mathematical sans-serif bold epsilon symbol boldsans*"vartheta" => "๐ž‹", # mathematical sans-serif bold theta symbol boldsans*"varkappa" => "๐žŒ", # mathematical sans-serif bold kappa symbol boldsans*"phi" => "๐ž", # mathematical sans-serif bold phi symbol boldsans*"varrho" => "๐žŽ", # mathematical sans-serif bold rho symbol boldsans*"varpi" => "๐ž", # mathematical sans-serif bold pi symbol bolditalicsans*"Alpha" => "๐ž", # mathematical sans-serif bold italic capital alpha bolditalicsans*"Beta" => "๐ž‘", # mathematical sans-serif bold italic capital beta bolditalicsans*"Gamma" => "๐ž’", # mathematical sans-serif bold italic capital gamma bolditalicsans*"Delta" => "๐ž“", # mathematical sans-serif bold italic capital delta bolditalicsans*"Epsilon" => "๐ž”", # mathematical sans-serif bold italic capital epsilon bolditalicsans*"Zeta" => "๐ž•", # mathematical sans-serif bold italic capital zeta bolditalicsans*"Eta" => "๐ž–", # mathematical sans-serif bold italic capital eta bolditalicsans*"Theta" => "๐ž—", # mathematical sans-serif bold italic capital theta bolditalicsans*"Iota" => "๐ž˜", # mathematical sans-serif bold italic capital iota bolditalicsans*"Kappa" => "๐ž™", # mathematical sans-serif bold italic capital kappa bolditalicsans*"Lambda" => "๐žš", # mathematical sans-serif bold italic capital lambda bolditalicsans*"Mu" => "๐ž›", # mathematical sans-serif bold italic capital mu bolditalicsans*"Nu" => "๐žœ", # mathematical sans-serif bold italic capital nu bolditalicsans*"Xi" => "๐ž", # mathematical sans-serif bold italic capital xi bolditalicsans*"Omicron" => "๐žž", # mathematical sans-serif bold italic capital omicron bolditalicsans*"Pi" => "๐žŸ", # mathematical sans-serif bold italic capital pi bolditalicsans*"Rho" => "๐ž ", # mathematical sans-serif bold italic capital rho bolditalicsans*"varTheta" => "๐žก", # mathematical sans-serif bold italic capital theta symbol bolditalicsans*"Sigma" => "๐žข", # mathematical sans-serif bold italic capital sigma bolditalicsans*"Tau" => "๐žฃ", # mathematical sans-serif bold italic capital tau bolditalicsans*"Upsilon" => "๐žค", # mathematical sans-serif bold italic capital upsilon bolditalicsans*"Phi" => "๐žฅ", # mathematical sans-serif bold italic capital phi bolditalicsans*"Chi" => "๐žฆ", # mathematical sans-serif bold italic capital chi bolditalicsans*"Psi" => "๐žง", # mathematical sans-serif bold italic capital psi bolditalicsans*"Omega" => "๐žจ", # mathematical sans-serif bold italic capital omega bolditalicsans*"nabla" => "๐žฉ", # mathematical sans-serif bold italic nabla bolditalicsans*"alpha" => "๐žช", # mathematical sans-serif bold italic small alpha bolditalicsans*"beta" => "๐žซ", # mathematical sans-serif bold italic small beta bolditalicsans*"gamma" => "๐žฌ", # mathematical sans-serif bold italic small gamma bolditalicsans*"delta" => "๐žญ", # mathematical sans-serif bold italic small delta bolditalicsans*"varepsilon" => "๐žฎ", # mathematical sans-serif bold italic small epsilon bolditalicsans*"zeta" => "๐žฏ", # mathematical sans-serif bold italic small zeta bolditalicsans*"eta" => "๐žฐ", # mathematical sans-serif bold italic small eta bolditalicsans*"theta" => "๐žฑ", # mathematical sans-serif bold italic small theta bolditalicsans*"iota" => "๐žฒ", # mathematical sans-serif bold italic small iota bolditalicsans*"kappa" => "๐žณ", # mathematical sans-serif bold italic small kappa bolditalicsans*"lambda" => "๐žด", # mathematical sans-serif bold italic small lambda bolditalicsans*"mu" => "๐žต", # mathematical sans-serif bold italic small mu bolditalicsans*"nu" => "๐žถ", # mathematical sans-serif bold italic small nu bolditalicsans*"xi" => "๐žท", # mathematical sans-serif bold italic small xi bolditalicsans*"omicron" => "๐žธ", # mathematical sans-serif bold italic small omicron bolditalicsans*"pi" => "๐žน", # mathematical sans-serif bold italic small pi bolditalicsans*"rho" => "๐žบ", # mathematical sans-serif bold italic small rho bolditalicsans*"varsigma" => "๐žป", # mathematical sans-serif bold italic small final sigma bolditalicsans*"sigma" => "๐žผ", # mathematical sans-serif bold italic small sigma bolditalicsans*"tau" => "๐žฝ", # mathematical sans-serif bold italic small tau bolditalicsans*"upsilon" => "๐žพ", # mathematical sans-serif bold italic small upsilon bolditalicsans*"varphi" => "๐žฟ", # mathematical sans-serif bold italic small phi bolditalicsans*"chi" => "๐Ÿ€", # mathematical sans-serif bold italic small chi bolditalicsans*"psi" => "๐Ÿ", # mathematical sans-serif bold italic small psi bolditalicsans*"omega" => "๐Ÿ‚", # mathematical sans-serif bold italic small omega bolditalicsans*"partial" => "๐Ÿƒ", # mathematical sans-serif bold italic partial differential bolditalicsans*"epsilon" => "๐Ÿ„", # mathematical sans-serif bold italic epsilon symbol bolditalicsans*"vartheta" => "๐Ÿ…", # mathematical sans-serif bold italic theta symbol bolditalicsans*"varkappa" => "๐Ÿ†", # mathematical sans-serif bold italic kappa symbol bolditalicsans*"phi" => "๐Ÿ‡", # mathematical sans-serif bold italic phi symbol bolditalicsans*"varrho" => "๐Ÿˆ", # mathematical sans-serif bold italic rho symbol bolditalicsans*"varpi" => "๐Ÿ‰", # mathematical sans-serif bold italic pi symbol bold*"Digamma" => "\U1d7ca", # mathematical bold capital digamma bold*"digamma" => "\U1d7cb", # mathematical bold small digamma bold*"zero" => "๐ŸŽ", # mathematical bold digit 0 bold*"one" => "๐Ÿ", # mathematical bold digit 1 bold*"two" => "๐Ÿ", # mathematical bold digit 2 bold*"three" => "๐Ÿ‘", # mathematical bold digit 3 bold*"four" => "๐Ÿ’", # mathematical bold digit 4 bold*"five" => "๐Ÿ“", # mathematical bold digit 5 bold*"six" => "๐Ÿ”", # mathematical bold digit 6 bold*"seven" => "๐Ÿ•", # mathematical bold digit 7 bold*"eight" => "๐Ÿ–", # mathematical bold digit 8 bold*"nine" => "๐Ÿ—", # mathematical bold digit 9 blackboard*"zero" => "๐Ÿ˜", # mathematical double-struck digit 0 blackboard*"one" => "๐Ÿ™", # mathematical double-struck digit 1 blackboard*"two" => "๐Ÿš", # mathematical double-struck digit 2 blackboard*"three" => "๐Ÿ›", # mathematical double-struck digit 3 blackboard*"four" => "๐Ÿœ", # mathematical double-struck digit 4 blackboard*"five" => "๐Ÿ", # mathematical double-struck digit 5 blackboard*"six" => "๐Ÿž", # mathematical double-struck digit 6 blackboard*"seven" => "๐ŸŸ", # mathematical double-struck digit 7 blackboard*"eight" => "๐Ÿ ", # mathematical double-struck digit 8 blackboard*"nine" => "๐Ÿก", # mathematical double-struck digit 9 sans*"zero" => "๐Ÿข", # mathematical sans-serif digit 0 sans*"one" => "๐Ÿฃ", # mathematical sans-serif digit 1 sans*"two" => "๐Ÿค", # mathematical sans-serif digit 2 sans*"three" => "๐Ÿฅ", # mathematical sans-serif digit 3 sans*"four" => "๐Ÿฆ", # mathematical sans-serif digit 4 sans*"five" => "๐Ÿง", # mathematical sans-serif digit 5 sans*"six" => "๐Ÿจ", # mathematical sans-serif digit 6 sans*"seven" => "๐Ÿฉ", # mathematical sans-serif digit 7 sans*"eight" => "๐Ÿช", # mathematical sans-serif digit 8 sans*"nine" => "๐Ÿซ", # mathematical sans-serif digit 9 boldsans*"zero" => "๐Ÿฌ", # mathematical sans-serif bold digit 0 boldsans*"one" => "๐Ÿญ", # mathematical sans-serif bold digit 1 boldsans*"two" => "๐Ÿฎ", # mathematical sans-serif bold digit 2 boldsans*"three" => "๐Ÿฏ", # mathematical sans-serif bold digit 3 boldsans*"four" => "๐Ÿฐ", # mathematical sans-serif bold digit 4 boldsans*"five" => "๐Ÿฑ", # mathematical sans-serif bold digit 5 boldsans*"six" => "๐Ÿฒ", # mathematical sans-serif bold digit 6 boldsans*"seven" => "๐Ÿณ", # mathematical sans-serif bold digit 7 boldsans*"eight" => "๐Ÿด", # mathematical sans-serif bold digit 8 boldsans*"nine" => "๐Ÿต", # mathematical sans-serif bold digit 9 mono*"zero" => "๐Ÿถ", # mathematical monospace digit 0 mono*"one" => "๐Ÿท", # mathematical monospace digit 1 mono*"two" => "๐Ÿธ", # mathematical monospace digit 2 mono*"three" => "๐Ÿน", # mathematical monospace digit 3 mono*"four" => "๐Ÿบ", # mathematical monospace digit 4 mono*"five" => "๐Ÿป", # mathematical monospace digit 5 mono*"six" => "๐Ÿผ", # mathematical monospace digit 6 mono*"seven" => "๐Ÿฝ", # mathematical monospace digit 7 mono*"eight" => "๐Ÿพ", # mathematical monospace digit 8 mono*"nine" => "๐Ÿฟ", # mathematical monospace digit 9 "\\triangleright" => "โ–ท", # (large) right triangle, open; z notation range restriction "\\triangleleft" => "โ—", # (large) left triangle, open; z notation domain restriction "\\leftouterjoin" => "โŸ•", # left outer join "\\rightouterjoin" => "โŸ–", # right outer join "\\fullouterjoin" => "โŸ—", # full outer join "\\join" => "โจ", # join "\\underbar" => "ฬฒ", # combining low line "\\underleftrightarrow" => "อ", # underleftrightarrow accent "\\leftwavearrow" => "โ†œ", # left arrow-wavy "\\rightwavearrow" => "โ†", # right arrow-wavy "\\varbarwedge" => "โŒ…", # /barwedge b: logical and, bar above [projective (bar over small wedge)] "\\smallblacktriangleright" => "โ–ธ", # right triangle, filled "\\smallblacktriangleleft" => "โ—‚", # left triangle, filled "\\leftmoon" => "โ˜พ", # last quarter moon "\\smalltriangleright" => "โ–น", # right triangle, open "\\smalltriangleleft" => "โ—ƒ", # left triangle, open "\\tricolon" => "โ", # tricolon # fractions "\\1/4" => "ยผ", # vulgar fraction one quarter "\\1/2" => "ยฝ", # vulgar fraction one half "\\3/4" => "ยพ", # vulgar fraction three quarters "\\1/7" => "โ…", # vulgar fraction one seventh "\\1/9" => "โ…‘", # vulgar fraction one ninth "\\1/10" => "โ…’", # vulgar fraction one tenth "\\1/3" => "โ…“", # vulgar fraction one third "\\2/3" => "โ…”", # vulgar fraction two thirds "\\1/5" => "โ…•", # vulgar fraction one fifth "\\2/5" => "โ…–", # vulgar fraction two fifths "\\3/5" => "โ…—", # vulgar fraction three fifths "\\4/5" => "โ…˜", # vulgar fraction four fifths "\\1/6" => "โ…™", # vulgar fraction one sixth "\\5/6" => "โ…š", # vulgar fraction five sixths "\\1/8" => "โ…›", # vulgar fraction one eigth "\\3/8" => "โ…œ", # vulgar fraction three eigths "\\5/8" => "โ…", # vulgar fraction five eigths "\\7/8" => "โ…ž", # vulgar fraction seventh eigths "\\1/" => "โ…Ÿ", # fraction numerator one "\\0/3" => "โ†‰", # vulgar fraction zero thirds "\\1/4" => "ยผ", # vulgar fraction one quarter ) # Canonical reverse mapping for symbols that have several completions (#39148). # # These duplicate mappings can be investigated with the following commands: #= ls = REPL.REPLCompletions.latex_symbols; symbols = values(ls) duplicates = [v for v in unique(symbols) if count(==(v), symbols) > 1] [(v, REPL.symbol_latex(v)) => findall(==(v), ls) for v in duplicates] =# const symbols_latex_canonical = Dict( "โซซ" => "\\Bot", "รฐ" => "\\dh", "โ€ฆ" => "\\ldots", "โˆ…" => "\\emptyset", "โ„ฏ" => "\\euler", "โ™€" => "\\female", "โ‰ฅ" => "\\ge", "โŸบ" => "\\iff", "โ„‘" => "\\Im", "โŸธ" => "\\impliedby", "โŸน" => "\\implies", "โ‰ค" => "\\le", "โŸฆ" => "\\llbracket", "โ™‚" => "\\male", "โˆ‡" => "\\del", "โ„Ž" => "\\planck", "โ„œ" => "\\Re", "โŸง" => "\\rrbracket", "โˆš" => "\\sqrt", "ฬถ" => "\\sout", "โ†’" => "\\to", "โซช" => "\\Top", "ฮต" => "\\varepsilon", "โŠป" => "\\xor", "โŠผ" => "\\nand", "โŠฝ" => "\\nor", "โ‰ " => "\\ne", ) u���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/emoji_symbols.jlว—������# This file is a part of Julia. License is MIT: https://julialang.org/license #== using Pkg: @pkg_str pkg"activate --temp" pkg"add JSON@0.21" import JSON function emoji_data(url) emojis = JSON.parsefile(download(url)) result = Dict() for emj in emojis name = "\\:" * emj["short_name"] * ":" unicode = emj["unified"] if '-' in unicode continue end result[name] = "$(Char(parse(UInt32, unicode, base = 16)))" end return result end # We combine multiple versions as the data changes, and not only by growing. result = mapfoldr(emoji_data, merge, [ # Newer versions must be added to the bottom list as we want the newer versions to # overwrite the old with names that changed but still keep old ones that were removed "https://raw.githubusercontent.com/iamcal/emoji-data/0f0cf4ea8845eb52d26df2a48c3c31c3b8cad14e/emoji_pretty.json", "https://raw.githubusercontent.com/iamcal/emoji-data/e512953312c012f6bd00e3f2ef6bf152ca3710f8/emoji_pretty.json", ]; init=Dict() ) skeys = sort(collect(keys(result))) open("emoji_symbols.jl", "w") do fh println(fh, "const emoji_symbols = Dict(") for key in skeys println(fh, " \"", escape_string(key), "\" => \"", escape_string(result[key]), "\",") end println(fh, ")") end =# const emoji_symbols = Dict( "\\:+1:" => "๐Ÿ‘", "\\:-1:" => "๐Ÿ‘Ž", "\\:100:" => "๐Ÿ’ฏ", "\\:1234:" => "๐Ÿ”ข", "\\:8ball:" => "๐ŸŽฑ", "\\:a:" => "๐Ÿ…ฐ", "\\:ab:" => "๐Ÿ†Ž", "\\:abacus:" => "๐Ÿงฎ", "\\:abc:" => "๐Ÿ”ค", "\\:abcd:" => "๐Ÿ”ก", "\\:accept:" => "๐Ÿ‰‘", "\\:accordion:" => "๐Ÿช—", "\\:adhesive_bandage:" => "๐Ÿฉน", "\\:adult:" => "๐Ÿง‘", "\\:aerial_tramway:" => "๐Ÿšก", "\\:airplane:" => "โœˆ", "\\:airplane_arriving:" => "๐Ÿ›ฌ", "\\:airplane_departure:" => "๐Ÿ›ซ", "\\:alarm_clock:" => "โฐ", "\\:alien:" => "๐Ÿ‘ฝ", "\\:ambulance:" => "๐Ÿš‘", "\\:amphora:" => "๐Ÿบ", "\\:anatomical_heart:" => "๐Ÿซ€", "\\:anchor:" => "โš“", "\\:angel:" => "๐Ÿ‘ผ", "\\:anger:" => "๐Ÿ’ข", "\\:angry:" => "๐Ÿ˜ ", "\\:anguished:" => "๐Ÿ˜ง", "\\:ant:" => "๐Ÿœ", "\\:apple:" => "๐ŸŽ", "\\:aquarius:" => "โ™’", "\\:aries:" => "โ™ˆ", "\\:arrow_backward:" => "โ—€", "\\:arrow_double_down:" => "โฌ", "\\:arrow_double_up:" => "โซ", "\\:arrow_down:" => "โฌ‡", "\\:arrow_down_small:" => "๐Ÿ”ฝ", "\\:arrow_forward:" => "โ–ถ", "\\:arrow_heading_down:" => "โคต", "\\:arrow_heading_up:" => "โคด", "\\:arrow_left:" => "โฌ…", "\\:arrow_lower_left:" => "โ†™", "\\:arrow_lower_right:" => "โ†˜", "\\:arrow_right:" => "โžก", "\\:arrow_right_hook:" => "โ†ช", "\\:arrow_up:" => "โฌ†", "\\:arrow_up_down:" => "โ†•", "\\:arrow_up_small:" => "๐Ÿ”ผ", "\\:arrow_upper_left:" => "โ†–", "\\:arrow_upper_right:" => "โ†—", "\\:arrows_clockwise:" => "๐Ÿ”ƒ", "\\:arrows_counterclockwise:" => "๐Ÿ”„", "\\:art:" => "๐ŸŽจ", "\\:articulated_lorry:" => "๐Ÿš›", "\\:astonished:" => "๐Ÿ˜ฒ", "\\:athletic_shoe:" => "๐Ÿ‘Ÿ", "\\:atm:" => "๐Ÿง", "\\:auto_rickshaw:" => "๐Ÿ›บ", "\\:avocado:" => "๐Ÿฅ‘", "\\:axe:" => "๐Ÿช“", "\\:b:" => "๐Ÿ…ฑ", "\\:baby:" => "๐Ÿ‘ถ", "\\:baby_bottle:" => "๐Ÿผ", "\\:baby_chick:" => "๐Ÿค", "\\:baby_symbol:" => "๐Ÿšผ", "\\:back:" => "๐Ÿ”™", "\\:bacon:" => "๐Ÿฅ“", "\\:badger:" => "๐Ÿฆก", "\\:badminton_racquet_and_shuttlecock:" => "๐Ÿธ", "\\:bagel:" => "๐Ÿฅฏ", "\\:baggage_claim:" => "๐Ÿ›„", "\\:baguette_bread:" => "๐Ÿฅ–", "\\:ballet_shoes:" => "๐Ÿฉฐ", "\\:balloon:" => "๐ŸŽˆ", "\\:ballot_box_with_check:" => "โ˜‘", "\\:bamboo:" => "๐ŸŽ", "\\:banana:" => "๐ŸŒ", "\\:bangbang:" => "โ€ผ", "\\:banjo:" => "๐Ÿช•", "\\:bank:" => "๐Ÿฆ", "\\:bar_chart:" => "๐Ÿ“Š", "\\:barber:" => "๐Ÿ’ˆ", "\\:baseball:" => "โšพ", "\\:basket:" => "๐Ÿงบ", "\\:basketball:" => "๐Ÿ€", "\\:bat:" => "๐Ÿฆ‡", "\\:bath:" => "๐Ÿ›€", "\\:bathtub:" => "๐Ÿ›", "\\:battery:" => "๐Ÿ”‹", "\\:bear:" => "๐Ÿป", "\\:bearded_person:" => "๐Ÿง”", "\\:beaver:" => "๐Ÿฆซ", "\\:bee:" => "๐Ÿ", "\\:beer:" => "๐Ÿบ", "\\:beers:" => "๐Ÿป", "\\:beetle:" => "๐Ÿชฒ", "\\:beginner:" => "๐Ÿ”ฐ", "\\:bell:" => "๐Ÿ””", "\\:bell_pepper:" => "๐Ÿซ‘", "\\:bento:" => "๐Ÿฑ", "\\:beverage_box:" => "๐Ÿงƒ", "\\:bicyclist:" => "๐Ÿšด", "\\:bike:" => "๐Ÿšฒ", "\\:bikini:" => "๐Ÿ‘™", "\\:billed_cap:" => "๐Ÿงข", "\\:bird:" => "๐Ÿฆ", "\\:birthday:" => "๐ŸŽ‚", "\\:bison:" => "๐Ÿฆฌ", "\\:black_circle:" => "โšซ", "\\:black_heart:" => "๐Ÿ–ค", "\\:black_joker:" => "๐Ÿƒ", "\\:black_large_square:" => "โฌ›", "\\:black_medium_small_square:" => "โ—พ", "\\:black_medium_square:" => "โ—ผ", "\\:black_nib:" => "โœ’", "\\:black_small_square:" => "โ–ช", "\\:black_square_button:" => "๐Ÿ”ฒ", "\\:blossom:" => "๐ŸŒผ", "\\:blowfish:" => "๐Ÿก", "\\:blue_book:" => "๐Ÿ“˜", "\\:blue_car:" => "๐Ÿš™", "\\:blue_heart:" => "๐Ÿ’™", "\\:blueberries:" => "๐Ÿซ", "\\:blush:" => "๐Ÿ˜Š", "\\:boar:" => "๐Ÿ—", "\\:boat:" => "โ›ต", "\\:bomb:" => "๐Ÿ’ฃ", "\\:bone:" => "๐Ÿฆด", "\\:book:" => "๐Ÿ“–", "\\:bookmark:" => "๐Ÿ”–", "\\:bookmark_tabs:" => "๐Ÿ“‘", "\\:books:" => "๐Ÿ“š", "\\:boom:" => "๐Ÿ’ฅ", "\\:boomerang:" => "๐Ÿชƒ", "\\:boot:" => "๐Ÿ‘ข", "\\:bouquet:" => "๐Ÿ’", "\\:bow:" => "๐Ÿ™‡", "\\:bow_and_arrow:" => "๐Ÿน", "\\:bowl_with_spoon:" => "๐Ÿฅฃ", "\\:bowling:" => "๐ŸŽณ", "\\:boxing_glove:" => "๐ŸฅŠ", "\\:boy:" => "๐Ÿ‘ฆ", "\\:brain:" => "๐Ÿง ", "\\:bread:" => "๐Ÿž", "\\:breast-feeding:" => "๐Ÿคฑ", "\\:bricks:" => "๐Ÿงฑ", "\\:bride_with_veil:" => "๐Ÿ‘ฐ", "\\:bridge_at_night:" => "๐ŸŒ‰", "\\:briefcase:" => "๐Ÿ’ผ", "\\:briefs:" => "๐Ÿฉฒ", "\\:broccoli:" => "๐Ÿฅฆ", "\\:broken_heart:" => "๐Ÿ’”", "\\:broom:" => "๐Ÿงน", "\\:brown_heart:" => "๐ŸคŽ", "\\:bubble_tea:" => "๐Ÿง‹", "\\:bucket:" => "๐Ÿชฃ", "\\:bug:" => "๐Ÿ›", "\\:bulb:" => "๐Ÿ’ก", "\\:bullettrain_front:" => "๐Ÿš…", "\\:bullettrain_side:" => "๐Ÿš„", "\\:burrito:" => "๐ŸŒฏ", "\\:bus:" => "๐ŸšŒ", "\\:busstop:" => "๐Ÿš", "\\:bust_in_silhouette:" => "๐Ÿ‘ค", "\\:busts_in_silhouette:" => "๐Ÿ‘ฅ", "\\:butter:" => "๐Ÿงˆ", "\\:butterfly:" => "๐Ÿฆ‹", "\\:cactus:" => "๐ŸŒต", "\\:cake:" => "๐Ÿฐ", "\\:calendar:" => "๐Ÿ“†", "\\:call_me_hand:" => "๐Ÿค™", "\\:calling:" => "๐Ÿ“ฒ", "\\:camel:" => "๐Ÿซ", "\\:camera:" => "๐Ÿ“ท", "\\:camera_with_flash:" => "๐Ÿ“ธ", "\\:cancer:" => "โ™‹", "\\:candy:" => "๐Ÿฌ", "\\:canned_food:" => "๐Ÿฅซ", "\\:canoe:" => "๐Ÿ›ถ", "\\:capital_abcd:" => "๐Ÿ” ", "\\:capricorn:" => "โ™‘", "\\:car:" => "๐Ÿš—", "\\:card_index:" => "๐Ÿ“‡", "\\:carousel_horse:" => "๐ŸŽ ", "\\:carpentry_saw:" => "๐Ÿชš", "\\:carrot:" => "๐Ÿฅ•", "\\:cat2:" => "๐Ÿˆ", "\\:cat:" => "๐Ÿฑ", "\\:cd:" => "๐Ÿ’ฟ", "\\:chair:" => "๐Ÿช‘", "\\:champagne:" => "๐Ÿพ", "\\:chart:" => "๐Ÿ’น", "\\:chart_with_downwards_trend:" => "๐Ÿ“‰", "\\:chart_with_upwards_trend:" => "๐Ÿ“ˆ", "\\:checkered_flag:" => "๐Ÿ", "\\:cheese_wedge:" => "๐Ÿง€", "\\:cherries:" => "๐Ÿ’", "\\:cherry_blossom:" => "๐ŸŒธ", "\\:chestnut:" => "๐ŸŒฐ", "\\:chicken:" => "๐Ÿ”", "\\:child:" => "๐Ÿง’", "\\:children_crossing:" => "๐Ÿšธ", "\\:chocolate_bar:" => "๐Ÿซ", "\\:chopsticks:" => "๐Ÿฅข", "\\:christmas_tree:" => "๐ŸŽ„", "\\:church:" => "โ›ช", "\\:cinema:" => "๐ŸŽฆ", "\\:circus_tent:" => "๐ŸŽช", "\\:city_sunrise:" => "๐ŸŒ‡", "\\:city_sunset:" => "๐ŸŒ†", "\\:cl:" => "๐Ÿ†‘", "\\:clap:" => "๐Ÿ‘", "\\:clapper:" => "๐ŸŽฌ", "\\:clinking_glasses:" => "๐Ÿฅ‚", "\\:clipboard:" => "๐Ÿ“‹", "\\:clock1030:" => "๐Ÿ•ฅ", "\\:clock10:" => "๐Ÿ•™", "\\:clock1130:" => "๐Ÿ•ฆ", "\\:clock11:" => "๐Ÿ•š", "\\:clock1230:" => "๐Ÿ•ง", "\\:clock12:" => "๐Ÿ•›", "\\:clock130:" => "๐Ÿ•œ", "\\:clock1:" => "๐Ÿ•", "\\:clock230:" => "๐Ÿ•", "\\:clock2:" => "๐Ÿ•‘", "\\:clock330:" => "๐Ÿ•ž", "\\:clock3:" => "๐Ÿ•’", "\\:clock430:" => "๐Ÿ•Ÿ", "\\:clock4:" => "๐Ÿ•“", "\\:clock530:" => "๐Ÿ• ", "\\:clock5:" => "๐Ÿ•”", "\\:clock630:" => "๐Ÿ•ก", "\\:clock6:" => "๐Ÿ••", "\\:clock730:" => "๐Ÿ•ข", "\\:clock7:" => "๐Ÿ•–", "\\:clock830:" => "๐Ÿ•ฃ", "\\:clock8:" => "๐Ÿ•—", "\\:clock930:" => "๐Ÿ•ค", "\\:clock9:" => "๐Ÿ•˜", "\\:closed_book:" => "๐Ÿ“•", "\\:closed_lock_with_key:" => "๐Ÿ”", "\\:closed_umbrella:" => "๐ŸŒ‚", "\\:cloud:" => "โ˜", "\\:clown_face:" => "๐Ÿคก", "\\:clubs:" => "โ™ฃ", "\\:coat:" => "๐Ÿงฅ", "\\:cockroach:" => "๐Ÿชณ", "\\:cocktail:" => "๐Ÿธ", "\\:coconut:" => "๐Ÿฅฅ", "\\:coffee:" => "โ˜•", "\\:coin:" => "๐Ÿช™", "\\:cold_face:" => "๐Ÿฅถ", "\\:cold_sweat:" => "๐Ÿ˜ฐ", "\\:compass:" => "๐Ÿงญ", "\\:computer:" => "๐Ÿ’ป", "\\:confetti_ball:" => "๐ŸŽŠ", "\\:confounded:" => "๐Ÿ˜–", "\\:confused:" => "๐Ÿ˜•", "\\:congratulations:" => "ใŠ—", "\\:construction:" => "๐Ÿšง", "\\:construction_worker:" => "๐Ÿ‘ท", "\\:convenience_store:" => "๐Ÿช", "\\:cookie:" => "๐Ÿช", "\\:cool:" => "๐Ÿ†’", "\\:cop:" => "๐Ÿ‘ฎ", "\\:copyright:" => "ยฉ", "\\:corn:" => "๐ŸŒฝ", "\\:couple:" => "๐Ÿ‘ซ", "\\:couple_with_heart:" => "๐Ÿ’‘", "\\:couplekiss:" => "๐Ÿ’", "\\:cow2:" => "๐Ÿ„", "\\:cow:" => "๐Ÿฎ", "\\:crab:" => "๐Ÿฆ€", "\\:credit_card:" => "๐Ÿ’ณ", "\\:crescent_moon:" => "๐ŸŒ™", "\\:cricket:" => "๐Ÿฆ—", "\\:cricket_bat_and_ball:" => "๐Ÿ", "\\:crocodile:" => "๐ŸŠ", "\\:croissant:" => "๐Ÿฅ", "\\:crossed_fingers:" => "๐Ÿคž", "\\:crossed_flags:" => "๐ŸŽŒ", "\\:crown:" => "๐Ÿ‘‘", "\\:cry:" => "๐Ÿ˜ข", "\\:crying_cat_face:" => "๐Ÿ˜ฟ", "\\:crystal_ball:" => "๐Ÿ”ฎ", "\\:cucumber:" => "๐Ÿฅ’", "\\:cup_with_straw:" => "๐Ÿฅค", "\\:cupcake:" => "๐Ÿง", "\\:cupid:" => "๐Ÿ’˜", "\\:curling_stone:" => "๐ŸฅŒ", "\\:curly_loop:" => "โžฐ", "\\:currency_exchange:" => "๐Ÿ’ฑ", "\\:curry:" => "๐Ÿ›", "\\:custard:" => "๐Ÿฎ", "\\:customs:" => "๐Ÿ›ƒ", "\\:cut_of_meat:" => "๐Ÿฅฉ", "\\:cyclone:" => "๐ŸŒ€", "\\:dancer:" => "๐Ÿ’ƒ", "\\:dancers:" => "๐Ÿ‘ฏ", "\\:dango:" => "๐Ÿก", "\\:dart:" => "๐ŸŽฏ", "\\:dash:" => "๐Ÿ’จ", "\\:date:" => "๐Ÿ“…", "\\:deaf_person:" => "๐Ÿง", "\\:deciduous_tree:" => "๐ŸŒณ", "\\:deer:" => "๐ŸฆŒ", "\\:department_store:" => "๐Ÿฌ", "\\:diamond_shape_with_a_dot_inside:" => "๐Ÿ’ ", "\\:diamonds:" => "โ™ฆ", "\\:disappointed:" => "๐Ÿ˜ž", "\\:disappointed_relieved:" => "๐Ÿ˜ฅ", "\\:disguised_face:" => "๐Ÿฅธ", "\\:diving_mask:" => "๐Ÿคฟ", "\\:diya_lamp:" => "๐Ÿช”", "\\:dizzy:" => "๐Ÿ’ซ", "\\:dizzy_face:" => "๐Ÿ˜ต", "\\:dna:" => "๐Ÿงฌ", "\\:do_not_litter:" => "๐Ÿšฏ", "\\:dodo:" => "๐Ÿฆค", "\\:dog2:" => "๐Ÿ•", "\\:dog:" => "๐Ÿถ", "\\:dollar:" => "๐Ÿ’ต", "\\:dolls:" => "๐ŸŽŽ", "\\:dolphin:" => "๐Ÿฌ", "\\:door:" => "๐Ÿšช", "\\:doughnut:" => "๐Ÿฉ", "\\:dragon:" => "๐Ÿ‰", "\\:dragon_face:" => "๐Ÿฒ", "\\:dress:" => "๐Ÿ‘—", "\\:dromedary_camel:" => "๐Ÿช", "\\:drooling_face:" => "๐Ÿคค", "\\:drop_of_blood:" => "๐Ÿฉธ", "\\:droplet:" => "๐Ÿ’ง", "\\:drum_with_drumsticks:" => "๐Ÿฅ", "\\:duck:" => "๐Ÿฆ†", "\\:dumpling:" => "๐ŸฅŸ", "\\:dvd:" => "๐Ÿ“€", "\\:e-mail:" => "๐Ÿ“ง", "\\:eagle:" => "๐Ÿฆ…", "\\:ear:" => "๐Ÿ‘‚", "\\:ear_of_rice:" => "๐ŸŒพ", "\\:ear_with_hearing_aid:" => "๐Ÿฆป", "\\:earth_africa:" => "๐ŸŒ", "\\:earth_americas:" => "๐ŸŒŽ", "\\:earth_asia:" => "๐ŸŒ", "\\:egg:" => "๐Ÿฅš", "\\:eggplant:" => "๐Ÿ†", "\\:eight_pointed_black_star:" => "โœด", "\\:eight_spoked_asterisk:" => "โœณ", "\\:electric_plug:" => "๐Ÿ”Œ", "\\:elephant:" => "๐Ÿ˜", "\\:elevator:" => "๐Ÿ›—", "\\:elf:" => "๐Ÿง", "\\:email:" => "โœ‰", "\\:end:" => "๐Ÿ”š", "\\:envelope_with_arrow:" => "๐Ÿ“ฉ", "\\:euro:" => "๐Ÿ’ถ", "\\:european_castle:" => "๐Ÿฐ", "\\:european_post_office:" => "๐Ÿค", "\\:evergreen_tree:" => "๐ŸŒฒ", "\\:exclamation:" => "โ—", "\\:exploding_head:" => "๐Ÿคฏ", "\\:expressionless:" => "๐Ÿ˜‘", "\\:eyeglasses:" => "๐Ÿ‘“", "\\:eyes:" => "๐Ÿ‘€", "\\:face_palm:" => "๐Ÿคฆ", "\\:face_vomiting:" => "๐Ÿคฎ", "\\:face_with_cowboy_hat:" => "๐Ÿค ", "\\:face_with_hand_over_mouth:" => "๐Ÿคญ", "\\:face_with_head_bandage:" => "๐Ÿค•", "\\:face_with_monocle:" => "๐Ÿง", "\\:face_with_raised_eyebrow:" => "๐Ÿคจ", "\\:face_with_rolling_eyes:" => "๐Ÿ™„", "\\:face_with_symbols_on_mouth:" => "๐Ÿคฌ", "\\:face_with_thermometer:" => "๐Ÿค’", "\\:facepunch:" => "๐Ÿ‘Š", "\\:factory:" => "๐Ÿญ", "\\:fairy:" => "๐Ÿงš", "\\:falafel:" => "๐Ÿง†", "\\:fallen_leaf:" => "๐Ÿ‚", "\\:family:" => "๐Ÿ‘ช", "\\:fast_forward:" => "โฉ", "\\:fax:" => "๐Ÿ“ ", "\\:fearful:" => "๐Ÿ˜จ", "\\:feather:" => "๐Ÿชถ", "\\:feet:" => "๐Ÿพ", "\\:fencer:" => "๐Ÿคบ", "\\:ferris_wheel:" => "๐ŸŽก", "\\:field_hockey_stick_and_ball:" => "๐Ÿ‘", "\\:file_folder:" => "๐Ÿ“", "\\:fire:" => "๐Ÿ”ฅ", "\\:fire_engine:" => "๐Ÿš’", "\\:fire_extinguisher:" => "๐Ÿงฏ", "\\:firecracker:" => "๐Ÿงจ", "\\:fireworks:" => "๐ŸŽ†", "\\:first_place_medal:" => "๐Ÿฅ‡", "\\:first_quarter_moon:" => "๐ŸŒ“", "\\:first_quarter_moon_with_face:" => "๐ŸŒ›", "\\:fish:" => "๐ŸŸ", "\\:fish_cake:" => "๐Ÿฅ", "\\:fishing_pole_and_fish:" => "๐ŸŽฃ", "\\:fist:" => "โœŠ", "\\:flags:" => "๐ŸŽ", "\\:flamingo:" => "๐Ÿฆฉ", "\\:flashlight:" => "๐Ÿ”ฆ", "\\:flatbread:" => "๐Ÿซ“", "\\:floppy_disk:" => "๐Ÿ’พ", "\\:flower_playing_cards:" => "๐ŸŽด", "\\:flushed:" => "๐Ÿ˜ณ", "\\:fly:" => "๐Ÿชฐ", "\\:flying_disc:" => "๐Ÿฅ", "\\:flying_saucer:" => "๐Ÿ›ธ", "\\:foggy:" => "๐ŸŒ", "\\:fondue:" => "๐Ÿซ•", "\\:foot:" => "๐Ÿฆถ", "\\:football:" => "๐Ÿˆ", "\\:footprints:" => "๐Ÿ‘ฃ", "\\:fork_and_knife:" => "๐Ÿด", "\\:fortune_cookie:" => "๐Ÿฅ ", "\\:fountain:" => "โ›ฒ", "\\:four_leaf_clover:" => "๐Ÿ€", "\\:fox_face:" => "๐ŸฆŠ", "\\:free:" => "๐Ÿ†“", "\\:fried_egg:" => "๐Ÿณ", "\\:fried_shrimp:" => "๐Ÿค", "\\:fries:" => "๐ŸŸ", "\\:frog:" => "๐Ÿธ", "\\:frowning:" => "๐Ÿ˜ฆ", "\\:fuelpump:" => "โ›ฝ", "\\:full_moon:" => "๐ŸŒ•", "\\:full_moon_with_face:" => "๐ŸŒ", "\\:game_die:" => "๐ŸŽฒ", "\\:garlic:" => "๐Ÿง„", "\\:gem:" => "๐Ÿ’Ž", "\\:gemini:" => "โ™Š", "\\:genie:" => "๐Ÿงž", "\\:ghost:" => "๐Ÿ‘ป", "\\:gift:" => "๐ŸŽ", "\\:gift_heart:" => "๐Ÿ’", "\\:giraffe_face:" => "๐Ÿฆ’", "\\:girl:" => "๐Ÿ‘ง", "\\:glass_of_milk:" => "๐Ÿฅ›", "\\:globe_with_meridians:" => "๐ŸŒ", "\\:gloves:" => "๐Ÿงค", "\\:goal_net:" => "๐Ÿฅ…", "\\:goat:" => "๐Ÿ", "\\:goggles:" => "๐Ÿฅฝ", "\\:golf:" => "โ›ณ", "\\:gorilla:" => "๐Ÿฆ", "\\:grapes:" => "๐Ÿ‡", "\\:green_apple:" => "๐Ÿ", "\\:green_book:" => "๐Ÿ“—", "\\:green_heart:" => "๐Ÿ’š", "\\:green_salad:" => "๐Ÿฅ—", "\\:grey_exclamation:" => "โ•", "\\:grey_question:" => "โ”", "\\:grimacing:" => "๐Ÿ˜ฌ", "\\:grin:" => "๐Ÿ˜", "\\:grinning:" => "๐Ÿ˜€", "\\:guardsman:" => "๐Ÿ’‚", "\\:guide_dog:" => "๐Ÿฆฎ", "\\:guitar:" => "๐ŸŽธ", "\\:gun:" => "๐Ÿ”ซ", "\\:haircut:" => "๐Ÿ’‡", "\\:hamburger:" => "๐Ÿ”", "\\:hammer:" => "๐Ÿ”จ", "\\:hamster:" => "๐Ÿน", "\\:hand:" => "โœ‹", "\\:handbag:" => "๐Ÿ‘œ", "\\:handball:" => "๐Ÿคพ", "\\:handshake:" => "๐Ÿค", "\\:hankey:" => "๐Ÿ’ฉ", "\\:hatched_chick:" => "๐Ÿฅ", "\\:hatching_chick:" => "๐Ÿฃ", "\\:headphones:" => "๐ŸŽง", "\\:headstone:" => "๐Ÿชฆ", "\\:hear_no_evil:" => "๐Ÿ™‰", "\\:heart:" => "โค", "\\:heart_decoration:" => "๐Ÿ’Ÿ", "\\:heart_eyes:" => "๐Ÿ˜", "\\:heart_eyes_cat:" => "๐Ÿ˜ป", "\\:heartbeat:" => "๐Ÿ’“", "\\:heartpulse:" => "๐Ÿ’—", "\\:hearts:" => "โ™ฅ", "\\:heavy_check_mark:" => "โœ”", "\\:heavy_division_sign:" => "โž—", "\\:heavy_dollar_sign:" => "๐Ÿ’ฒ", "\\:heavy_minus_sign:" => "โž–", "\\:heavy_multiplication_x:" => "โœ–", "\\:heavy_plus_sign:" => "โž•", "\\:hedgehog:" => "๐Ÿฆ”", "\\:helicopter:" => "๐Ÿš", "\\:herb:" => "๐ŸŒฟ", "\\:hibiscus:" => "๐ŸŒบ", "\\:high_brightness:" => "๐Ÿ”†", "\\:high_heel:" => "๐Ÿ‘ ", "\\:hiking_boot:" => "๐Ÿฅพ", "\\:hindu_temple:" => "๐Ÿ›•", "\\:hippopotamus:" => "๐Ÿฆ›", "\\:hocho:" => "๐Ÿ”ช", "\\:honey_pot:" => "๐Ÿฏ", "\\:hook:" => "๐Ÿช", "\\:horse:" => "๐Ÿด", "\\:horse_racing:" => "๐Ÿ‡", "\\:hospital:" => "๐Ÿฅ", "\\:hot_face:" => "๐Ÿฅต", "\\:hotdog:" => "๐ŸŒญ", "\\:hotel:" => "๐Ÿจ", "\\:hotsprings:" => "โ™จ", "\\:hourglass:" => "โŒ›", "\\:hourglass_flowing_sand:" => "โณ", "\\:house:" => "๐Ÿ ", "\\:house_with_garden:" => "๐Ÿก", "\\:hugging_face:" => "๐Ÿค—", "\\:hushed:" => "๐Ÿ˜ฏ", "\\:hut:" => "๐Ÿ›–", "\\:i_love_you_hand_sign:" => "๐ŸคŸ", "\\:ice_cream:" => "๐Ÿจ", "\\:ice_cube:" => "๐ŸงŠ", "\\:ice_hockey_stick_and_puck:" => "๐Ÿ’", "\\:icecream:" => "๐Ÿฆ", "\\:id:" => "๐Ÿ†”", "\\:ideograph_advantage:" => "๐Ÿ‰", "\\:imp:" => "๐Ÿ‘ฟ", "\\:inbox_tray:" => "๐Ÿ“ฅ", "\\:incoming_envelope:" => "๐Ÿ“จ", "\\:information_desk_person:" => "๐Ÿ’", "\\:information_source:" => "โ„น", "\\:innocent:" => "๐Ÿ˜‡", "\\:interrobang:" => "โ‰", "\\:iphone:" => "๐Ÿ“ฑ", "\\:izakaya_lantern:" => "๐Ÿฎ", "\\:jack_o_lantern:" => "๐ŸŽƒ", "\\:japan:" => "๐Ÿ—พ", "\\:japanese_castle:" => "๐Ÿฏ", "\\:japanese_goblin:" => "๐Ÿ‘บ", "\\:japanese_ogre:" => "๐Ÿ‘น", "\\:jeans:" => "๐Ÿ‘–", "\\:jigsaw:" => "๐Ÿงฉ", "\\:joy:" => "๐Ÿ˜‚", "\\:joy_cat:" => "๐Ÿ˜น", "\\:juggling:" => "๐Ÿคน", "\\:kaaba:" => "๐Ÿ•‹", "\\:kangaroo:" => "๐Ÿฆ˜", "\\:key:" => "๐Ÿ”‘", "\\:keycap_ten:" => "๐Ÿ”Ÿ", "\\:kimono:" => "๐Ÿ‘˜", "\\:kiss:" => "๐Ÿ’‹", "\\:kissing:" => "๐Ÿ˜—", "\\:kissing_cat:" => "๐Ÿ˜ฝ", "\\:kissing_closed_eyes:" => "๐Ÿ˜š", "\\:kissing_heart:" => "๐Ÿ˜˜", "\\:kissing_smiling_eyes:" => "๐Ÿ˜™", "\\:kite:" => "๐Ÿช", "\\:kiwifruit:" => "๐Ÿฅ", "\\:kneeling_person:" => "๐ŸงŽ", "\\:knot:" => "๐Ÿชข", "\\:koala:" => "๐Ÿจ", "\\:koko:" => "๐Ÿˆ", "\\:lab_coat:" => "๐Ÿฅผ", "\\:lacrosse:" => "๐Ÿฅ", "\\:ladder:" => "๐Ÿชœ", "\\:ladybug:" => "๐Ÿž", "\\:large_blue_circle:" => "๐Ÿ”ต", "\\:large_blue_diamond:" => "๐Ÿ”ท", "\\:large_blue_square:" => "๐ŸŸฆ", "\\:large_brown_circle:" => "๐ŸŸค", "\\:large_brown_square:" => "๐ŸŸซ", "\\:large_green_circle:" => "๐ŸŸข", "\\:large_green_square:" => "๐ŸŸฉ", "\\:large_orange_circle:" => "๐ŸŸ ", "\\:large_orange_diamond:" => "๐Ÿ”ถ", "\\:large_orange_square:" => "๐ŸŸง", "\\:large_purple_circle:" => "๐ŸŸฃ", "\\:large_purple_square:" => "๐ŸŸช", "\\:large_red_square:" => "๐ŸŸฅ", "\\:large_yellow_circle:" => "๐ŸŸก", "\\:large_yellow_square:" => "๐ŸŸจ", "\\:last_quarter_moon:" => "๐ŸŒ—", "\\:last_quarter_moon_with_face:" => "๐ŸŒœ", "\\:laughing:" => "๐Ÿ˜†", "\\:leafy_green:" => "๐Ÿฅฌ", "\\:leaves:" => "๐Ÿƒ", "\\:ledger:" => "๐Ÿ“’", "\\:left-facing_fist:" => "๐Ÿค›", "\\:left_luggage:" => "๐Ÿ›…", "\\:left_right_arrow:" => "โ†”", "\\:leftwards_arrow_with_hook:" => "โ†ฉ", "\\:leg:" => "๐Ÿฆต", "\\:lemon:" => "๐Ÿ‹", "\\:leo:" => "โ™Œ", "\\:leopard:" => "๐Ÿ†", "\\:libra:" => "โ™Ž", "\\:light_rail:" => "๐Ÿšˆ", "\\:link:" => "๐Ÿ”—", "\\:lion_face:" => "๐Ÿฆ", "\\:lips:" => "๐Ÿ‘„", "\\:lipstick:" => "๐Ÿ’„", "\\:lizard:" => "๐ŸฆŽ", "\\:llama:" => "๐Ÿฆ™", "\\:lobster:" => "๐Ÿฆž", "\\:lock:" => "๐Ÿ”’", "\\:lock_with_ink_pen:" => "๐Ÿ”", "\\:lollipop:" => "๐Ÿญ", "\\:long_drum:" => "๐Ÿช˜", "\\:loop:" => "โžฟ", "\\:lotion_bottle:" => "๐Ÿงด", "\\:loud_sound:" => "๐Ÿ”Š", "\\:loudspeaker:" => "๐Ÿ“ข", "\\:love_hotel:" => "๐Ÿฉ", "\\:love_letter:" => "๐Ÿ’Œ", "\\:low_brightness:" => "๐Ÿ”…", "\\:luggage:" => "๐Ÿงณ", "\\:lungs:" => "๐Ÿซ", "\\:lying_face:" => "๐Ÿคฅ", "\\:m:" => "โ“‚", "\\:mag:" => "๐Ÿ”", "\\:mag_right:" => "๐Ÿ”Ž", "\\:mage:" => "๐Ÿง™", "\\:magic_wand:" => "๐Ÿช„", "\\:magnet:" => "๐Ÿงฒ", "\\:mahjong:" => "๐Ÿ€„", "\\:mailbox:" => "๐Ÿ“ซ", "\\:mailbox_closed:" => "๐Ÿ“ช", "\\:mailbox_with_mail:" => "๐Ÿ“ฌ", "\\:mailbox_with_no_mail:" => "๐Ÿ“ญ", "\\:mammoth:" => "๐Ÿฆฃ", "\\:man:" => "๐Ÿ‘จ", "\\:man_and_woman_holding_hands:" => "๐Ÿ‘ซ", "\\:man_dancing:" => "๐Ÿ•บ", "\\:man_with_gua_pi_mao:" => "๐Ÿ‘ฒ", "\\:man_with_turban:" => "๐Ÿ‘ณ", "\\:mango:" => "๐Ÿฅญ", "\\:mans_shoe:" => "๐Ÿ‘ž", "\\:manual_wheelchair:" => "๐Ÿฆฝ", "\\:maple_leaf:" => "๐Ÿ", "\\:martial_arts_uniform:" => "๐Ÿฅ‹", "\\:mask:" => "๐Ÿ˜ท", "\\:massage:" => "๐Ÿ’†", "\\:mate_drink:" => "๐Ÿง‰", "\\:meat_on_bone:" => "๐Ÿ–", "\\:mechanical_arm:" => "๐Ÿฆพ", "\\:mechanical_leg:" => "๐Ÿฆฟ", "\\:mega:" => "๐Ÿ“ฃ", "\\:melon:" => "๐Ÿˆ", "\\:memo:" => "๐Ÿ“", "\\:menorah_with_nine_branches:" => "๐Ÿ•Ž", "\\:mens:" => "๐Ÿšน", "\\:merperson:" => "๐Ÿงœ", "\\:metro:" => "๐Ÿš‡", "\\:microbe:" => "๐Ÿฆ ", "\\:microphone:" => "๐ŸŽค", "\\:microscope:" => "๐Ÿ”ฌ", "\\:middle_finger:" => "๐Ÿ–•", "\\:military_helmet:" => "๐Ÿช–", "\\:milky_way:" => "๐ŸŒŒ", "\\:minibus:" => "๐Ÿš", "\\:minidisc:" => "๐Ÿ’ฝ", "\\:mirror:" => "๐Ÿชž", "\\:mobile_phone_off:" => "๐Ÿ“ด", "\\:money_mouth_face:" => "๐Ÿค‘", "\\:money_with_wings:" => "๐Ÿ’ธ", "\\:moneybag:" => "๐Ÿ’ฐ", "\\:monkey:" => "๐Ÿ’", "\\:monkey_face:" => "๐Ÿต", "\\:monorail:" => "๐Ÿš", "\\:moon:" => "๐ŸŒ”", "\\:moon_cake:" => "๐Ÿฅฎ", "\\:mortar_board:" => "๐ŸŽ“", "\\:mosque:" => "๐Ÿ•Œ", "\\:mosquito:" => "๐ŸฆŸ", "\\:motor_scooter:" => "๐Ÿ›ต", "\\:motorized_wheelchair:" => "๐Ÿฆผ", "\\:mount_fuji:" => "๐Ÿ—ป", "\\:mountain_bicyclist:" => "๐Ÿšต", "\\:mountain_cableway:" => "๐Ÿš ", "\\:mountain_railway:" => "๐Ÿšž", "\\:mouse2:" => "๐Ÿ", "\\:mouse:" => "๐Ÿญ", "\\:mouse_trap:" => "๐Ÿชค", "\\:movie_camera:" => "๐ŸŽฅ", "\\:moyai:" => "๐Ÿ—ฟ", "\\:mrs_claus:" => "๐Ÿคถ", "\\:muscle:" => "๐Ÿ’ช", "\\:mushroom:" => "๐Ÿ„", "\\:musical_keyboard:" => "๐ŸŽน", "\\:musical_note:" => "๐ŸŽต", "\\:musical_score:" => "๐ŸŽผ", "\\:mute:" => "๐Ÿ”‡", "\\:nail_care:" => "๐Ÿ’…", "\\:name_badge:" => "๐Ÿ“›", "\\:nauseated_face:" => "๐Ÿคข", "\\:nazar_amulet:" => "๐Ÿงฟ", "\\:necktie:" => "๐Ÿ‘”", "\\:negative_squared_cross_mark:" => "โŽ", "\\:nerd_face:" => "๐Ÿค“", "\\:nesting_dolls:" => "๐Ÿช†", "\\:neutral_face:" => "๐Ÿ˜", "\\:new:" => "๐Ÿ†•", "\\:new_moon:" => "๐ŸŒ‘", "\\:new_moon_with_face:" => "๐ŸŒš", "\\:newspaper:" => "๐Ÿ“ฐ", "\\:ng:" => "๐Ÿ†–", "\\:night_with_stars:" => "๐ŸŒƒ", "\\:ninja:" => "๐Ÿฅท", "\\:no_bell:" => "๐Ÿ”•", "\\:no_bicycles:" => "๐Ÿšณ", "\\:no_entry:" => "โ›”", "\\:no_entry_sign:" => "๐Ÿšซ", "\\:no_good:" => "๐Ÿ™…", "\\:no_mobile_phones:" => "๐Ÿ“ต", "\\:no_mouth:" => "๐Ÿ˜ถ", "\\:no_pedestrians:" => "๐Ÿšท", "\\:no_smoking:" => "๐Ÿšญ", "\\:non-potable_water:" => "๐Ÿšฑ", "\\:nose:" => "๐Ÿ‘ƒ", "\\:notebook:" => "๐Ÿ““", "\\:notebook_with_decorative_cover:" => "๐Ÿ“”", "\\:notes:" => "๐ŸŽถ", "\\:nut_and_bolt:" => "๐Ÿ”ฉ", "\\:o2:" => "๐Ÿ…พ", "\\:o:" => "โญ•", "\\:ocean:" => "๐ŸŒŠ", "\\:octagonal_sign:" => "๐Ÿ›‘", "\\:octopus:" => "๐Ÿ™", "\\:oden:" => "๐Ÿข", "\\:office:" => "๐Ÿข", "\\:ok:" => "๐Ÿ†—", "\\:ok_hand:" => "๐Ÿ‘Œ", "\\:ok_woman:" => "๐Ÿ™†", "\\:older_adult:" => "๐Ÿง“", "\\:older_man:" => "๐Ÿ‘ด", "\\:older_woman:" => "๐Ÿ‘ต", "\\:olive:" => "๐Ÿซ’", "\\:on:" => "๐Ÿ”›", "\\:oncoming_automobile:" => "๐Ÿš˜", "\\:oncoming_bus:" => "๐Ÿš", "\\:oncoming_police_car:" => "๐Ÿš”", "\\:oncoming_taxi:" => "๐Ÿš–", "\\:one-piece_swimsuit:" => "๐Ÿฉฑ", "\\:onion:" => "๐Ÿง…", "\\:open_file_folder:" => "๐Ÿ“‚", "\\:open_hands:" => "๐Ÿ‘", "\\:open_mouth:" => "๐Ÿ˜ฎ", "\\:ophiuchus:" => "โ›Ž", "\\:orange_book:" => "๐Ÿ“™", "\\:orange_heart:" => "๐Ÿงก", "\\:orangutan:" => "๐Ÿฆง", "\\:otter:" => "๐Ÿฆฆ", "\\:outbox_tray:" => "๐Ÿ“ค", "\\:owl:" => "๐Ÿฆ‰", "\\:ox:" => "๐Ÿ‚", "\\:oyster:" => "๐Ÿฆช", "\\:package:" => "๐Ÿ“ฆ", "\\:page_facing_up:" => "๐Ÿ“„", "\\:page_with_curl:" => "๐Ÿ“ƒ", "\\:pager:" => "๐Ÿ“Ÿ", "\\:palm_tree:" => "๐ŸŒด", "\\:palms_up_together:" => "๐Ÿคฒ", "\\:pancakes:" => "๐Ÿฅž", "\\:panda_face:" => "๐Ÿผ", "\\:paperclip:" => "๐Ÿ“Ž", "\\:parachute:" => "๐Ÿช‚", "\\:parking:" => "๐Ÿ…ฟ", "\\:parrot:" => "๐Ÿฆœ", "\\:part_alternation_mark:" => "ใ€ฝ", "\\:partly_sunny:" => "โ›…", "\\:partying_face:" => "๐Ÿฅณ", "\\:passport_control:" => "๐Ÿ›‚", "\\:peach:" => "๐Ÿ‘", "\\:peacock:" => "๐Ÿฆš", "\\:peanuts:" => "๐Ÿฅœ", "\\:pear:" => "๐Ÿ", "\\:pencil2:" => "โœ", "\\:penguin:" => "๐Ÿง", "\\:pensive:" => "๐Ÿ˜”", "\\:people_hugging:" => "๐Ÿซ‚", "\\:performing_arts:" => "๐ŸŽญ", "\\:persevere:" => "๐Ÿ˜ฃ", "\\:person_climbing:" => "๐Ÿง—", "\\:person_doing_cartwheel:" => "๐Ÿคธ", "\\:person_frowning:" => "๐Ÿ™", "\\:person_in_lotus_position:" => "๐Ÿง˜", "\\:person_in_steamy_room:" => "๐Ÿง–", "\\:person_in_tuxedo:" => "๐Ÿคต", "\\:person_with_blond_hair:" => "๐Ÿ‘ฑ", "\\:person_with_headscarf:" => "๐Ÿง•", "\\:person_with_pouting_face:" => "๐Ÿ™Ž", "\\:petri_dish:" => "๐Ÿงซ", "\\:phone:" => "โ˜Ž", "\\:pickup_truck:" => "๐Ÿ›ป", "\\:pie:" => "๐Ÿฅง", "\\:pig2:" => "๐Ÿ–", "\\:pig:" => "๐Ÿท", "\\:pig_nose:" => "๐Ÿฝ", "\\:pill:" => "๐Ÿ’Š", "\\:pinata:" => "๐Ÿช…", "\\:pinched_fingers:" => "๐ŸคŒ", "\\:pinching_hand:" => "๐Ÿค", "\\:pineapple:" => "๐Ÿ", "\\:pisces:" => "โ™“", "\\:pizza:" => "๐Ÿ•", "\\:placard:" => "๐Ÿชง", "\\:place_of_worship:" => "๐Ÿ›", "\\:pleading_face:" => "๐Ÿฅบ", "\\:plunger:" => "๐Ÿช ", "\\:point_down:" => "๐Ÿ‘‡", "\\:point_left:" => "๐Ÿ‘ˆ", "\\:point_right:" => "๐Ÿ‘‰", "\\:point_up:" => "โ˜", "\\:point_up_2:" => "๐Ÿ‘†", "\\:police_car:" => "๐Ÿš“", "\\:poodle:" => "๐Ÿฉ", "\\:popcorn:" => "๐Ÿฟ", "\\:post_office:" => "๐Ÿฃ", "\\:postal_horn:" => "๐Ÿ“ฏ", "\\:postbox:" => "๐Ÿ“ฎ", "\\:potable_water:" => "๐Ÿšฐ", "\\:potato:" => "๐Ÿฅ”", "\\:potted_plant:" => "๐Ÿชด", "\\:pouch:" => "๐Ÿ‘", "\\:poultry_leg:" => "๐Ÿ—", "\\:pound:" => "๐Ÿ’ท", "\\:pouting_cat:" => "๐Ÿ˜พ", "\\:pray:" => "๐Ÿ™", "\\:prayer_beads:" => "๐Ÿ“ฟ", "\\:pregnant_woman:" => "๐Ÿคฐ", "\\:pretzel:" => "๐Ÿฅจ", "\\:prince:" => "๐Ÿคด", "\\:princess:" => "๐Ÿ‘ธ", "\\:probing_cane:" => "๐Ÿฆฏ", "\\:purple_heart:" => "๐Ÿ’œ", "\\:purse:" => "๐Ÿ‘›", "\\:pushpin:" => "๐Ÿ“Œ", "\\:put_litter_in_its_place:" => "๐Ÿšฎ", "\\:question:" => "โ“", "\\:rabbit2:" => "๐Ÿ‡", "\\:rabbit:" => "๐Ÿฐ", "\\:raccoon:" => "๐Ÿฆ", "\\:racehorse:" => "๐ŸŽ", "\\:radio:" => "๐Ÿ“ป", "\\:radio_button:" => "๐Ÿ”˜", "\\:rage:" => "๐Ÿ˜ก", "\\:railway_car:" => "๐Ÿšƒ", "\\:rainbow:" => "๐ŸŒˆ", "\\:raised_back_of_hand:" => "๐Ÿคš", "\\:raised_hands:" => "๐Ÿ™Œ", "\\:raising_hand:" => "๐Ÿ™‹", "\\:ram:" => "๐Ÿ", "\\:ramen:" => "๐Ÿœ", "\\:rat:" => "๐Ÿ€", "\\:razor:" => "๐Ÿช’", "\\:receipt:" => "๐Ÿงพ", "\\:recycle:" => "โ™ป", "\\:red_circle:" => "๐Ÿ”ด", "\\:red_envelope:" => "๐Ÿงง", "\\:registered:" => "ยฎ", "\\:relaxed:" => "โ˜บ", "\\:relieved:" => "๐Ÿ˜Œ", "\\:repeat:" => "๐Ÿ”", "\\:repeat_one:" => "๐Ÿ”‚", "\\:restroom:" => "๐Ÿšป", "\\:revolving_hearts:" => "๐Ÿ’ž", "\\:rewind:" => "โช", "\\:rhinoceros:" => "๐Ÿฆ", "\\:ribbon:" => "๐ŸŽ€", "\\:rice:" => "๐Ÿš", "\\:rice_ball:" => "๐Ÿ™", "\\:rice_cracker:" => "๐Ÿ˜", "\\:rice_scene:" => "๐ŸŽ‘", "\\:right-facing_fist:" => "๐Ÿคœ", "\\:ring:" => "๐Ÿ’", "\\:ringed_planet:" => "๐Ÿช", "\\:robot_face:" => "๐Ÿค–", "\\:rock:" => "๐Ÿชจ", "\\:rocket:" => "๐Ÿš€", "\\:roll_of_paper:" => "๐Ÿงป", "\\:roller_coaster:" => "๐ŸŽข", "\\:roller_skate:" => "๐Ÿ›ผ", "\\:rolling_on_the_floor_laughing:" => "๐Ÿคฃ", "\\:rooster:" => "๐Ÿ“", "\\:rose:" => "๐ŸŒน", "\\:rotating_light:" => "๐Ÿšจ", "\\:round_pushpin:" => "๐Ÿ“", "\\:rowboat:" => "๐Ÿšฃ", "\\:rugby_football:" => "๐Ÿ‰", "\\:runner:" => "๐Ÿƒ", "\\:running_shirt_with_sash:" => "๐ŸŽฝ", "\\:sa:" => "๐Ÿˆ‚", "\\:safety_pin:" => "๐Ÿงท", "\\:safety_vest:" => "๐Ÿฆบ", "\\:sagittarius:" => "โ™", "\\:sake:" => "๐Ÿถ", "\\:salt:" => "๐Ÿง‚", "\\:sandal:" => "๐Ÿ‘ก", "\\:sandwich:" => "๐Ÿฅช", "\\:santa:" => "๐ŸŽ…", "\\:sari:" => "๐Ÿฅป", "\\:satellite:" => "๐Ÿ“ก", "\\:satellite_antenna:" => "๐Ÿ“ก", "\\:sauropod:" => "๐Ÿฆ•", "\\:saxophone:" => "๐ŸŽท", "\\:scarf:" => "๐Ÿงฃ", "\\:school:" => "๐Ÿซ", "\\:school_satchel:" => "๐ŸŽ’", "\\:scissors:" => "โœ‚", "\\:scooter:" => "๐Ÿ›ด", "\\:scorpion:" => "๐Ÿฆ‚", "\\:scorpius:" => "โ™", "\\:scream:" => "๐Ÿ˜ฑ", "\\:scream_cat:" => "๐Ÿ™€", "\\:screwdriver:" => "๐Ÿช›", "\\:scroll:" => "๐Ÿ“œ", "\\:seal:" => "๐Ÿฆญ", "\\:seat:" => "๐Ÿ’บ", "\\:second_place_medal:" => "๐Ÿฅˆ", "\\:secret:" => "ใŠ™", "\\:see_no_evil:" => "๐Ÿ™ˆ", "\\:seedling:" => "๐ŸŒฑ", "\\:selfie:" => "๐Ÿคณ", "\\:sewing_needle:" => "๐Ÿชก", "\\:shallow_pan_of_food:" => "๐Ÿฅ˜", "\\:shark:" => "๐Ÿฆˆ", "\\:shaved_ice:" => "๐Ÿง", "\\:sheep:" => "๐Ÿ‘", "\\:shell:" => "๐Ÿš", "\\:ship:" => "๐Ÿšข", "\\:shirt:" => "๐Ÿ‘•", "\\:shopping_trolley:" => "๐Ÿ›’", "\\:shorts:" => "๐Ÿฉณ", "\\:shower:" => "๐Ÿšฟ", "\\:shrimp:" => "๐Ÿฆ", "\\:shrug:" => "๐Ÿคท", "\\:shushing_face:" => "๐Ÿคซ", "\\:signal_strength:" => "๐Ÿ“ถ", "\\:six_pointed_star:" => "๐Ÿ”ฏ", "\\:skateboard:" => "๐Ÿ›น", "\\:ski:" => "๐ŸŽฟ", "\\:skin-tone-2:" => "๐Ÿป", "\\:skin-tone-3:" => "๐Ÿผ", "\\:skin-tone-4:" => "๐Ÿฝ", "\\:skin-tone-5:" => "๐Ÿพ", "\\:skin-tone-6:" => "๐Ÿฟ", "\\:skull:" => "๐Ÿ’€", "\\:skunk:" => "๐Ÿฆจ", "\\:sled:" => "๐Ÿ›ท", "\\:sleeping:" => "๐Ÿ˜ด", "\\:sleeping_accommodation:" => "๐Ÿ›Œ", "\\:sleepy:" => "๐Ÿ˜ช", "\\:slightly_frowning_face:" => "๐Ÿ™", "\\:slightly_smiling_face:" => "๐Ÿ™‚", "\\:slot_machine:" => "๐ŸŽฐ", "\\:sloth:" => "๐Ÿฆฅ", "\\:small_blue_diamond:" => "๐Ÿ”น", "\\:small_orange_diamond:" => "๐Ÿ”ธ", "\\:small_red_triangle:" => "๐Ÿ”บ", "\\:small_red_triangle_down:" => "๐Ÿ”ป", "\\:smile:" => "๐Ÿ˜„", "\\:smile_cat:" => "๐Ÿ˜ธ", "\\:smiley:" => "๐Ÿ˜ƒ", "\\:smiley_cat:" => "๐Ÿ˜บ", "\\:smiling_face_with_3_hearts:" => "๐Ÿฅฐ", "\\:smiling_face_with_tear:" => "๐Ÿฅฒ", "\\:smiling_imp:" => "๐Ÿ˜ˆ", "\\:smirk:" => "๐Ÿ˜", "\\:smirk_cat:" => "๐Ÿ˜ผ", "\\:smoking:" => "๐Ÿšฌ", "\\:snail:" => "๐ŸŒ", "\\:snake:" => "๐Ÿ", "\\:sneezing_face:" => "๐Ÿคง", "\\:snowboarder:" => "๐Ÿ‚", "\\:snowflake:" => "โ„", "\\:snowman:" => "โ›„", "\\:snowman_without_snow:" => "โ›„", "\\:soap:" => "๐Ÿงผ", "\\:sob:" => "๐Ÿ˜ญ", "\\:soccer:" => "โšฝ", "\\:socks:" => "๐Ÿงฆ", "\\:softball:" => "๐ŸฅŽ", "\\:soon:" => "๐Ÿ”œ", "\\:sos:" => "๐Ÿ†˜", "\\:sound:" => "๐Ÿ”‰", "\\:space_invader:" => "๐Ÿ‘พ", "\\:spades:" => "โ™ ", "\\:spaghetti:" => "๐Ÿ", "\\:sparkle:" => "โ‡", "\\:sparkler:" => "๐ŸŽ‡", "\\:sparkles:" => "โœจ", "\\:sparkling_heart:" => "๐Ÿ’–", "\\:speak_no_evil:" => "๐Ÿ™Š", "\\:speaker:" => "๐Ÿ”ˆ", "\\:speech_balloon:" => "๐Ÿ’ฌ", "\\:speedboat:" => "๐Ÿšค", "\\:spock-hand:" => "๐Ÿ––", "\\:sponge:" => "๐Ÿงฝ", "\\:spoon:" => "๐Ÿฅ„", "\\:sports_medal:" => "๐Ÿ…", "\\:squid:" => "๐Ÿฆ‘", "\\:standing_person:" => "๐Ÿง", "\\:star-struck:" => "๐Ÿคฉ", "\\:star2:" => "๐ŸŒŸ", "\\:star:" => "โญ", "\\:stars:" => "๐ŸŒ ", "\\:station:" => "๐Ÿš‰", "\\:statue_of_liberty:" => "๐Ÿ—ฝ", "\\:steam_locomotive:" => "๐Ÿš‚", "\\:stethoscope:" => "๐Ÿฉบ", "\\:stew:" => "๐Ÿฒ", "\\:straight_ruler:" => "๐Ÿ“", "\\:strawberry:" => "๐Ÿ“", "\\:stuck_out_tongue:" => "๐Ÿ˜›", "\\:stuck_out_tongue_closed_eyes:" => "๐Ÿ˜", "\\:stuck_out_tongue_winking_eye:" => "๐Ÿ˜œ", "\\:stuffed_flatbread:" => "๐Ÿฅ™", "\\:sun_with_face:" => "๐ŸŒž", "\\:sunflower:" => "๐ŸŒป", "\\:sunglasses:" => "๐Ÿ˜Ž", "\\:sunny:" => "โ˜€", "\\:sunrise:" => "๐ŸŒ…", "\\:sunrise_over_mountains:" => "๐ŸŒ„", "\\:superhero:" => "๐Ÿฆธ", "\\:supervillain:" => "๐Ÿฆน", "\\:surfer:" => "๐Ÿ„", "\\:sushi:" => "๐Ÿฃ", "\\:suspension_railway:" => "๐ŸšŸ", "\\:swan:" => "๐Ÿฆข", "\\:sweat:" => "๐Ÿ˜“", "\\:sweat_drops:" => "๐Ÿ’ฆ", "\\:sweat_smile:" => "๐Ÿ˜…", "\\:sweet_potato:" => "๐Ÿ ", "\\:swimmer:" => "๐ŸŠ", "\\:symbols:" => "๐Ÿ”ฃ", "\\:synagogue:" => "๐Ÿ•", "\\:syringe:" => "๐Ÿ’‰", "\\:t-rex:" => "๐Ÿฆ–", "\\:table_tennis_paddle_and_ball:" => "๐Ÿ“", "\\:taco:" => "๐ŸŒฎ", "\\:tada:" => "๐ŸŽ‰", "\\:takeout_box:" => "๐Ÿฅก", "\\:tamale:" => "๐Ÿซ”", "\\:tanabata_tree:" => "๐ŸŽ‹", "\\:tangerine:" => "๐ŸŠ", "\\:taurus:" => "โ™‰", "\\:taxi:" => "๐Ÿš•", "\\:tea:" => "๐Ÿต", "\\:teapot:" => "๐Ÿซ–", "\\:teddy_bear:" => "๐Ÿงธ", "\\:telephone_receiver:" => "๐Ÿ“ž", "\\:telescope:" => "๐Ÿ”ญ", "\\:tennis:" => "๐ŸŽพ", "\\:tent:" => "โ›บ", "\\:test_tube:" => "๐Ÿงช", "\\:the_horns:" => "๐Ÿค˜", "\\:thinking_face:" => "๐Ÿค”", "\\:third_place_medal:" => "๐Ÿฅ‰", "\\:thong_sandal:" => "๐Ÿฉด", "\\:thought_balloon:" => "๐Ÿ’ญ", "\\:thread:" => "๐Ÿงต", "\\:ticket:" => "๐ŸŽซ", "\\:tiger2:" => "๐Ÿ…", "\\:tiger:" => "๐Ÿฏ", "\\:tired_face:" => "๐Ÿ˜ซ", "\\:tm:" => "โ„ข", "\\:toilet:" => "๐Ÿšฝ", "\\:tokyo_tower:" => "๐Ÿ—ผ", "\\:tomato:" => "๐Ÿ…", "\\:tongue:" => "๐Ÿ‘…", "\\:toolbox:" => "๐Ÿงฐ", "\\:tooth:" => "๐Ÿฆท", "\\:toothbrush:" => "๐Ÿชฅ", "\\:top:" => "๐Ÿ”", "\\:tophat:" => "๐ŸŽฉ", "\\:tractor:" => "๐Ÿšœ", "\\:traffic_light:" => "๐Ÿšฅ", "\\:train2:" => "๐Ÿš†", "\\:train:" => "๐Ÿš‹", "\\:tram:" => "๐ŸšŠ", "\\:triangular_flag_on_post:" => "๐Ÿšฉ", "\\:triangular_ruler:" => "๐Ÿ“", "\\:trident:" => "๐Ÿ”ฑ", "\\:triumph:" => "๐Ÿ˜ค", "\\:trolleybus:" => "๐ŸšŽ", "\\:trophy:" => "๐Ÿ†", "\\:tropical_drink:" => "๐Ÿน", "\\:tropical_fish:" => "๐Ÿ ", "\\:truck:" => "๐Ÿšš", "\\:trumpet:" => "๐ŸŽบ", "\\:tulip:" => "๐ŸŒท", "\\:tumbler_glass:" => "๐Ÿฅƒ", "\\:turkey:" => "๐Ÿฆƒ", "\\:turtle:" => "๐Ÿข", "\\:tv:" => "๐Ÿ“บ", "\\:twisted_rightwards_arrows:" => "๐Ÿ”€", "\\:two_hearts:" => "๐Ÿ’•", "\\:two_men_holding_hands:" => "๐Ÿ‘ฌ", "\\:two_women_holding_hands:" => "๐Ÿ‘ญ", "\\:u5272:" => "๐Ÿˆน", "\\:u5408:" => "๐Ÿˆด", "\\:u55b6:" => "๐Ÿˆบ", "\\:u6307:" => "๐Ÿˆฏ", "\\:u6708:" => "๐Ÿˆท", "\\:u6709:" => "๐Ÿˆถ", "\\:u6e80:" => "๐Ÿˆต", "\\:u7121:" => "๐Ÿˆš", "\\:u7533:" => "๐Ÿˆธ", "\\:u7981:" => "๐Ÿˆฒ", "\\:u7a7a:" => "๐Ÿˆณ", "\\:umbrella:" => "โ˜”", "\\:umbrella_with_rain_drops:" => "โ˜”", "\\:unamused:" => "๐Ÿ˜’", "\\:underage:" => "๐Ÿ”ž", "\\:unicorn_face:" => "๐Ÿฆ„", "\\:unlock:" => "๐Ÿ”“", "\\:up:" => "๐Ÿ†™", "\\:upside_down_face:" => "๐Ÿ™ƒ", "\\:v:" => "โœŒ", "\\:vampire:" => "๐Ÿง›", "\\:vertical_traffic_light:" => "๐Ÿšฆ", "\\:vhs:" => "๐Ÿ“ผ", "\\:vibration_mode:" => "๐Ÿ“ณ", "\\:video_camera:" => "๐Ÿ“น", "\\:video_game:" => "๐ŸŽฎ", "\\:violin:" => "๐ŸŽป", "\\:virgo:" => "โ™", "\\:volcano:" => "๐ŸŒ‹", "\\:volleyball:" => "๐Ÿ", "\\:vs:" => "๐Ÿ†š", "\\:waffle:" => "๐Ÿง‡", "\\:walking:" => "๐Ÿšถ", "\\:waning_crescent_moon:" => "๐ŸŒ˜", "\\:waning_gibbous_moon:" => "๐ŸŒ–", "\\:warning:" => "โš ", "\\:watch:" => "โŒš", "\\:water_buffalo:" => "๐Ÿƒ", "\\:water_polo:" => "๐Ÿคฝ", "\\:watermelon:" => "๐Ÿ‰", "\\:wave:" => "๐Ÿ‘‹", "\\:waving_black_flag:" => "๐Ÿด", "\\:wavy_dash:" => "ใ€ฐ", "\\:waxing_crescent_moon:" => "๐ŸŒ’", "\\:wc:" => "๐Ÿšพ", "\\:weary:" => "๐Ÿ˜ฉ", "\\:wedding:" => "๐Ÿ’’", "\\:whale2:" => "๐Ÿ‹", "\\:whale:" => "๐Ÿณ", "\\:wheelchair:" => "โ™ฟ", "\\:white_check_mark:" => "โœ…", "\\:white_circle:" => "โšช", "\\:white_flower:" => "๐Ÿ’ฎ", "\\:white_heart:" => "๐Ÿค", "\\:white_large_square:" => "โฌœ", "\\:white_medium_small_square:" => "โ—ฝ", "\\:white_medium_square:" => "โ—ป", "\\:white_small_square:" => "โ–ซ", "\\:white_square_button:" => "๐Ÿ”ณ", "\\:wilted_flower:" => "๐Ÿฅ€", "\\:wind_chime:" => "๐ŸŽ", "\\:window:" => "๐ŸชŸ", "\\:wine_glass:" => "๐Ÿท", "\\:wink:" => "๐Ÿ˜‰", "\\:wolf:" => "๐Ÿบ", "\\:woman:" => "๐Ÿ‘ฉ", "\\:womans_clothes:" => "๐Ÿ‘š", "\\:womans_flat_shoe:" => "๐Ÿฅฟ", "\\:womans_hat:" => "๐Ÿ‘’", "\\:womens:" => "๐Ÿšบ", "\\:wood:" => "๐Ÿชต", "\\:woozy_face:" => "๐Ÿฅด", "\\:worm:" => "๐Ÿชฑ", "\\:worried:" => "๐Ÿ˜Ÿ", "\\:wrench:" => "๐Ÿ”ง", "\\:wrestlers:" => "๐Ÿคผ", "\\:x:" => "โŒ", "\\:yarn:" => "๐Ÿงถ", "\\:yawning_face:" => "๐Ÿฅฑ", "\\:yellow_heart:" => "๐Ÿ’›", "\\:yen:" => "๐Ÿ’ด", "\\:yo-yo:" => "๐Ÿช€", "\\:yum:" => "๐Ÿ˜‹", "\\:zany_face:" => "๐Ÿคช", "\\:zap:" => "โšก", "\\:zebra_face:" => "๐Ÿฆ“", "\\:zipper_mouth_face:" => "๐Ÿค", "\\:zombie:" => "๐ŸงŸ", "\\:zzz:" => "๐Ÿ’ค", ) ƒ���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/TerminalMenus.jla������# This file is a part of Julia. License is MIT: https://julialang.org/license module TerminalMenus terminal = nothing # The user terminal import REPL function __init__() global terminal terminal = REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), stdin, stdout, stderr) end include("util.jl") include("config.jl") include("AbstractMenu.jl") include("RadioMenu.jl") include("MultiSelectMenu.jl") include("Pager.jl") export RadioMenu, MultiSelectMenu, Pager, request # TODO: remove in Julia 2.0 # While not exported, AbstractMenu documented these as an extension interface @deprecate printMenu printmenu function writeLine(buf::IOBuffer, m::AbstractMenu, idx::Int, cursor::Bool) Base.depwarn("`writeLine` is deprecated, use `writeline` instead.", :writeLine) error("unimplemented") end end # module z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/util.jl๕������# This file is a part of Julia. License is MIT: https://julialang.org/license # Enum for escaped (multi-byte) keys such as the arrows or the home/end keys @enum(Key, ARROW_LEFT = 1000, ARROW_RIGHT, ARROW_UP, ARROW_DOWN, DEL_KEY, HOME_KEY, END_KEY, PAGE_UP, PAGE_DOWN) readbyte(stream::IO=stdin) = read(stream, Char) # Read the next key from stdin. It is also able to read several bytes for # escaped keys such as the arrow keys, home/end keys, etc. # Escaped keys are returned using the `Key` enum. readkey(stream::IO=stdin) = UInt32(_readkey(stream)) function _readkey(stream::IO=stdin) c = readbyte(stream) # Escape characters if c == '\x1b' bytesavailable(stream) < 1 && return '\x1b' esc_a = readbyte(stream) esc_a == 'v' && return PAGE_UP # M-v esc_a == '<' && return HOME_KEY # M-< esc_a == '>' && return END_KEY # M-> bytesavailable(stream) < 1 && return '\x1b' esc_b = readbyte(stream) if esc_a == '[' || esc_a == 'O' if esc_b >= '0' && esc_b <= '9' bytesavailable(stream) < 1 && return '\x1b' esc_c = readbyte(stream) if esc_c == '~' esc_b == '1' && return HOME_KEY esc_b == '4' && return END_KEY esc_b == '3' && return DEL_KEY esc_b == '5' && return PAGE_UP esc_b == '6' && return PAGE_DOWN esc_b == '7' && return HOME_KEY esc_b == '8' && return END_KEY return '\x1b' end else # Arrow keys esc_b == 'A' && return ARROW_UP esc_b == 'B' && return ARROW_DOWN esc_b == 'C' && return ARROW_RIGHT esc_b == 'D' && return ARROW_LEFT esc_b == 'H' && return HOME_KEY esc_b == 'F' && return END_KEY return '\x1b' end end esc_a == 'H' && return HOME_KEY esc_a == 'F' && return END_KEY return '\x1b' end c == '\x16' && return PAGE_DOWN # C-v c == '\x10' && return ARROW_UP # C-p c == '\x0e' && return ARROW_DOWN # C-n return c end |���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/config.jl๗������# This file is a part of Julia. License is MIT: https://julialang.org/license abstract type AbstractConfig end struct Config <: AbstractConfig cursor::Char up_arrow::Char down_arrow::Char updown_arrow::Char scroll_wrap::Bool ctrl_c_interrupt::Bool end struct MultiSelectConfig <: AbstractConfig config::Config checked::String unchecked::String end """ Config(; scroll_wrap=false, ctrl_c_interrupt=true, charset=:ascii, cursor::Char, up_arrow::Char, down_arrow::Char) Configure behavior for selection menus via keyword arguments: - `scroll_wrap`, if `true`, causes the menu to wrap around when scrolling above the first or below the last entry - `ctrl_c_interrupt`, if `true`, throws an `InterruptException` if the user hits Ctrl-C during menu selection. If `false`, [`TerminalMenus.request`](@ref) will return the default result from [`TerminalMenus.selected`](@ref). - `charset` affects the default values for `cursor`, `up_arrow`, and `down_arrow`, and can be `:ascii` or `:unicode` - `cursor` is the character printed to indicate the option that will be chosen by hitting "Enter." Defaults are '>' or 'โ†’', depending on `charset`. - `up_arrow` is the character printed when the display does not include the first entry. Defaults are '^' or 'โ†‘', depending on `charset`. - `down_arrow` is the character printed when the display does not include the last entry. Defaults are 'v' or 'โ†“', depending on `charset`. Subtypes of `ConfiguredMenu` will print `cursor`, `up_arrow`, and `down_arrow` automatically as needed, your `writeline` method should not print them. !!! compat "Julia 1.6" `Config` is available as of Julia 1.6. On older releases use the global `CONFIG`. """ function Config(; charset::Symbol = :ascii, cursor::Char = '\0', up_arrow::Char = '\0', down_arrow::Char = '\0', updown_arrow::Char = '\0', scroll_wrap::Bool = false, ctrl_c_interrupt::Bool = true) charset === :ascii || charset === :unicode || throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) if cursor == '\0' cursor = charset === :ascii ? '>' : 'โ†’' end if up_arrow == '\0' up_arrow = charset === :ascii ? '^' : 'โ†‘' end if down_arrow == '\0' down_arrow = charset === :ascii ? 'v' : 'โ†“' end if updown_arrow == '\0' updown_arrow = charset === :ascii ? 'I' : 'โ†•' end return Config(cursor, up_arrow, down_arrow, updown_arrow, scroll_wrap, ctrl_c_interrupt) end """ MultiSelectConfig(; charset=:ascii, checked::String, unchecked::String, kwargs...) Configure behavior for a multiple-selection menu via keyword arguments: - `checked` is the string to print when an option has been selected. Defaults are "[X]" or "โœ“", depending on `charset`. - `unchecked` is the string to print when an option has not been selected. Defaults are "[ ]" or "โฌš", depending on `charset`. All other keyword arguments are as described for [`TerminalMenus.Config`](@ref). `checked` and `unchecked` are not printed automatically, and should be printed by your `writeline` method. !!! compat "Julia 1.6" `MultiSelectConfig` is available as of Julia 1.6. On older releases use the global `CONFIG`. """ function MultiSelectConfig(; charset::Symbol = :ascii, checked::String = "", unchecked::String = "", kwargs...) charset === :ascii || charset === :unicode || throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) if isempty(checked) checked = charset === :ascii ? "[X]" : "โœ“" end if isempty(unchecked) unchecked = charset === :ascii ? "[ ]" : "โฌš" end return MultiSelectConfig(Config(; charset=charset, kwargs...), checked, unchecked) end ## Below is the old-style CONFIG interface, kept for backwards compatibility. ## Not recommended for any new menu types. """ CONFIG Global menu configuration parameters !!! compat "Julia 1.6" `CONFIG` is deprecated, instead configure menus via their constructors. """ const CONFIG = Dict{Symbol,Union{Char,String,Bool}}() """ config( <see arguments> ) Keyword-only function to configure global menu parameters # Arguments - `charset::Symbol=:na`: ui characters to use (`:ascii` or `:unicode`); overridden by other arguments - `cursor::Char='>'|'โ†’'`: character to use for cursor - `up_arrow::Char='^'|'โ†‘'`: character to use for up arrow - `down_arrow::Char='v'|'โ†“'`: character to use for down arrow - `checked::String="[X]"|"โœ“"`: string to use for checked - `unchecked::String="[ ]"|"โฌš")`: string to use for unchecked - `scroll::Symbol=:nowrap`: If `:wrap` wrap cursor around top and bottom, if :`nowrap` do not wrap cursor - `supress_output::Bool=false`: Ignored legacy argument, pass `suppress_output` as a keyword argument to `request` instead. - `ctrl_c_interrupt::Bool=true`: If `false`, return empty on ^C, if `true` throw InterruptException() on ^C !!! compat "Julia 1.6" As of Julia 1.6, `config` is deprecated. Use `Config` or `MultiSelectConfig` instead. """ function config(;charset::Symbol = :na, scroll::Symbol = :na, cursor::Char = '\0', up_arrow::Char = '\0', down_arrow::Char = '\0', updown_arrow::Char = '\0', checked::String = "", unchecked::String = "", supress_output::Union{Nothing, Bool}=nothing, # typo was documented, unfortunately ctrl_c_interrupt::Union{Nothing, Bool}=nothing) if charset === :ascii cursor = '>' up_arrow = '^' down_arrow = 'v' updown_arrow = 'I' checked = "[X]" unchecked = "[ ]" elseif charset === :unicode cursor = 'โ†’' up_arrow = 'โ†‘' down_arrow = 'โ†“' updown_arrow = 'โ†•' checked = "โœ“" unchecked = "โฌš" elseif charset === :na else throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) end scroll โˆ‰ [:na, :wrap, :nowrap] && throw(ArgumentError("scroll must be :wrap or :nowrap, received $scroll")) scroll === :wrap && (CONFIG[:scroll_wrap] = true) scroll === :nowrap && (CONFIG[:scroll_wrap] = false) cursor != '\0' && (CONFIG[:cursor] = cursor) up_arrow != '\0' && (CONFIG[:up_arrow] = up_arrow) down_arrow != '\0' && (CONFIG[:down_arrow] = down_arrow) updown_arrow != '\0' && (CONFIG[:updown_arrow] = updown_arrow) checked != "" && (CONFIG[:checked] = checked) unchecked != "" && (CONFIG[:unchecked] = unchecked) supress_output isa Bool && (CONFIG[:supress_output] = supress_output) ctrl_c_interrupt isa Bool && (CONFIG[:ctrl_c_interrupt] = ctrl_c_interrupt) return nothing end # Set up defaults config(charset=:ascii, scroll=:nowrap, supress_output=false, ctrl_c_interrupt=true) ‚���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/AbstractMenu.jll5������# This file is a part of Julia. License is MIT: https://julialang.org/license """ AbstractMenu The supertype for all Menu types. # Functions The following functions can be called on all <:AbstractMenu types. Details can be found in ## Exported - `request(m::AbstractMenu)` - `request(msg::AbstractString, m::AbstractMenu)` ## Hidden - `printmenu(m::AbstractMenu, cursor::Int; init::Bool=false, oldstate=nothing)` # Subtypes All subtypes must be mutable, and must contain the fields `pagesize::Int` and `pageoffset::Int`. They must also implement the following functions. ## Necessary Functions These functions must be implemented for all subtypes of AbstractMenu. - `pick(m::AbstractMenu, cursor::Int)` - `cancel(m::AbstractMenu)` - `options(m::AbstractMenu)` # `numoptions` is an alternative - `writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool)` If `m` does not have a field called `selected`, then you must also implement `selected(m)`. ## Optional Functions These functions do not need to be implemented for all AbstractMenu subtypes. - `header(m::AbstractMenu)` - `keypress(m::AbstractMenu, i::UInt32)` - `numoptions(m::AbstractMenu)` - `selected(m::AbstractMenu)` """ abstract type AbstractMenu end function getproperty(m::AbstractMenu, name::Symbol) if name === :pagesize return getfield(m, :pagesize)::Int elseif name === :pageoffset return getfield(m, :pageoffset)::Int end return getfield(m, name) end # TODO Julia2.0: get rid of parametric intermediate, making it just # abstract type ConfiguredMenu <: AbstractMenu end # Or perhaps just make all menus ConfiguredMenus # Also consider making `cursor` a mandatory field in the Menu structs # instead of going via the RefValue in `request`. abstract type _ConfiguredMenu{C} <: AbstractMenu end const ConfiguredMenu = _ConfiguredMenu{<:AbstractConfig} # NECESSARY FUNCTIONS # These functions must be implemented for all subtypes of AbstractMenu ###################################################################### """ pick(m::AbstractMenu, cursor::Int) Defines what happens when a user presses the Enter key while the menu is open. If `true` is returned, `request()` will exit. `cursor` indexes the position of the selection. """ pick(m::AbstractMenu, cursor::Int) = error("unimplemented") """ cancel(m::AbstractMenu) Define what happens when a user cancels ('q' or ctrl-c) a menu. `request()` will always exit after calling this function. """ cancel(m::AbstractMenu) = error("unimplemented") """ options(m::AbstractMenu) Return a list of strings to be displayed as options in the current page. Alternatively, implement `numoptions`, in which case `options` is not needed. """ options(m::AbstractMenu) = error("unimplemented") """ writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool) Write the option at index `idx` to `buf`. `iscursor`, if `true`, indicates that this item is at the current cursor position (the one that will be selected by hitting "Enter"). If `m` is a `ConfiguredMenu`, `TerminalMenus` will print the cursor indicator. Otherwise the callee is expected to handle such printing. !!! compat "Julia 1.6" `writeline` requires Julia 1.6 or higher. On older versions of Julia, this was `writeLine(buf::IO, m::AbstractMenu, idx, iscursor::Bool)` and `m` is assumed to be unconfigured. The selection and cursor indicators can be obtained from `TerminalMenus.CONFIG`. This older function is supported on all Julia 1.x versions but will be dropped in Julia 2.0. """ function writeline(buf::IO, m::AbstractMenu, idx::Int, iscursor::Bool) # error("unimplemented") # TODO: use this in Julia 2.0 writeLine(buf, m, idx, iscursor) end # OPTIONAL FUNCTIONS # These functions do not need to be implemented for all menu types ################################################################## """ header(m::AbstractMenu) -> String Return a header string to be printed above the menu. Defaults to "". """ header(m::AbstractMenu) = "" """ keypress(m::AbstractMenu, i::UInt32) -> Bool Handle any non-standard keypress event. If `true` is returned, [`TerminalMenus.request`](@ref) will exit. Defaults to `false`. """ keypress(m::AbstractMenu, i::UInt32) = false """ numoptions(m::AbstractMenu) -> Int Return the number of options in menu `m`. Defaults to `length(options(m))`. !!! compat "Julia 1.6" This function requires Julia 1.6 or later. """ numoptions(m::AbstractMenu) = length(options(m)) """ selected(m::AbstractMenu) Return information about the user-selected option. By default it returns `m.selected`. """ selected(m::AbstractMenu) = m.selected """ request(m::AbstractMenu; cursor=1) Display the menu and enter interactive mode. `cursor` indicates the item number used for the initial cursor position. `cursor` can be either an `Int` or a `RefValue{Int}`. The latter is useful for observation and control of the cursor position from the outside. Returns `selected(m)`. !!! compat "Julia 1.6" The `cursor` argument requires Julia 1.6 or later. """ request(m::AbstractMenu; kwargs...) = request(terminal, m; kwargs...) function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu; cursor::Union{Int, Base.RefValue{Int}}=1, suppress_output=false) if cursor isa Int cursor = Ref(cursor) end state = nothing if !suppress_output state = printmenu(term.out_stream, m, cursor[], init=true) end raw_mode_enabled = try REPL.Terminals.raw!(term, true) true catch err suppress_output || @warn "TerminalMenus: Unable to enter raw mode: " exception=(err, catch_backtrace()) false end # hide the cursor raw_mode_enabled && !suppress_output && print(term.out_stream, "\x1b[?25l") try while true lastoption = numoptions(m) c = readkey(term.in_stream) if c == Int(ARROW_UP) cursor[] = move_up!(m, cursor[], lastoption) elseif c == Int(ARROW_DOWN) cursor[] = move_down!(m, cursor[], lastoption) elseif c == Int(PAGE_UP) cursor[] = page_up!(m, cursor[], lastoption) elseif c == Int(PAGE_DOWN) cursor[] = page_down!(m, cursor[], lastoption) elseif c == Int(HOME_KEY) cursor[] = 1 m.pageoffset = 0 elseif c == Int(END_KEY) cursor[] = lastoption m.pageoffset = max(0, lastoption - m.pagesize) elseif c == 13 # <enter> # will break if pick returns true pick(m, cursor[]) && break elseif c == UInt32('q') cancel(m) break elseif c == 3 # ctrl-c cancel(m) ctrl_c_interrupt(m) ? throw(InterruptException()) : break else # will break if keypress returns true keypress(m, c) && break end if !suppress_output state = printmenu(term.out_stream, m, cursor[], oldstate=state) end end finally # always disable raw mode if raw_mode_enabled !suppress_output && print(term.out_stream, "\x1b[?25h") # unhide cursor REPL.Terminals.raw!(term, false) end end !suppress_output && println(term.out_stream) return selected(m) end """ request([term,] msg::AbstractString, m::AbstractMenu) Shorthand for `println(msg); request(m)`. """ request(msg::AbstractString, m::AbstractMenu; kwargs...) = request(terminal, msg, m; kwargs...) function request(term::REPL.Terminals.TTYTerminal, msg::AbstractString, m::AbstractMenu; kwargs...) println(term.out_stream, msg) request(term, m; kwargs...) end function move_up!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) if cursor > 1 cursor -= 1 # move selection up if cursor < (2+m.pageoffset) && m.pageoffset > 0 m.pageoffset -= 1 # scroll page up end elseif scroll_wrap(m) # wrap to bottom cursor = lastoption m.pageoffset = max(0, lastoption - m.pagesize) end return cursor end function move_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) if cursor < lastoption cursor += 1 # move selection down pagepos = m.pagesize + m.pageoffset if pagepos <= cursor && pagepos < lastoption m.pageoffset += 1 # scroll page down end elseif scroll_wrap(m) # wrap to top cursor = 1 m.pageoffset = 0 end return cursor end function page_up!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) # If we're at the bottom, move the page 1 less to move the cursor up from # the bottom entry, since we try to avoid putting the cursor at bounds. m.pageoffset -= m.pagesize - (cursor == lastoption ? 1 : 0) m.pageoffset = max(m.pageoffset, 0) return max(cursor - m.pagesize, 1) end function page_down!(m::AbstractMenu, cursor::Int, lastoption::Int=numoptions(m)) m.pageoffset += m.pagesize - (cursor == 1 ? 1 : 0) m.pageoffset = max(0, min(m.pageoffset, lastoption - m.pagesize)) return min(cursor + m.pagesize, lastoption) end """ printmenu(out, m::AbstractMenu, cursoridx::Int; init::Bool=false, oldstate=nothing) -> newstate Display the state of a menu. `init=true` causes `m.pageoffset` to be initialized to start printing at or just above the current cursor location; when `init` is false, the terminal will preserve the current setting of `m.pageoffset` and overwrite the previous display. Returns `newstate`, which can be passed in as `oldstate` on the next call to allow accurate overwriting of the previous display. !!! compat "Julia 1.6" `printmenu` requires Julia 1.6 or higher. On older versions of Julia, this was called `printMenu` and it lacked the `state` argument/return value. This older function is supported on all Julia 1.x versions but will be dropped in Julia 2.0. """ function printmenu(out::IO, m::AbstractMenu, cursoridx::Int; oldstate=nothing, init::Bool=false) # TODO Julia 2.0?: get rid of `init` and just use `oldstate` buf = IOBuffer() lastoption = numoptions(m)::Int ncleared = oldstate === nothing ? m.pagesize-1 : oldstate if init # like clamp, except this takes the min if max < min m.pageoffset = max(0, min(cursoridx - m.pagesize รท 2, lastoption - m.pagesize)) else print(buf, "\r") if ncleared > 0 # Move up `ncleared` lines. However, moving up zero lines # is interpreted as one line, so need to do this # conditionally. (More specifically, the `0` value means # to use the default, and for move up this is one.) print(buf, "\x1b[$(ncleared)A") end end nheaderlines = 0 for headerline in split(header(m), "\n", keepempty=false) print(buf, "\x1b[2K", headerline, "\r\n") nheaderlines += 1 end firstline = m.pageoffset+1 lastline = min(m.pagesize+m.pageoffset, lastoption) for i in firstline:lastline # clearline print(buf, "\x1b[2K") upscrollable = i == firstline && m.pageoffset > 0 downscrollable = i == lastline && i != lastoption if upscrollable && downscrollable print(buf, updown_arrow(m)::Union{Char,String}) elseif upscrollable print(buf, up_arrow(m)::Union{Char,String}) elseif downscrollable print(buf, down_arrow(m)::Union{Char,String}) else print(buf, ' ') end printcursor(buf, m, i == cursoridx) writeline(buf, m, i, i == cursoridx) (i != lastline) && print(buf, "\r\n") end newstate = nheaderlines + lastline - firstline # final line doesn't have `\n` if newstate < ncleared && oldstate !== nothing # we printed fewer lines than last time. Erase the leftovers. for i = newstate+1:ncleared print(buf, "\r\n\x1b[2K") end print(buf, "\x1b[$(ncleared-newstate)A") end print(out, String(take!(buf))) return newstate end scroll_wrap(m::ConfiguredMenu) = scroll_wrap(m.config) scroll_wrap(c::AbstractConfig) = scroll_wrap(c.config) scroll_wrap(c::Config) = c.scroll_wrap scroll_wrap(::AbstractMenu) = CONFIG[:scroll_wrap]::Bool ctrl_c_interrupt(m::ConfiguredMenu) = ctrl_c_interrupt(m.config) ctrl_c_interrupt(c::AbstractConfig) = ctrl_c_interrupt(c.config) ctrl_c_interrupt(c::Config) = c.ctrl_c_interrupt ctrl_c_interrupt(::AbstractMenu) = CONFIG[:ctrl_c_interrupt]::Bool up_arrow(m::ConfiguredMenu) = up_arrow(m.config) up_arrow(c::AbstractConfig) = up_arrow(c.config) up_arrow(c::Config) = c.up_arrow up_arrow(::AbstractMenu) = CONFIG[:up_arrow]::Char down_arrow(m::ConfiguredMenu) = down_arrow(m.config) down_arrow(c::AbstractConfig) = down_arrow(c.config) down_arrow(c::Config) = c.down_arrow down_arrow(::AbstractMenu) = CONFIG[:down_arrow]::Char updown_arrow(m::ConfiguredMenu) = updown_arrow(m.config) updown_arrow(c::AbstractConfig) = updown_arrow(c.config) updown_arrow(c::Config) = c.updown_arrow updown_arrow(::AbstractMenu) = CONFIG[:updown_arrow]::Char printcursor(buf, m::ConfiguredMenu, iscursor::Bool) = print(buf, iscursor ? cursor(m.config) : ' ', ' ') cursor(c::AbstractConfig) = cursor(c.config) cursor(c::Config) = c.cursor printcursor(buf, ::AbstractMenu, ::Bool) = nothing # `writeLine` is expected to do the printing (get from CONFIG[:cursor]) ���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/RadioMenu.jl* ������# This file is a part of Julia. License is MIT: https://julialang.org/license """ RadioMenu A menu that allows a user to select a single option from a list. # Sample Output ```julia-repl julia> request(RadioMenu(options, pagesize=4)) Choose your favorite fruit: ^ grape strawberry > blueberry v peach Your favorite fruit is blueberry! ``` """ mutable struct RadioMenu{C} <: _ConfiguredMenu{C} options::Array{String,1} keybindings::Vector{Char} pagesize::Int pageoffset::Int selected::Int config::C end """ RadioMenu(options::Array{String,1}; pagesize::Int=10, keybindings::Vector{Char}=Char[], kwargs...) Create a RadioMenu object. Use `request(menu::RadioMenu)` to get user input. `request()` returns an `Int` which is the index of the option selected by the user. # Arguments - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize - `keybindings::Vector{Char}=Char[]`: Shortcuts to pick corresponding entry from `options` Any additional keyword arguments will be passed to [`TerminalMenus.Config`](@ref). !!! compat "Julia 1.8" The `keybindings` argument requires Julia 1.8 or later. """ function RadioMenu(options::Array{String,1}; pagesize::Int=10, warn::Bool=true, keybindings::Vector{Char}=Char[], kwargs...) length(options) < 1 && error("RadioMenu must have at least one option") length(keybindings) in [0, length(options)] || error("RadioMenu must have either no keybindings, or one per option") # if pagesize is -1, use automatic paging pagesize = pagesize == -1 ? length(options) : pagesize # pagesize shouldn't be bigger than options pagesize = min(length(options), pagesize) # after other checks, pagesize must be greater than 1 pagesize < 1 && error("pagesize must be >= 1") pageoffset = 0 selected = -1 # none if !isempty(kwargs) RadioMenu(options, keybindings, pagesize, pageoffset, selected, Config(; kwargs...)) else warn && Base.depwarn("Legacy `RadioMenu` interface is deprecated, set a configuration option such as `RadioMenu(options; charset=:ascii)` to trigger the new interface.", :RadioMenu) RadioMenu(options, keybindings, pagesize, pageoffset, selected, CONFIG) end end # AbstractMenu implementation functions # See AbstractMenu.jl ####################################### options(m::RadioMenu) = m.options cancel(m::RadioMenu) = m.selected = -1 function pick(menu::RadioMenu, cursor::Int) menu.selected = cursor return true #break out of the menu end function writeline(buf::IOBuffer, menu::RadioMenu{Config}, idx::Int, iscursor::Bool) print(buf, replace(menu.options[idx], "\n" => "\\n")) end function keypress(m::RadioMenu, i::UInt32) isempty(m.keybindings) && return false i = findfirst(isequal(i), Int.(m.keybindings)) isnothing(i) && return false m.selected = i return true end # Legacy interface function writeLine(buf::IOBuffer, menu::RadioMenu{<:Dict}, idx::Int, cursor::Bool) # print a ">" on the selected entry cursor ? print(buf, menu.config[:cursor] ," ") : print(buf, " ") print(buf, replace(menu.options[idx], "\n" => "\\n")) end …���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/MultiSelectMenu.jlY������# This file is a part of Julia. License is MIT: https://julialang.org/license """ MultiSelectMenu A menu that allows a user to select a multiple options from a list. # Sample Output ```julia-repl julia> request(MultiSelectMenu(options)) Select the fruits you like: [press: Enter=toggle, a=all, n=none, d=done, q=abort] [ ] apple > [X] orange [X] grape [ ] strawberry [ ] blueberry [X] peach [ ] lemon [ ] lime You like the following fruits: - orange - grape - peach ``` """ mutable struct MultiSelectMenu{C} <: _ConfiguredMenu{C} options::Array{String,1} pagesize::Int pageoffset::Int selected::Set{Int} config::C end """ MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=[], kwargs...) Create a MultiSelectMenu object. Use `request(menu::MultiSelectMenu)` to get user input. It returns a `Set` containing the indices of options that were selected by the user. # Arguments - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize - `selected=[]`: pre-selected items. `i โˆˆ selected` means that `options[i]` is preselected. Any additional keyword arguments will be passed to [`TerminalMenus.MultiSelectConfig`](@ref). !!! compat "Julia 1.6" The `selected` argument requires Julia 1.6 or later. """ function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, selected=Int[], warn::Bool=true, kwargs...) length(options) < 1 && error("MultiSelectMenu must have at least one option") # if pagesize is -1, use automatic paging pagesize = pagesize == -1 ? length(options) : pagesize # pagesize shouldn't be bigger than options pagesize = min(length(options), pagesize) # after other checks, pagesize must be at least 1 pagesize < 1 && error("pagesize must be >= 1") pageoffset = 0 _selected = Set{Int}() for item in selected push!(_selected, item) end if !isempty(kwargs) MultiSelectMenu(options, pagesize, pageoffset, _selected, MultiSelectConfig(; kwargs...)) else warn && Base.depwarn("Legacy `MultiSelectMenu` interface is deprecated, set a configuration option such as `MultiSelectMenu(options; charset=:ascii)` to trigger the new interface.", :MultiSelectMenu) MultiSelectMenu(options, pagesize, pageoffset, _selected, CONFIG) end end # AbstractMenu implementation functions # See AbstractMenu.jl ####################################### header(m::MultiSelectMenu) = "[press: Enter=toggle, a=all, n=none, d=done, q=abort]" options(m::MultiSelectMenu) = m.options cancel(m::MultiSelectMenu) = m.selected = Set{Int}() # Do not exit menu when a user selects one of the options function pick(menu::MultiSelectMenu, cursor::Int) if cursor in menu.selected delete!(menu.selected, cursor) else push!(menu.selected, cursor) end return false #break out of the menu end function writeline(buf::IOBuffer, menu::MultiSelectMenu{MultiSelectConfig}, idx::Int, iscursor::Bool) if idx in menu.selected print(buf, menu.config.checked, " ") else print(buf, menu.config.unchecked, " ") end print(buf, replace(menu.options[idx], "\n" => "\\n")) end # d: Done, return from request # a: Select all # n: Deselect all function keypress(menu::MultiSelectMenu, key::UInt32) if key == UInt32('d') || key == UInt32('D') return true # break elseif key == UInt32('a') || key == UInt32('A') menu.selected = Set(1:length(menu.options)) elseif key == UInt32('n') || key == UInt32('N') menu.selected = Set{Int}() end false # don't break end ## Legacy interface function TerminalMenus.writeLine(buf::IOBuffer, menu::MultiSelectMenu{<:Dict}, idx::Int, cursor::Bool) # print a ">" on the selected entry cursor ? print(buf, menu.config[:cursor]," ") : print(buf, " ") if idx in menu.selected print(buf, menu.config[:checked], " ") else print(buf, menu.config[:unchecked], " ") end print(buf, replace(menu.options[idx], "\n" => "\\n")) end {���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/TerminalMenus/Pager.jl๙������# This file is a part of Julia. License is MIT: https://julialang.org/license mutable struct Pager{C} <: _ConfiguredMenu{C} lines::Vector{String} pagesize::Int pageoffset::Int selected::Nothing config::C end function Pager(text::AbstractString; pagesize::Int=10, kwargs...) lines = readlines(IOBuffer(text)) return Pager(lines, pagesize, 0, nothing, Config(; kwargs...)) end function header(p::Pager) total = length(p.lines) current = min(p.pageoffset + p.pagesize, total) percent = round(Int, (current / total) * 100) return "($(lpad(current, ndigits(total))) / $total) $(lpad(percent, 3))%" end options(p::Pager) = p.lines cancel(::Pager) = nothing pick(::Pager, ::Int) = true function writeline(buf::IOBuffer, pager::Pager{Config}, idx::Int, iscursor::Bool) print(buf, pager.lines[idx]) end function pager(terminal, object) lines, columns = displaysize(terminal)::Tuple{Int,Int} columns -= 3 buffer = IOBuffer() ctx = IOContext(buffer, :color => REPL.Terminals.hascolor(terminal), :displaysize => (lines, columns)) show(ctx, "text/plain", object) pager = Pager(String(take!(buffer)); pagesize = div(lines, 2)) return request(terminal, pager) end pager(object) = pager(terminal, object) o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/REPL/src/docview.jlor������# This file is a part of Julia. License is MIT: https://julialang.org/license ## Code for searching and viewing documentation using Markdown using Base.Docs: catdoc, modules, DocStr, Binding, MultiDoc, keywords, isfield, namify, bindingexpr, defined, resolve, getdoc, meta, aliasof, signature import Base.Docs: doc, formatdoc, parsedoc, apropos using Base: with_output_color, mapany import REPL using InteractiveUtils: subtypes using Unicode: normalize ## Help mode ## # This is split into helpmode and _helpmode to easier unittest _helpmode helpmode(io::IO, line::AbstractString, mod::Module=Main) = :($REPL.insert_hlines($io, $(REPL._helpmode(io, line, mod)))) helpmode(line::AbstractString, mod::Module=Main) = helpmode(stdout, line, mod) const extended_help_on = Ref{Any}(nothing) function _helpmode(io::IO, line::AbstractString, mod::Module=Main) line = strip(line) ternary_operator_help = (line == "?" || line == "?:") if startswith(line, '?') && !ternary_operator_help line = line[2:end] extended_help_on[] = line brief = false else extended_help_on[] = nothing brief = true end # interpret anything starting with # or #= as asking for help on comments if startswith(line, "#") if startswith(line, "#=") line = "#=" else line = "#" end end x = Meta.parse(line, raise = false, depwarn = false) assym = Symbol(line) expr = if haskey(keywords, Symbol(line)) || Base.isoperator(assym) || isexpr(x, :error) || isexpr(x, :invalid) || isexpr(x, :incomplete) # Docs for keywords must be treated separately since trying to parse a single # keyword such as `function` would throw a parse error due to the missing `end`. assym elseif isexpr(x, (:using, :import)) (x::Expr).head else # Retrieving docs for macros requires us to make a distinction between the text # `@macroname` and `@macroname()`. These both parse the same, but are used by # the docsystem to return different results. The first returns all documentation # for `@macroname`, while the second returns *only* the docs for the 0-arg # definition if it exists. (isexpr(x, :macrocall, 1) && !endswith(line, "()")) ? quot(x) : x end # the following must call repl(io, expr) via the @repl macro # so that the resulting expressions are evaluated in the Base.Docs namespace :($REPL.@repl $io $expr $brief $mod) end _helpmode(line::AbstractString, mod::Module=Main) = _helpmode(stdout, line, mod) # Print vertical lines along each docstring if there are multiple docs function insert_hlines(io::IO, docs) if !isa(docs, Markdown.MD) || !haskey(docs.meta, :results) || isempty(docs.meta[:results]) return docs end docs = docs::Markdown.MD v = Any[] for (n, doc) in enumerate(docs.content) push!(v, doc) n == length(docs.content) || push!(v, Markdown.HorizontalRule()) end return Markdown.MD(v) end function formatdoc(d::DocStr) buffer = IOBuffer() for part in d.text formatdoc(buffer, d, part) end Markdown.MD(Any[Markdown.parse(seekstart(buffer))]) end @noinline formatdoc(buffer, d, part) = print(buffer, part) function parsedoc(d::DocStr) if d.object === nothing md = formatdoc(d) md.meta[:module] = d.data[:module] md.meta[:path] = d.data[:path] d.object = md end d.object end ## Trimming long help ("# Extended help") struct Message # For direct messages to the terminal msg # AbstractString fmt # keywords to `printstyled` end Message(msg) = Message(msg, ()) function Markdown.term(io::IO, msg::Message, columns) printstyled(io, msg.msg; msg.fmt...) end trimdocs(doc, brief::Bool) = doc function trimdocs(md::Markdown.MD, brief::Bool) brief || return md md, trimmed = _trimdocs(md, brief) if trimmed line = extended_help_on[] line = isa(line, AbstractString) ? line : "" push!(md.content, Message("Extended help is available with `??$line`", (color=Base.info_color(), bold=true))) end return md end function _trimdocs(md::Markdown.MD, brief::Bool) content, trimmed = [], false for c in md.content if isa(c, Markdown.Header{1}) && isa(c.text, AbstractArray) && !isempty(c.text) item = c.text[1] if isa(item, AbstractString) && lowercase(item) โˆˆ ("extended help", "extended documentation", "extended docs") trimmed = true break end end c, trm = _trimdocs(c, brief) trimmed |= trm push!(content, c) end return Markdown.MD(content, md.meta), trimmed end _trimdocs(md, brief::Bool) = md, false """ Docs.doc(binding, sig) Return all documentation that matches both `binding` and `sig`. If `getdoc` returns a non-`nothing` result on the value of the binding, then a dynamic docstring is returned instead of one based on the binding itself. """ function doc(binding::Binding, sig::Type = Union{}) if defined(binding) result = getdoc(resolve(binding), sig) result === nothing || return result end results, groups = DocStr[], MultiDoc[] # Lookup `binding` and `sig` for matches in all modules of the docsystem. for mod in modules dict = meta(mod; autoinit=false) isnothing(dict) && continue if haskey(dict, binding) multidoc = dict[binding] push!(groups, multidoc) for msig in multidoc.order sig <: msig && push!(results, multidoc.docs[msig]) end end end if isempty(groups) # When no `MultiDoc`s are found that match `binding` then we check whether `binding` # is an alias of some other `Binding`. When it is we then re-run `doc` with that # `Binding`, otherwise if it's not an alias then we generate a summary for the # `binding` and display that to the user instead. alias = aliasof(binding) alias == binding ? summarize(alias, sig) : doc(alias, sig) else # There was at least one match for `binding` while searching. If there weren't any # matches for `sig` then we concatenate *all* the docs from the matching `Binding`s. if isempty(results) for group in groups, each in group.order push!(results, group.docs[each]) end end # Get parsed docs and concatenate them. md = catdoc(mapany(parsedoc, results)...) # Save metadata in the generated markdown. if isa(md, Markdown.MD) md.meta[:results] = results md.meta[:binding] = binding md.meta[:typesig] = sig end return md end end # Some additional convenience `doc` methods that take objects rather than `Binding`s. doc(obj::UnionAll) = doc(Base.unwrap_unionall(obj)) doc(object, sig::Type = Union{}) = doc(aliasof(object, typeof(object)), sig) doc(object, sig...) = doc(object, Tuple{sig...}) function lookup_doc(ex) if isa(ex, Expr) && ex.head !== :(.) && Base.isoperator(ex.head) # handle syntactic operators, e.g. +=, ::, .= ex = ex.head end if haskey(keywords, ex) return parsedoc(keywords[ex]) elseif Meta.isexpr(ex, :incomplete) return :($(Markdown.md"No documentation found.")) elseif !isa(ex, Expr) && !isa(ex, Symbol) return :($(doc)($(typeof)($(esc(ex))))) end if isa(ex, Symbol) && Base.isoperator(ex) str = string(ex) isdotted = startswith(str, ".") if endswith(str, "=") && Base.operator_precedence(ex) == Base.prec_assignment && ex !== :(:=) op = chop(str) eq = isdotted ? ".=" : "=" return Markdown.parse("`x $op= y` is a synonym for `x $eq x $op y`") elseif isdotted && ex !== :(..) op = str[2:end] if op in ("&&", "||") return Markdown.parse("`x $ex y` broadcasts the boolean operator `$op` to `x` and `y`. See [`broadcast`](@ref).") else return Markdown.parse("`x $ex y` is akin to `broadcast($op, x, y)`. See [`broadcast`](@ref).") end end end binding = esc(bindingexpr(namify(ex))) if isexpr(ex, :call) || isexpr(ex, :macrocall) || isexpr(ex, :where) sig = esc(signature(ex)) :($(doc)($binding, $sig)) else :($(doc)($binding)) end end # Object Summaries. # ================= function summarize(binding::Binding, sig) io = IOBuffer() if defined(binding) binding_res = resolve(binding) !isa(binding_res, Module) && println(io, "No documentation found.\n") summarize(io, binding_res, binding) else println(io, "No documentation found.\n") quot = any(isspace, sprint(print, binding)) ? "'" : "" if Base.isbindingresolved(binding.mod, binding.var) println(io, "Binding ", quot, "`", binding, "`", quot, " exists, but has not been assigned a value.") else println(io, "Binding ", quot, "`", binding, "`", quot, " does not exist.") end end md = Markdown.parse(seekstart(io)) # Save metadata in the generated markdown. md.meta[:results] = DocStr[] md.meta[:binding] = binding md.meta[:typesig] = sig return md end function summarize(io::IO, ฮป::Function, binding::Binding) kind = startswith(string(binding.var), '@') ? "macro" : "`Function`" println(io, "`", binding, "` is a ", kind, ".") println(io, "```\n", methods(ฮป), "\n```") end function summarize(io::IO, TT::Type, binding::Binding) println(io, "# Summary") T = Base.unwrap_unionall(TT) if T isa DataType println(io, "```") print(io, Base.isabstracttype(T) ? "abstract type " : Base.ismutabletype(T) ? "mutable struct " : Base.isstructtype(T) ? "struct " : "primitive type ") supert = supertype(T) println(io, T) println(io, "```") if !Base.isabstracttype(T) && T.name !== Tuple.name && !isempty(fieldnames(T)) println(io, "# Fields") println(io, "```") pad = maximum(length(string(f)) for f in fieldnames(T)) for (f, t) in zip(fieldnames(T), fieldtypes(T)) println(io, rpad(f, pad), " :: ", t) end println(io, "```") end subt = subtypes(TT) if !isempty(subt) println(io, "# Subtypes") println(io, "```") for t in subt println(io, Base.unwrap_unionall(t)) end println(io, "```") end if supert != Any println(io, "# Supertype Hierarchy") println(io, "```") Base.show_supertypes(io, T) println(io) println(io, "```") end elseif T isa Union println(io, "`", binding, "` is of type `", typeof(TT), "`.\n") println(io, "# Union Composed of Types") for T1 in Base.uniontypes(T) println(io, " - `", Base.rewrap_unionall(T1, TT), "`") end else # unreachable? println(io, "`", binding, "` is of type `", typeof(TT), "`.\n") end end function find_readme(m::Module)::Union{String, Nothing} mpath = pathof(m) isnothing(mpath) && return nothing !isfile(mpath) && return nothing # modules in sysimage, where src files are omitted path = dirname(mpath) top_path = pkgdir(m) while true for file in readdir(path; join=true, sort=true) isfile(file) && (basename(lowercase(file)) in ["readme.md", "readme"]) || continue return file end path == top_path && break # go no further than pkgdir path = dirname(path) # work up through nested modules end return nothing end function summarize(io::IO, m::Module, binding::Binding; nlines::Int = 200) readme_path = find_readme(m) if isnothing(readme_path) println(io, "No docstring or readme file found for module `$m`.\n") else println(io, "No docstring found for module `$m`.") end exports = filter!(!=(nameof(m)), names(m)) if isempty(exports) println(io, "Module does not export any names.") else println(io, "# Exported names") print(io, " `") join(io, exports, "`, `") println(io, "`\n") end if !isnothing(readme_path) readme_lines = readlines(readme_path) isempty(readme_lines) && return # don't say we are going to print empty file println(io, "# Displaying contents of readme found at `$(readme_path)`") for line in first(readme_lines, nlines) println(io, line) end length(readme_lines) > nlines && println(io, "\n[output truncated to first $nlines lines]") end end function summarize(io::IO, @nospecialize(T), binding::Binding) T = typeof(T) println(io, "`", binding, "` is of type `", T, "`.\n") summarize(io, T, binding) end # repl search and completions for help quote_spaces(x) = any(isspace, x) ? "'" * x * "'" : x function repl_search(io::IO, s::Union{Symbol,String}, mod::Module) pre = "search:" print(io, pre) printmatches(io, s, map(quote_spaces, doc_completions(s, mod)), cols = _displaysize(io)[2] - length(pre)) println(io, "\n") end # TODO: document where this is used repl_search(s, mod::Module) = repl_search(stdout, s, mod) function repl_corrections(io::IO, s, mod::Module) print(io, "Couldn't find ") quot = any(isspace, s) ? "'" : "" print(io, quot) printstyled(io, s, color=:cyan) print(io, quot, '\n') print_correction(io, s, mod) end repl_corrections(s) = repl_corrections(stdout, s) # inverse of latex_symbols Dict, lazily created as needed const symbols_latex = Dict{String,String}() function symbol_latex(s::String) if isempty(symbols_latex) && isassigned(Base.REPL_MODULE_REF) for (k,v) in Iterators.flatten((REPLCompletions.latex_symbols, REPLCompletions.emoji_symbols)) symbols_latex[v] = k end # Overwrite with canonical mapping when a symbol has several completions (#39148) merge!(symbols_latex, REPLCompletions.symbols_latex_canonical) end return get(symbols_latex, s, "") end function repl_latex(io::IO, s0::String) # This has rampant `Core.Box` problems (#15276). Use the tricks of # https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured # We're changing some of the values so the `let` trick isn't applicable. s::String = s0 latex::String = symbol_latex(s) if isempty(latex) # Decompose NFC-normalized identifier to match tab-completion # input if the first search came up empty. s = normalize(s, :NFD) latex = symbol_latex(s) end if !isempty(latex) print(io, "\"") printstyled(io, s, color=:cyan) print(io, "\" can be typed by ") printstyled(io, latex, "<tab>", color=:cyan) println(io, '\n') elseif any(c -> haskey(symbols_latex, string(c)), s) print(io, "\"") printstyled(io, s, color=:cyan) print(io, "\" can be typed by ") state::Char = '\0' with_output_color(:cyan, io) do io for c in s cstr = string(c) if haskey(symbols_latex, cstr) latex = symbols_latex[cstr] if length(latex) == 3 && latex[2] in ('^','_') # coalesce runs of sub/superscripts if state != latex[2] '\0' != state && print(io, "<tab>") print(io, latex[1:2]) state = latex[2] end print(io, latex[3]) else if '\0' != state print(io, "<tab>") state = '\0' end print(io, latex, "<tab>") end else if '\0' != state print(io, "<tab>") state = '\0' end print(io, c) end end '\0' != state && print(io, "<tab>") end println(io, '\n') end end repl_latex(s::String) = repl_latex(stdout, s) macro repl(ex, brief::Bool=false, mod::Module=Main) repl(ex; brief, mod) end macro repl(io, ex, brief, mod) repl(io, ex; brief, mod) end function repl(io::IO, s::Symbol; brief::Bool=true, mod::Module=Main) str = string(s) quote repl_latex($io, $str) repl_search($io, $str, $mod) $(if !isdefined(mod, s) && !Base.isbindingresolved(mod, s) && !haskey(keywords, s) && !Base.isoperator(s) # n.b. we call isdefined for the side-effect of resolving the binding, if possible :(repl_corrections($io, $str, $mod)) end) $(_repl(s, brief)) end end isregex(x) = isexpr(x, :macrocall, 3) && x.args[1] === Symbol("@r_str") && !isempty(x.args[3]) repl(io::IO, ex::Expr; brief::Bool=true, mod::Module=Main) = isregex(ex) ? :(apropos($io, $ex)) : _repl(ex, brief) repl(io::IO, str::AbstractString; brief::Bool=true, mod::Module=Main) = :(apropos($io, $str)) repl(io::IO, other; brief::Bool=true, mod::Module=Main) = esc(:(@doc $other)) #repl(io::IO, other) = lookup_doc(other) # TODO repl(x; brief::Bool=true, mod::Module=Main) = repl(stdout, x; brief, mod) function _repl(x, brief::Bool=true) if isexpr(x, :call) x = x::Expr # determine the types of the values kwargs = nothing pargs = Any[] for arg in x.args[2:end] if isexpr(arg, :parameters) kwargs = mapany(arg.args) do kwarg if kwarg isa Symbol kwarg = :($kwarg::Any) elseif isexpr(kwarg, :kw) lhs = kwarg.args[1] rhs = kwarg.args[2] if lhs isa Symbol if rhs isa Symbol kwarg.args[1] = :($lhs::(@isdefined($rhs) ? typeof($rhs) : Any)) else kwarg.args[1] = :($lhs::typeof($rhs)) end end end kwarg end elseif isexpr(arg, :kw) if kwargs === nothing kwargs = Any[] end lhs = arg.args[1] rhs = arg.args[2] if lhs isa Symbol if rhs isa Symbol arg.args[1] = :($lhs::(@isdefined($rhs) ? typeof($rhs) : Any)) else arg.args[1] = :($lhs::typeof($rhs)) end end push!(kwargs, arg) else if arg isa Symbol arg = :($arg::(@isdefined($arg) ? typeof($arg) : Any)) elseif !isexpr(arg, :(::)) arg = :(::typeof($arg)) end push!(pargs, arg) end end if kwargs === nothing x.args = Any[x.args[1], pargs...] else x.args = Any[x.args[1], Expr(:parameters, kwargs...), pargs...] end end #docs = lookup_doc(x) # TODO docs = esc(:(@doc $x)) docs = if isfield(x) quote if isa($(esc(x.args[1])), DataType) fielddoc($(esc(x.args[1])), $(esc(x.args[2]))) else $docs end end else docs end :(REPL.trimdocs($docs, $brief)) end """ fielddoc(binding, field) Return documentation for a particular `field` of a type if it exists. """ function fielddoc(binding::Binding, field::Symbol) for mod in modules dict = meta(mod; autoinit=false) isnothing(dict) && continue if haskey(dict, binding) multidoc = dict[binding] if haskey(multidoc.docs, Union{}) fields = multidoc.docs[Union{}].data[:fields] if haskey(fields, field) doc = fields[field] return isa(doc, Markdown.MD) ? doc : Markdown.parse(doc) end end end end fields = join(["`$f`" for f in fieldnames(resolve(binding))], ", ", ", and ") fields = isempty(fields) ? "no fields" : "fields $fields" Markdown.parse("`$(resolve(binding))` has $fields.") end # As with the additional `doc` methods, this converts an object to a `Binding` first. fielddoc(object, field::Symbol) = fielddoc(aliasof(object, typeof(object)), field) # Search & Rescue # Utilities for correcting user mistakes and (eventually) # doing full documentation searches from the repl. # Fuzzy Search Algorithm function matchinds(needle, haystack; acronym::Bool = false) chars = collect(needle) is = Int[] lastc = '\0' for (i, char) in enumerate(haystack) while !isempty(chars) && isspace(first(chars)) popfirst!(chars) # skip spaces end isempty(chars) && break if lowercase(char) == lowercase(chars[1]) && (!acronym || !isletter(lastc)) push!(is, i) popfirst!(chars) end lastc = char end return is end longer(x, y) = length(x) โ‰ฅ length(y) ? (x, true) : (y, false) bestmatch(needle, haystack) = longer(matchinds(needle, haystack, acronym = true), matchinds(needle, haystack)) avgdistance(xs) = isempty(xs) ? 0 : (xs[end] - xs[1] - length(xs)+1)/length(xs) function fuzzyscore(needle, haystack) score = 0. is, acro = bestmatch(needle, haystack) score += (acro ? 2 : 1)*length(is) # Matched characters score -= 2(length(needle)-length(is)) # Missing characters !acro && (score -= avgdistance(is)/10) # Contiguous !isempty(is) && (score -= sum(is)/length(is)/100) # Closer to beginning return score end function fuzzysort(search::String, candidates::Vector{String}) scores = map(cand -> (fuzzyscore(search, cand), -Float64(levenshtein(search, cand))), candidates) candidates[sortperm(scores)] |> reverse end # Levenshtein Distance function levenshtein(s1, s2) a, b = collect(s1), collect(s2) m = length(a) n = length(b) d = Matrix{Int}(undef, m+1, n+1) d[1:m+1, 1] = 0:m d[1, 1:n+1] = 0:n for i = 1:m, j = 1:n d[i+1,j+1] = min(d[i , j+1] + 1, d[i+1, j ] + 1, d[i , j ] + (a[i] != b[j])) end return d[m+1, n+1] end function levsort(search::String, candidates::Vector{String}) scores = map(cand -> (Float64(levenshtein(search, cand)), -fuzzyscore(search, cand)), candidates) candidates = candidates[sortperm(scores)] i = 0 for outer i = 1:length(candidates) levenshtein(search, candidates[i]) > 3 && break end return candidates[1:i] end # Result printing function printmatch(io::IO, word, match) is, _ = bestmatch(word, match) for (i, char) = enumerate(match) if i in is printstyled(io, char, bold=true) else print(io, char) end end end function printmatches(io::IO, word, matches; cols::Int = _displaysize(io)[2]) total = 0 for match in matches total + length(match) + 1 > cols && break fuzzyscore(word, match) < 0 && break print(io, " ") printmatch(io, word, match) total += length(match) + 1 end end printmatches(args...; cols::Int = _displaysize(stdout)[2]) = printmatches(stdout, args..., cols = cols) function print_joined_cols(io::IO, ss::Vector{String}, delim = "", last = delim; cols::Int = _displaysize(io)[2]) i = 0 total = 0 for outer i = 1:length(ss) total += length(ss[i]) total + max(i-2,0)*length(delim) + (i>1 ? 1 : 0)*length(last) > cols && (i-=1; break) end join(io, ss[1:i], delim, last) end print_joined_cols(args...; cols::Int = _displaysize(stdout)[2]) = print_joined_cols(stdout, args...; cols=cols) function print_correction(io::IO, word::String, mod::Module) cors = map(quote_spaces, levsort(word, accessible(mod))) pre = "Perhaps you meant " print(io, pre) print_joined_cols(io, cors, ", ", " or "; cols = _displaysize(io)[2] - length(pre)) println(io) return end # TODO: document where this is used print_correction(word, mod::Module) = print_correction(stdout, word, mod) # Completion data moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod) filtervalid(names) = filter(x->!occursin(r"#", x), map(string, names)) accessible(mod::Module) = Symbol[filter!(s -> !Base.isdeprecated(mod, s), names(mod, all=true, imported=true)); map(names, moduleusings(mod))...; collect(keys(Base.Docs.keywords))] |> unique |> filtervalid function doc_completions(name, mod::Module=Main) res = fuzzysort(name, accessible(mod)) # to insert an entry like `raw""` for `"@raw_str"` in `res` ms = match.(r"^@(.*?)_str$", res) idxs = findall(!isnothing, ms) # avoid messing up the order while inserting for i in reverse!(idxs) c = only((ms[i]::AbstractMatch).captures) insert!(res, i, "$(c)\"\"") end res end doc_completions(name::Symbol) = doc_completions(string(name), mod) # Searching and apropos # Docsearch simply returns true or false if an object contains the given needle docsearch(haystack::AbstractString, needle) = occursin(needle, haystack) docsearch(haystack::Symbol, needle) = docsearch(string(haystack), needle) docsearch(::Nothing, needle) = false function docsearch(haystack::Array, needle) for elt in haystack docsearch(elt, needle) && return true end false end function docsearch(haystack, needle) @warn "Unable to search documentation of type $(typeof(haystack))" maxlog=1 false end ## Searching specific documentation objects function docsearch(haystack::MultiDoc, needle) for v in values(haystack.docs) docsearch(v, needle) && return true end false end function docsearch(haystack::DocStr, needle) docsearch(parsedoc(haystack), needle) && return true if haskey(haystack.data, :fields) for doc in values(haystack.data[:fields]) docsearch(doc, needle) && return true end end false end ## doc search ## Markdown search simply strips all markup and searches plain text version docsearch(haystack::Markdown.MD, needle) = docsearch(stripmd(haystack.content), needle) """ stripmd(x) Strip all Markdown markup from x, leaving the result in plain text. Used internally by apropos to make docstrings containing more than one markdown element searchable. """ stripmd(@nospecialize x) = string(x) # for random objects interpolated into the docstring stripmd(x::AbstractString) = x # base case stripmd(x::Nothing) = " " stripmd(x::Vector) = string(map(stripmd, x)...) stripmd(x::Markdown.BlockQuote) = "$(stripmd(x.content))" stripmd(x::Markdown.Admonition) = "$(stripmd(x.content))" stripmd(x::Markdown.Bold) = "$(stripmd(x.text))" stripmd(x::Markdown.Code) = "$(stripmd(x.code))" stripmd(x::Markdown.Header) = stripmd(x.text) stripmd(x::Markdown.HorizontalRule) = " " stripmd(x::Markdown.Image) = "$(stripmd(x.alt)) $(x.url)" stripmd(x::Markdown.Italic) = "$(stripmd(x.text))" stripmd(x::Markdown.LaTeX) = "$(x.formula)" stripmd(x::Markdown.LineBreak) = " " stripmd(x::Markdown.Link) = "$(stripmd(x.text)) $(x.url)" stripmd(x::Markdown.List) = join(map(stripmd, x.items), " ") stripmd(x::Markdown.MD) = join(map(stripmd, x.content), " ") stripmd(x::Markdown.Paragraph) = stripmd(x.content) stripmd(x::Markdown.Footnote) = "$(stripmd(x.id)) $(stripmd(x.text))" stripmd(x::Markdown.Table) = join([join(map(stripmd, r), " ") for r in x.rows], " ") """ apropos([io::IO=stdout], pattern::Union{AbstractString,Regex}) Search available docstrings for entries containing `pattern`. When `pattern` is a string, case is ignored. Results are printed to `io`. `apropos` can be called from the help mode in the REPL by wrapping the query in double quotes: ``` help?> "pattern" ``` """ apropos(string) = apropos(stdout, string) apropos(io::IO, string) = apropos(io, Regex("\\Q$string", "i")) function apropos(io::IO, needle::Regex) for mod in modules # Module doc might be in README.md instead of the META dict docsearch(doc(mod), needle) && println(io, mod) dict = meta(mod; autoinit=false) isnothing(dict) && continue for (k, v) in dict docsearch(v, needle) && println(io, k) end end end l���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/TOML/src/TOML.jlฉ������# This file is a part of Julia. License is MIT: https://julialang.org/license module TOML module Internals # The parser is defined in Base using Base.TOML: Parser, parse, tryparse, ParserError, isvalid_barekey_char, reinit! # Put the error instances in this module for errtype in instances(Base.TOML.ErrorType) @eval using Base.TOML: $(Symbol(errtype)) end # We put the printing functionality in a separate module since It # defines a function `print` and we don't want that to collide with normal # usage of `(Base.)print` in other files module Printer include("print.jl") end end # https://github.com/JuliaLang/julia/issues/36605 readstring(f::AbstractString) = isfile(f) ? read(f, String) : error(repr(f), ": No such file") """ Parser() Constructor for a TOML `Parser`. Note that in most cases one does not need to explicitly create a `Parser` but instead one directly use use [`TOML.parsefile`](@ref) or [`TOML.parse`](@ref). Using an explicit parser will however reuse some internal data structures which can be beneficial for performance if a larger number of small files are parsed. """ const Parser = Internals.Parser """ parsefile(f::AbstractString) parsefile(p::Parser, f::AbstractString) Parse file `f` and return the resulting table (dictionary). Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparsefile`](@ref). """ parsefile(f::AbstractString) = Internals.parse(Parser(readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) """ tryparsefile(f::AbstractString) tryparsefile(p::Parser, f::AbstractString) Parse file `f` and return the resulting table (dictionary). Return a [`ParserError`](@ref) upon failure. See also [`TOML.parsefile`](@ref). """ tryparsefile(f::AbstractString) = Internals.tryparse(Parser(readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) """ parse(x::Union{AbstractString, IO}) parse(p::Parser, x::Union{AbstractString, IO}) Parse the string or stream `x`, and return the resulting table (dictionary). Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ parse(str::AbstractString) = Internals.parse(Parser(String(str))) parse(p::Parser, str::AbstractString) = Internals.parse(Internals.reinit!(p, String(str))) parse(io::IO) = parse(read(io, String)) parse(p::Parser, io::IO) = parse(p, read(io, String)) """ tryparse(x::Union{AbstractString, IO}) tryparse(p::Parser, x::Union{AbstractString, IO}) Parse the string or stream `x`, and return the resulting table (dictionary). Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ tryparse(str::AbstractString) = Internals.tryparse(Parser(String(str))) tryparse(p::Parser, str::AbstractString) = Internals.tryparse(Internals.reinit!(p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) tryparse(p::Parser, io::IO) = tryparse(p, read(io, String)) """ ParserError Type that is returned from [`tryparse`](@ref) and [`tryparsefile`](@ref) when parsing fails. It contains (among others) the following fields: - `pos`, the position in the string when the error happened - `table`, the result that so far was successfully parsed - `type`, an error type, different for different types of errors """ const ParserError = Internals.ParserError """ print([to_toml::Function], io::IO [=stdout], data::AbstractDict; sorted=false, by=identity) Write `data` as TOML syntax to the stream `io`. If the keyword argument `sorted` is set to `true`, sort tables according to the function given by the keyword argument `by`. The following data types are supported: `AbstractDict`, `AbstractVector`, `AbstractString`, `Integer`, `AbstractFloat`, `Bool`, `Dates.DateTime`, `Dates.Time`, `Dates.Date`. Note that the integers and floats need to be convertible to `Float64` and `Int64` respectively. For other data types, pass the function `to_toml` that takes the data types and returns a value of a supported type. """ const print = Internals.Printer.print end m���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/TOML/src/print.jl๖������# This file is a part of Julia. License is MIT: https://julialang.org/license import Dates import Base: @invokelatest import ..isvalid_barekey_char function print_toml_escaped(io::IO, s::AbstractString) for c::AbstractChar in s if !isvalid(c) error("TOML print: invalid character $(repr(c)) encountered when printing string") end if c == '\b' Base.print(io, '\\', 'b') elseif c == '\t' Base.print(io, '\\', 't') elseif c == '\n' Base.print(io, '\\', 'n') elseif c == '\f' Base.print(io, '\\', 'f') elseif c == '\r' Base.print(io, '\\', 'r') elseif c == '"' Base.print(io, '\\', '"') elseif c == '\\' Base.print(io, "\\", '\\') elseif Base.iscntrl(c) Base.print(io, "\\u") Base.print(io, string(UInt32(c), base=16, pad=4)) else Base.print(io, c) end end end const MbyFunc = Union{Function, Nothing} const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString} ######## # Keys # ######## function printkey(io::IO, keys::Vector{String}) for (i, k) in enumerate(keys) i != 1 && Base.print(io, ".") if length(k) == 0 # empty key Base.print(io, "\"\"") elseif any(!isvalid_barekey_char, k) # quoted key Base.print(io, "\"") print_toml_escaped(io, k) Base.print(io, "\"") else Base.print(io, k) end end end function to_toml_value(f::MbyFunc, value) if f === nothing error("type `$(typeof(value))` is not a valid TOML type, pass a conversion function to `TOML.print`") end toml_value = f(value) if !(toml_value isa TOMLValue) error("TOML syntax function for type `$(typeof(value))` did not return a valid TOML type but a `$(typeof(toml_value))`") end return toml_value end ########## # Values # ########## # Fallback function printvalue(f::MbyFunc, io::IO, value) toml_value = to_toml_value(f, value) @invokelatest printvalue(f, io, toml_value) end function printvalue(f::MbyFunc, io::IO, value::AbstractVector) Base.print(io, "[") for (i, x) in enumerate(value) i != 1 && Base.print(io, ", ") printvalue(f, io, x) end Base.print(io, "]") end function printvalue(f::MbyFunc, io::IO, value::TOMLValue) value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) : value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) : value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) : value isa Bool ? Base.print(io, value ? "true" : "false") : value isa Integer ? print_integer(io, value) : # Julia's own printing should be compatible with TOML on integers value isa AbstractFloat ? Base.print(io, isnan(value) ? "nan" : isinf(value) ? string(value > 0 ? "+" : "-", "inf") : Float64(value)) : # TOML specifies IEEE 754 binary64 for float value isa AbstractString ? (Base.print(io, "\""); print_toml_escaped(io, value); Base.print(io, "\"")) : value isa AbstractDict ? print_inline_table(f, io, value) : error("internal error in TOML printing, unhandled value") end function print_integer(io::IO, value::Integer) value isa Signed && return Base.show(io, value) # unsigned integers are printed as hex n = 2 * ndigits(value, base=256) Base.print(io, "0x", string(value, base=16, pad=n)) return end function print_inline_table(f::MbyFunc, io::IO, value::AbstractDict) Base.print(io, "{") for (i, (k,v)) in enumerate(value) i != 1 && Base.print(io, ", ") printkey(io, [String(k)]) Base.print(io, " = ") printvalue(f, io, v) end Base.print(io, "}") end ########## # Tables # ########## is_table(value) = isa(value, AbstractDict) is_array_of_tables(value) = isa(value, AbstractArray) && length(value) > 0 && ( isa(value, AbstractArray{<:AbstractDict}) || all(v -> isa(v, AbstractDict), value) ) is_tabular(value) = is_table(value) || @invokelatest(is_array_of_tables(value)) function print_table(f::MbyFunc, io::IO, a::AbstractDict, ks::Vector{String} = String[]; indent::Int = 0, first_block::Bool = true, sorted::Bool = false, by::Function = identity, ) akeys = keys(a) if sorted akeys = sort!(collect(akeys); by=by) end # First print non-tabular entries for key in akeys value = a[key] if !isa(value, TOMLValue) value = to_toml_value(f, value) end is_tabular(value) && continue Base.print(io, ' '^4max(0,indent-1)) printkey(io, [String(key)]) Base.print(io, " = ") # print separator printvalue(f, io, value) Base.print(io, "\n") # new line? first_block = false end for key in akeys value = a[key] if !isa(value, TOMLValue) value = to_toml_value(f, value) end if is_table(value) push!(ks, String(key)) _values = @invokelatest values(value) header = isempty(value) || !all(is_tabular(v) for v in _values)::Bool if header # print table first_block || println(io) first_block = false Base.print(io, ' '^4indent) Base.print(io,"[") printkey(io, ks) Base.print(io,"]\n") end # Use runtime dispatch here since the type of value seems not to be enforced other than as AbstractDict @invokelatest print_table(f, io, value, ks; indent = indent + header, first_block = header, sorted=sorted, by=by) pop!(ks) elseif @invokelatest(is_array_of_tables(value)) # print array of tables first_block || println(io) first_block = false push!(ks, String(key)) for v in value Base.print(io, ' '^4indent) Base.print(io,"[[") printkey(io, ks) Base.print(io,"]]\n") # TODO, nicer error here !isa(v, AbstractDict) && error("array should contain only tables") @invokelatest print_table(f, io, v, ks; indent = indent + 1, sorted=sorted, by=by) end pop!(ks) end end end ####### # API # ####### print(f::MbyFunc, io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(f, io, a; sorted=sorted, by=by) print(f::MbyFunc, a::AbstractDict; sorted::Bool=false, by=identity) = print(f, stdout, a; sorted=sorted, by=by) print(io::IO, a::AbstractDict; sorted::Bool=false, by=identity) = print_table(nothing, io, a; sorted=sorted, by=by) print(a::AbstractDict; sorted::Bool=false, by=identity) = print(nothing, stdout, a; sorted=sorted, by=by) r���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/LibCURL.jl ������ module LibCURL using LibCURL_jll using MozillaCACerts_jll const time_t = Int const size_t = Csize_t const curl_off_t = Int64 const fd_set = Union{} const socklen_t = Int32 cacert = "" export Mime_ext function __init__() # Note: `MozillaCACerts_jll.cacert` is filled by `__init__` which requires LibCURL's # copy to also be filled in during initialization. Doing this ensures compatibility # with building system images. global cacert = MozillaCACerts_jll.cacert end include("lC_exports_h.jl") include("lC_common_h.jl") include("lC_curl_h.jl") # curl_easy_getinfo, curl_easy_setopt, and curl_multi_setopt are vararg C functions curl_easy_setopt(handle, opt, ptrval::Array{T}) where T = ccall((:curl_easy_setopt, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Ptr{T}...), handle, opt, ptrval) curl_easy_setopt(handle, opt, ptrval::Integer) = ccall((:curl_easy_setopt, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Clong...), handle, opt, ptrval) curl_easy_setopt(handle, opt, ptrval::Ptr{T}) where {T} = ccall((:curl_easy_setopt, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Ptr{T}...), handle, opt, ptrval) curl_easy_setopt(handle, opt, ptrval::AbstractString) = ccall((:curl_easy_setopt, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Ptr{UInt8}...), handle, opt, ptrval) curl_multi_setopt(handle, opt, ptrval::Array{T}) where T = ccall((:curl_multi_setopt, libcurl), CURLMcode, (Ptr{CURLM}, CURLMoption, Ptr{T}...), handle, opt, ptrval) curl_multi_setopt(handle, opt, ptrval::Integer) = ccall((:curl_multi_setopt, libcurl), CURLMcode, (Ptr{CURLM}, CURLMoption, Clong...), handle, opt, ptrval) curl_multi_setopt(handle, opt, ptrval::Ptr{T}) where {T} = ccall((:curl_multi_setopt, libcurl), CURLMcode, (Ptr{CURLM}, CURLMoption, Ptr{T}...), handle, opt, ptrval) curl_multi_setopt(handle, opt, ptrval::AbstractString) = ccall((:curl_multi_setopt, libcurl), CURLMcode, (Ptr{CURLM}, CURLMoption, Ptr{UInt8}...), handle, opt, ptrval) curl_easy_getinfo(handle, opt, ptrval::Array{T}) where T = ccall((:curl_easy_getinfo, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Ptr{T}...), handle, opt, ptrval) curl_easy_getinfo(handle, opt, ptrval::AbstractString) = ccall((:curl_easy_getinfo, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Ptr{UInt8}...), handle, opt, ptrval) include("lC_defines_h.jl") include("Mime_ext.jl") end z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL_jll/src/LibCURL_jll.jlQ������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/LibCURL_jll.jl baremodule LibCURL_jll using Base, Libdl, nghttp2_jll Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libcurl # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libcurl_handle::Ptr{Cvoid} = C_NULL libcurl_path::String = "" if Sys.iswindows() const libcurl = "libcurl-4.dll" elseif Sys.isapple() const libcurl = "@rpath/libcurl.4.dylib" else const libcurl = "libcurl.so.4" end function __init__() global libcurl_handle = dlopen(libcurl) global libcurl_path = dlpath(libcurl_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libcurl_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libcurl_path() = libcurl_path end # module LibCURL_jll z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/nghttp2_jll/src/nghttp2_jll.jlv������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/nghttp2_jll.jl baremodule nghttp2_jll using Base, Libdl Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export libnghttp2 # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" libnghttp2_handle::Ptr{Cvoid} = C_NULL libnghttp2_path::String = "" if Sys.iswindows() const libnghttp2 = "libnghttp2-14.dll" elseif Sys.isapple() const libnghttp2 = "@rpath/libnghttp2.14.dylib" else const libnghttp2 = "libnghttp2.so.14" end function __init__() global libnghttp2_handle = dlopen(libnghttp2) global libnghttp2_path = dlpath(libnghttp2_handle) global artifact_dir = dirname(Sys.BINDIR) LIBPATH[] = dirname(libnghttp2_path) push!(LIBPATH_list, LIBPATH[]) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing get_libnghttp2_path() = libnghttp2_path end # module nghttp2_jll ˆ���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/MozillaCACerts_jll/src/MozillaCACerts_jll.jl๔������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/MozillaCACerts_jll.jl baremodule MozillaCACerts_jll using Base Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") global artifact_dir::String = "" global cacert::String = "" function __init__() global artifact_dir = dirname(Sys.BINDIR) global cacert = normpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "cert.pem") end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing end # module w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/lC_exports_h.jlH†������# Generating exports export curl_strequal export curl_strnequal export curl_mime_init export curl_mime_free export curl_mime_addpart export curl_mime_name export curl_mime_filename export curl_mime_type export curl_mime_encoder export curl_mime_data export curl_mime_filedata export curl_mime_data_cb export curl_mime_subparts export curl_mime_headers export curl_formget export curl_formfree export curl_getenv export curl_version export curl_easy_escape export curl_escape export curl_easy_unescape export curl_unescape export curl_free export curl_global_init export curl_global_init_mem export curl_global_cleanup export curl_global_sslset export curl_slist_append export curl_slist_free_all export curl_getdate export curl_share_init export curl_share_setopt export curl_share_cleanup export curl_version_info export curl_easy_strerror export curl_share_strerror export curl_easy_pause export curl_easy_init export curl_easy_setopt export curl_easy_perform export curl_easy_cleanup export curl_easy_getinfo export curl_easy_duphandle export curl_easy_reset export curl_easy_recv export curl_easy_send export curl_multi_init export curl_multi_add_handle export curl_multi_remove_handle export curl_multi_fdset export curl_multi_wait export curl_multi_perform export curl_multi_cleanup export curl_multi_info_read export curl_multi_strerror export curl_multi_socket export curl_multi_socket_action export curl_multi_socket_all export curl_multi_timeout export curl_multi_setopt export curl_multi_assign export curl_pushheader_bynum export curl_pushheader_byname export CURL export CURLSH export curl_socket_t export CURL_SOCKET_BAD export curl_sslbackend export CURLSSLBACKEND_NONE export CURLSSLBACKEND_OPENSSL export CURLSSLBACKEND_GNUTLS export CURLSSLBACKEND_NSS export CURLSSLBACKEND_OBSOLETE4 export CURLSSLBACKEND_GSKIT export CURLSSLBACKEND_POLARSSL export CURLSSLBACKEND_WOLFSSL export CURLSSLBACKEND_SCHANNEL export CURLSSLBACKEND_DARWINSSL export CURLSSLBACKEND_AXTLS export CURLSSLBACKEND_MBEDTLS export CURLSSLBACKEND_LIBRESSL export CURLSSLBACKEND_BORINGSSL export CURLSSLBACKEND_CYASSL export CURL_HTTPPOST_FILENAME export CURL_HTTPPOST_READFILE export CURL_HTTPPOST_PTRNAME export CURL_HTTPPOST_PTRCONTENTS export CURL_HTTPPOST_BUFFER export CURL_HTTPPOST_PTRBUFFER export CURL_HTTPPOST_CALLBACK export CURL_HTTPPOST_LARGE export curl_progress_callback export curl_xferinfo_callback export CURL_MAX_READ_SIZE export CURL_MAX_WRITE_SIZE export CURL_MAX_HTTP_HEADER export CURL_WRITEFUNC_PAUSE export curl_write_callback export curl_resolver_start_callback export CURLFINFOFLAG_KNOWN_FILENAME export CURLFINFOFLAG_KNOWN_FILETYPE export CURLFINFOFLAG_KNOWN_TIME export CURLFINFOFLAG_KNOWN_PERM export CURLFINFOFLAG_KNOWN_UID export CURLFINFOFLAG_KNOWN_GID export CURLFINFOFLAG_KNOWN_SIZE export CURLFINFOFLAG_KNOWN_HLINKCOUNT export CURL_CHUNK_BGN_FUNC_OK export CURL_CHUNK_BGN_FUNC_FAIL export CURL_CHUNK_BGN_FUNC_SKIP export CURL_CHUNK_END_FUNC_OK export CURL_CHUNK_END_FUNC_FAIL export CURL_FNMATCHFUNC_MATCH export CURL_FNMATCHFUNC_NOMATCH export CURL_FNMATCHFUNC_FAIL export CURL_SEEKFUNC_OK export CURL_SEEKFUNC_FAIL export CURL_SEEKFUNC_CANTSEEK export CURL_READFUNC_ABORT export CURL_READFUNC_PAUSE export CURL_SOCKOPT_OK export CURL_SOCKOPT_ERROR export CURL_SOCKOPT_ALREADY_CONNECTED export curlfiletype export CURLFILETYPE_FILE export CURLFILETYPE_DIRECTORY export CURLFILETYPE_SYMLINK export CURLFILETYPE_DEVICE_BLOCK export CURLFILETYPE_DEVICE_CHAR export CURLFILETYPE_NAMEDPIPE export CURLFILETYPE_SOCKET export CURLFILETYPE_DOOR export CURLFILETYPE_UNKNOWN export curl_chunk_bgn_callback export curl_chunk_end_callback export curl_fnmatch_callback export curl_seek_callback export curl_read_callback export curlsocktype export CURLSOCKTYPE_IPCXN export CURLSOCKTYPE_ACCEPT export CURLSOCKTYPE_LAST export curl_sockopt_callback export curl_opensocket_callback export curl_closesocket_callback export curlioerr export CURLIOE_OK export CURLIOE_UNKNOWNCMD export CURLIOE_FAILRESTART export CURLIOE_LAST export curliocmd export CURLIOCMD_NOP export CURLIOCMD_RESTARTREAD export CURLIOCMD_LAST export curl_ioctl_callback export curl_malloc_callback export curl_free_callback export curl_realloc_callback export curl_strdup_callback export curl_calloc_callback export curl_infotype export CURLINFO_TEXT export CURLINFO_HEADER_IN export CURLINFO_HEADER_OUT export CURLINFO_DATA_IN export CURLINFO_DATA_OUT export CURLINFO_SSL_DATA_IN export CURLINFO_SSL_DATA_OUT export CURLINFO_END export curl_debug_callback export CURLcode export CURLE_OK export CURLE_UNSUPPORTED_PROTOCOL export CURLE_FAILED_INIT export CURLE_URL_MALFORMAT export CURLE_NOT_BUILT_IN export CURLE_COULDNT_RESOLVE_PROXY export CURLE_COULDNT_RESOLVE_HOST export CURLE_COULDNT_CONNECT export CURLE_WEIRD_SERVER_REPLY export CURLE_REMOTE_ACCESS_DENIED export CURLE_FTP_ACCEPT_FAILED export CURLE_FTP_WEIRD_PASS_REPLY export CURLE_FTP_ACCEPT_TIMEOUT export CURLE_FTP_WEIRD_PASV_REPLY export CURLE_FTP_WEIRD_227_FORMAT export CURLE_FTP_CANT_GET_HOST export CURLE_HTTP2 export CURLE_FTP_COULDNT_SET_TYPE export CURLE_PARTIAL_FILE export CURLE_FTP_COULDNT_RETR_FILE export CURLE_OBSOLETE20 export CURLE_QUOTE_ERROR export CURLE_HTTP_RETURNED_ERROR export CURLE_WRITE_ERROR export CURLE_OBSOLETE24 export CURLE_UPLOAD_FAILED export CURLE_READ_ERROR export CURLE_OUT_OF_MEMORY export CURLE_OPERATION_TIMEDOUT export CURLE_OBSOLETE29 export CURLE_FTP_PORT_FAILED export CURLE_FTP_COULDNT_USE_REST export CURLE_OBSOLETE32 export CURLE_RANGE_ERROR export CURLE_HTTP_POST_ERROR export CURLE_SSL_CONNECT_ERROR export CURLE_BAD_DOWNLOAD_RESUME export CURLE_FILE_COULDNT_READ_FILE export CURLE_LDAP_CANNOT_BIND export CURLE_LDAP_SEARCH_FAILED export CURLE_OBSOLETE40 export CURLE_FUNCTION_NOT_FOUND export CURLE_ABORTED_BY_CALLBACK export CURLE_BAD_FUNCTION_ARGUMENT export CURLE_OBSOLETE44 export CURLE_INTERFACE_FAILED export CURLE_OBSOLETE46 export CURLE_TOO_MANY_REDIRECTS export CURLE_UNKNOWN_OPTION export CURLE_TELNET_OPTION_SYNTAX export CURLE_OBSOLETE50 export CURLE_PEER_FAILED_VERIFICATION export CURLE_GOT_NOTHING export CURLE_SSL_ENGINE_NOTFOUND export CURLE_SSL_ENGINE_SETFAILED export CURLE_SEND_ERROR export CURLE_RECV_ERROR export CURLE_OBSOLETE57 export CURLE_SSL_CERTPROBLEM export CURLE_SSL_CIPHER export CURLE_SSL_CACERT export CURLE_BAD_CONTENT_ENCODING export CURLE_LDAP_INVALID_URL export CURLE_FILESIZE_EXCEEDED export CURLE_USE_SSL_FAILED export CURLE_SEND_FAIL_REWIND export CURLE_SSL_ENGINE_INITFAILED export CURLE_LOGIN_DENIED export CURLE_TFTP_NOTFOUND export CURLE_TFTP_PERM export CURLE_REMOTE_DISK_FULL export CURLE_TFTP_ILLEGAL export CURLE_TFTP_UNKNOWNID export CURLE_REMOTE_FILE_EXISTS export CURLE_TFTP_NOSUCHUSER export CURLE_CONV_FAILED export CURLE_CONV_REQD export CURLE_SSL_CACERT_BADFILE export CURLE_REMOTE_FILE_NOT_FOUND export CURLE_SSH export CURLE_SSL_SHUTDOWN_FAILED export CURLE_AGAIN export CURLE_SSL_CRL_BADFILE export CURLE_SSL_ISSUER_ERROR export CURLE_FTP_PRET_FAILED export CURLE_RTSP_CSEQ_ERROR export CURLE_RTSP_SESSION_ERROR export CURLE_FTP_BAD_FILE_LIST export CURLE_CHUNK_FAILED export CURLE_NO_CONNECTION_AVAILABLE export CURLE_SSL_PINNEDPUBKEYNOTMATCH export CURLE_SSL_INVALIDCERTSTATUS export CURLE_HTTP2_STREAM export CURLE_RECURSIVE_API_CALL export CURL_LAST export curl_conv_callback export curl_ssl_ctx_callback export curl_proxytype export CURLPROXY_HTTP export CURLPROXY_HTTP_1_0 export CURLPROXY_HTTPS export CURLPROXY_SOCKS4 export CURLPROXY_SOCKS5 export CURLPROXY_SOCKS4A export CURLPROXY_SOCKS5_HOSTNAME export CURLSSH_AUTH_ANY export CURLSSH_AUTH_NONE export CURLSSH_AUTH_PUBLICKEY export CURLSSH_AUTH_PASSWORD export CURLSSH_AUTH_HOST export CURLSSH_AUTH_KEYBOARD export CURLSSH_AUTH_AGENT export CURLSSH_AUTH_GSSAPI export CURLSSH_AUTH_DEFAULT export CURLGSSAPI_DELEGATION_NONE export CURLGSSAPI_DELEGATION_POLICY_FLAG export CURLGSSAPI_DELEGATION_FLAG export CURL_ERROR_SIZE export curl_khtype export CURLKHTYPE_UNKNOWN export CURLKHTYPE_RSA1 export CURLKHTYPE_RSA export CURLKHTYPE_DSS export CURLKHTYPE_ECDSA export CURLKHTYPE_ED25519 export curl_khstat export CURLKHSTAT_FINE_ADD_TO_FILE export CURLKHSTAT_FINE export CURLKHSTAT_REJECT export CURLKHSTAT_DEFER export CURLKHSTAT_LAST export curl_khmatch export CURLKHMATCH_OK export CURLKHMATCH_MISMATCH export CURLKHMATCH_MISSING export CURLKHMATCH_LAST export curl_sshkeycallback export curl_usessl export CURLUSESSL_NONE export CURLUSESSL_TRY export CURLUSESSL_CONTROL export CURLUSESSL_ALL export CURLUSESSL_LAST export CURLSSLOPT_ALLOW_BEAST export CURLSSLOPT_NO_REVOKE export CURL_HET_DEFAULT export curl_ftpssl export CURLFTPSSL_NONE export CURLFTPSSL_TRY export CURLFTPSSL_CONTROL export CURLFTPSSL_ALL export CURLFTPSSL_LAST export curl_ftpccc export CURLFTPSSL_CCC_NONE export CURLFTPSSL_CCC_PASSIVE export CURLFTPSSL_CCC_ACTIVE export CURLFTPSSL_CCC_LAST export curl_ftpauth export CURLFTPAUTH_DEFAULT export CURLFTPAUTH_SSL export CURLFTPAUTH_TLS export CURLFTPAUTH_LAST export curl_ftpcreatedir export CURLFTP_CREATE_DIR_NONE export CURLFTP_CREATE_DIR export CURLFTP_CREATE_DIR_RETRY export CURLFTP_CREATE_DIR_LAST export curl_ftpmethod export CURLFTPMETHOD_DEFAULT export CURLFTPMETHOD_MULTICWD export CURLFTPMETHOD_NOCWD export CURLFTPMETHOD_SINGLECWD export CURLFTPMETHOD_LAST export CURLHEADER_UNIFIED export CURLHEADER_SEPARATE export CURLPROTO_HTTP export CURLPROTO_HTTPS export CURLPROTO_FTP export CURLPROTO_FTPS export CURLPROTO_SCP export CURLPROTO_SFTP export CURLPROTO_TELNET export CURLPROTO_LDAP export CURLPROTO_LDAPS export CURLPROTO_DICT export CURLPROTO_FILE export CURLPROTO_TFTP export CURLPROTO_IMAP export CURLPROTO_IMAPS export CURLPROTO_POP3 export CURLPROTO_POP3S export CURLPROTO_SMTP export CURLPROTO_SMTPS export CURLPROTO_RTSP export CURLPROTO_RTMP export CURLPROTO_RTMPT export CURLPROTO_RTMPE export CURLPROTO_RTMPTE export CURLPROTO_RTMPS export CURLPROTO_RTMPTS export CURLPROTO_GOPHER export CURLPROTO_SMB export CURLPROTO_SMBS export CURLPROTO_ALL export CURLOPTTYPE_LONG export CURLOPTTYPE_OBJECTPOINT export CURLOPTTYPE_STRINGPOINT export CURLOPTTYPE_FUNCTIONPOINT export CURLOPTTYPE_OFF_T export CURLoption export CURLOPT_WRITEDATA export CURLOPT_URL export CURLOPT_PORT export CURLOPT_PROXY export CURLOPT_USERPWD export CURLOPT_PROXYUSERPWD export CURLOPT_RANGE export CURLOPT_READDATA export CURLOPT_ERRORBUFFER export CURLOPT_WRITEFUNCTION export CURLOPT_READFUNCTION export CURLOPT_TIMEOUT export CURLOPT_INFILESIZE export CURLOPT_POSTFIELDS export CURLOPT_REFERER export CURLOPT_FTPPORT export CURLOPT_USERAGENT export CURLOPT_LOW_SPEED_LIMIT export CURLOPT_LOW_SPEED_TIME export CURLOPT_RESUME_FROM export CURLOPT_COOKIE export CURLOPT_HTTPHEADER export CURLOPT_HTTPPOST export CURLOPT_SSLCERT export CURLOPT_KEYPASSWD export CURLOPT_CRLF export CURLOPT_QUOTE export CURLOPT_HEADERDATA export CURLOPT_COOKIEFILE export CURLOPT_SSLVERSION export CURLOPT_TIMECONDITION export CURLOPT_TIMEVALUE export CURLOPT_CUSTOMREQUEST export CURLOPT_STDERR export CURLOPT_POSTQUOTE export CURLOPT_OBSOLETE40 export CURLOPT_VERBOSE export CURLOPT_HEADER export CURLOPT_NOPROGRESS export CURLOPT_NOBODY export CURLOPT_FAILONERROR export CURLOPT_UPLOAD export CURLOPT_POST export CURLOPT_DIRLISTONLY export CURLOPT_APPEND export CURLOPT_NETRC export CURLOPT_FOLLOWLOCATION export CURLOPT_TRANSFERTEXT export CURLOPT_PUT export CURLOPT_PROGRESSFUNCTION export CURLOPT_PROGRESSDATA export CURLOPT_AUTOREFERER export CURLOPT_PROXYPORT export CURLOPT_POSTFIELDSIZE export CURLOPT_HTTPPROXYTUNNEL export CURLOPT_INTERFACE export CURLOPT_KRBLEVEL export CURLOPT_SSL_VERIFYPEER export CURLOPT_CAINFO export CURLOPT_MAXREDIRS export CURLOPT_FILETIME export CURLOPT_TELNETOPTIONS export CURLOPT_MAXCONNECTS export CURLOPT_OBSOLETE72 export CURLOPT_FRESH_CONNECT export CURLOPT_FORBID_REUSE export CURLOPT_RANDOM_FILE export CURLOPT_EGDSOCKET export CURLOPT_CONNECTTIMEOUT export CURLOPT_HEADERFUNCTION export CURLOPT_HTTPGET export CURLOPT_SSL_VERIFYHOST export CURLOPT_COOKIEJAR export CURLOPT_SSL_CIPHER_LIST export CURLOPT_HTTP_VERSION export CURLOPT_FTP_USE_EPSV export CURLOPT_SSLCERTTYPE export CURLOPT_SSLKEY export CURLOPT_SSLKEYTYPE export CURLOPT_SSLENGINE export CURLOPT_SSLENGINE_DEFAULT export CURLOPT_DNS_USE_GLOBAL_CACHE export CURLOPT_DNS_CACHE_TIMEOUT export CURLOPT_PREQUOTE export CURLOPT_DEBUGFUNCTION export CURLOPT_DEBUGDATA export CURLOPT_COOKIESESSION export CURLOPT_CAPATH export CURLOPT_BUFFERSIZE export CURLOPT_NOSIGNAL export CURLOPT_SHARE export CURLOPT_PROXYTYPE export CURLOPT_ACCEPT_ENCODING export CURLOPT_PRIVATE export CURLOPT_HTTP200ALIASES export CURLOPT_UNRESTRICTED_AUTH export CURLOPT_FTP_USE_EPRT export CURLOPT_HTTPAUTH export CURLOPT_SSL_CTX_FUNCTION export CURLOPT_SSL_CTX_DATA export CURLOPT_FTP_CREATE_MISSING_DIRS export CURLOPT_PROXYAUTH export CURLOPT_FTP_RESPONSE_TIMEOUT export CURLOPT_IPRESOLVE export CURLOPT_MAXFILESIZE export CURLOPT_INFILESIZE_LARGE export CURLOPT_RESUME_FROM_LARGE export CURLOPT_MAXFILESIZE_LARGE export CURLOPT_NETRC_FILE export CURLOPT_USE_SSL export CURLOPT_POSTFIELDSIZE_LARGE export CURLOPT_TCP_NODELAY export CURLOPT_FTPSSLAUTH export CURLOPT_IOCTLFUNCTION export CURLOPT_IOCTLDATA export CURLOPT_FTP_ACCOUNT export CURLOPT_COOKIELIST export CURLOPT_IGNORE_CONTENT_LENGTH export CURLOPT_FTP_SKIP_PASV_IP export CURLOPT_FTP_FILEMETHOD export CURLOPT_LOCALPORT export CURLOPT_LOCALPORTRANGE export CURLOPT_CONNECT_ONLY export CURLOPT_CONV_FROM_NETWORK_FUNCTION export CURLOPT_CONV_TO_NETWORK_FUNCTION export CURLOPT_CONV_FROM_UTF8_FUNCTION export CURLOPT_MAX_SEND_SPEED_LARGE export CURLOPT_MAX_RECV_SPEED_LARGE export CURLOPT_FTP_ALTERNATIVE_TO_USER export CURLOPT_SOCKOPTFUNCTION export CURLOPT_SOCKOPTDATA export CURLOPT_SSL_SESSIONID_CACHE export CURLOPT_SSH_AUTH_TYPES export CURLOPT_SSH_PUBLIC_KEYFILE export CURLOPT_SSH_PRIVATE_KEYFILE export CURLOPT_FTP_SSL_CCC export CURLOPT_TIMEOUT_MS export CURLOPT_CONNECTTIMEOUT_MS export CURLOPT_HTTP_TRANSFER_DECODING export CURLOPT_HTTP_CONTENT_DECODING export CURLOPT_NEW_FILE_PERMS export CURLOPT_NEW_DIRECTORY_PERMS export CURLOPT_POSTREDIR export CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 export CURLOPT_OPENSOCKETFUNCTION export CURLOPT_OPENSOCKETDATA export CURLOPT_COPYPOSTFIELDS export CURLOPT_PROXY_TRANSFER_MODE export CURLOPT_SEEKFUNCTION export CURLOPT_SEEKDATA export CURLOPT_CRLFILE export CURLOPT_ISSUERCERT export CURLOPT_ADDRESS_SCOPE export CURLOPT_CERTINFO export CURLOPT_USERNAME export CURLOPT_PASSWORD export CURLOPT_PROXYUSERNAME export CURLOPT_PROXYPASSWORD export CURLOPT_NOPROXY export CURLOPT_TFTP_BLKSIZE export CURLOPT_SOCKS5_GSSAPI_SERVICE export CURLOPT_SOCKS5_GSSAPI_NEC export CURLOPT_PROTOCOLS export CURLOPT_REDIR_PROTOCOLS export CURLOPT_SSH_KNOWNHOSTS export CURLOPT_SSH_KEYFUNCTION export CURLOPT_SSH_KEYDATA export CURLOPT_MAIL_FROM export CURLOPT_MAIL_RCPT export CURLOPT_FTP_USE_PRET export CURLOPT_RTSP_REQUEST export CURLOPT_RTSP_SESSION_ID export CURLOPT_RTSP_STREAM_URI export CURLOPT_RTSP_TRANSPORT export CURLOPT_RTSP_CLIENT_CSEQ export CURLOPT_RTSP_SERVER_CSEQ export CURLOPT_INTERLEAVEDATA export CURLOPT_INTERLEAVEFUNCTION export CURLOPT_WILDCARDMATCH export CURLOPT_CHUNK_BGN_FUNCTION export CURLOPT_CHUNK_END_FUNCTION export CURLOPT_FNMATCH_FUNCTION export CURLOPT_CHUNK_DATA export CURLOPT_FNMATCH_DATA export CURLOPT_RESOLVE export CURLOPT_TLSAUTH_USERNAME export CURLOPT_TLSAUTH_PASSWORD export CURLOPT_TLSAUTH_TYPE export CURLOPT_TRANSFER_ENCODING export CURLOPT_CLOSESOCKETFUNCTION export CURLOPT_CLOSESOCKETDATA export CURLOPT_GSSAPI_DELEGATION export CURLOPT_DNS_SERVERS export CURLOPT_ACCEPTTIMEOUT_MS export CURLOPT_TCP_KEEPALIVE export CURLOPT_TCP_KEEPIDLE export CURLOPT_TCP_KEEPINTVL export CURLOPT_SSL_OPTIONS export CURLOPT_MAIL_AUTH export CURLOPT_SASL_IR export CURLOPT_XFERINFOFUNCTION export CURLOPT_XOAUTH2_BEARER export CURLOPT_DNS_INTERFACE export CURLOPT_DNS_LOCAL_IP4 export CURLOPT_DNS_LOCAL_IP6 export CURLOPT_LOGIN_OPTIONS export CURLOPT_SSL_ENABLE_NPN export CURLOPT_SSL_ENABLE_ALPN export CURLOPT_EXPECT_100_TIMEOUT_MS export CURLOPT_PROXYHEADER export CURLOPT_HEADEROPT export CURLOPT_PINNEDPUBLICKEY export CURLOPT_UNIX_SOCKET_PATH export CURLOPT_SSL_VERIFYSTATUS export CURLOPT_SSL_FALSESTART export CURLOPT_PATH_AS_IS export CURLOPT_PROXY_SERVICE_NAME export CURLOPT_SERVICE_NAME export CURLOPT_PIPEWAIT export CURLOPT_DEFAULT_PROTOCOL export CURLOPT_STREAM_WEIGHT export CURLOPT_STREAM_DEPENDS export CURLOPT_STREAM_DEPENDS_E export CURLOPT_TFTP_NO_OPTIONS export CURLOPT_CONNECT_TO export CURLOPT_TCP_FASTOPEN export CURLOPT_KEEP_SENDING_ON_ERROR export CURLOPT_PROXY_CAINFO export CURLOPT_PROXY_CAPATH export CURLOPT_PROXY_SSL_VERIFYPEER export CURLOPT_PROXY_SSL_VERIFYHOST export CURLOPT_PROXY_SSLVERSION export CURLOPT_PROXY_TLSAUTH_USERNAME export CURLOPT_PROXY_TLSAUTH_PASSWORD export CURLOPT_PROXY_TLSAUTH_TYPE export CURLOPT_PROXY_SSLCERT export CURLOPT_PROXY_SSLCERTTYPE export CURLOPT_PROXY_SSLKEY export CURLOPT_PROXY_SSLKEYTYPE export CURLOPT_PROXY_KEYPASSWD export CURLOPT_PROXY_SSL_CIPHER_LIST export CURLOPT_PROXY_CRLFILE export CURLOPT_PROXY_SSL_OPTIONS export CURLOPT_PRE_PROXY export CURLOPT_PROXY_PINNEDPUBLICKEY export CURLOPT_ABSTRACT_UNIX_SOCKET export CURLOPT_SUPPRESS_CONNECT_HEADERS export CURLOPT_REQUEST_TARGET export CURLOPT_SOCKS5_AUTH export CURLOPT_SSH_COMPRESSION export CURLOPT_MIMEPOST export CURLOPT_TIMEVALUE_LARGE export CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS export CURLOPT_RESOLVER_START_FUNCTION export CURLOPT_RESOLVER_START_DATA export CURLOPT_HAPROXYPROTOCOL export CURLOPT_DNS_SHUFFLE_ADDRESSES export CURLOPT_TLS13_CIPHERS export CURLOPT_PROXY_TLS13_CIPHERS export CURLOPT_DISALLOW_USERNAME_IN_URL export CURLOPT_LASTENTRY export CURLOPT_XFERINFODATA export CURLOPT_SERVER_RESPONSE_TIMEOUT export CURLOPT_POST301 export CURLOPT_SSLKEYPASSWD export CURLOPT_FTPAPPEND export CURLOPT_FTPLISTONLY export CURLOPT_FTP_SSL export CURLOPT_SSLCERTPASSWD export CURLOPT_KRB4LEVEL export CURL_IPRESOLVE_WHATEVER export CURL_IPRESOLVE_V4 export CURL_IPRESOLVE_V6 export CURLOPT_RTSPHEADER export CURL_HTTP_VERSION_NONE export CURL_HTTP_VERSION_1_0 export CURL_HTTP_VERSION_1_1 export CURL_HTTP_VERSION_2_0 export CURL_HTTP_VERSION_2TLS export CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE export CURL_HTTP_VERSION_LAST export CURL_HTTP_VERSION_2 export CURL_RTSPREQ_NONE export CURL_RTSPREQ_OPTIONS export CURL_RTSPREQ_DESCRIBE export CURL_RTSPREQ_ANNOUNCE export CURL_RTSPREQ_SETUP export CURL_RTSPREQ_PLAY export CURL_RTSPREQ_PAUSE export CURL_RTSPREQ_TEARDOWN export CURL_RTSPREQ_GET_PARAMETER export CURL_RTSPREQ_SET_PARAMETER export CURL_RTSPREQ_RECORD export CURL_RTSPREQ_RECEIVE export CURL_RTSPREQ_LAST export CURL_NETRC_OPTION export CURL_NETRC_IGNORED export CURL_NETRC_OPTIONAL export CURL_NETRC_REQUIRED export CURL_NETRC_LAST export CURL_SSLVERSION_DEFAULT export CURL_SSLVERSION_TLSv1 export CURL_SSLVERSION_SSLv2 export CURL_SSLVERSION_SSLv3 export CURL_SSLVERSION_TLSv1_0 export CURL_SSLVERSION_TLSv1_1 export CURL_SSLVERSION_TLSv1_2 export CURL_SSLVERSION_TLSv1_3 export CURL_SSLVERSION_LAST export CURL_SSLVERSION_MAX_NONE export CURL_SSLVERSION_MAX_DEFAULT export CURL_SSLVERSION_MAX_TLSv1_0 export CURL_SSLVERSION_MAX_TLSv1_1 export CURL_SSLVERSION_MAX_TLSv1_2 export CURL_SSLVERSION_MAX_TLSv1_3 export CURL_SSLVERSION_MAX_LAST export CURL_TLSAUTH export CURL_TLSAUTH_NONE export CURL_TLSAUTH_SRP export CURL_TLSAUTH_LAST export CURL_REDIR_GET_ALL export CURL_REDIR_POST_301 export CURL_REDIR_POST_302 export CURL_REDIR_POST_303 export CURL_REDIR_POST_ALL export curl_TimeCond export CURL_TIMECOND_NONE export CURL_TIMECOND_IFMODSINCE export CURL_TIMECOND_IFUNMODSINCE export CURL_TIMECOND_LASTMOD export CURL_TIMECOND_LAST export curl_mime export curl_mimepart export CURLformoption export CURLFORM_NOTHING export CURLFORM_COPYNAME export CURLFORM_PTRNAME export CURLFORM_NAMELENGTH export CURLFORM_COPYCONTENTS export CURLFORM_PTRCONTENTS export CURLFORM_CONTENTSLENGTH export CURLFORM_FILECONTENT export CURLFORM_ARRAY export CURLFORM_OBSOLETE export CURLFORM_FILE export CURLFORM_BUFFER export CURLFORM_BUFFERPTR export CURLFORM_BUFFERLENGTH export CURLFORM_CONTENTTYPE export CURLFORM_CONTENTHEADER export CURLFORM_FILENAME export CURLFORM_END export CURLFORM_OBSOLETE2 export CURLFORM_STREAM export CURLFORM_CONTENTLEN export CURLFORM_LASTENTRY export CURLFORMcode export CURL_FORMADD_OK export CURL_FORMADD_MEMORY export CURL_FORMADD_OPTION_TWICE export CURL_FORMADD_NULL export CURL_FORMADD_UNKNOWN_OPTION export CURL_FORMADD_INCOMPLETE export CURL_FORMADD_ILLEGAL_ARRAY export CURL_FORMADD_DISABLED export CURL_FORMADD_LAST export curl_formget_callback export curl_ssl_backend export CURLsslset export CURLSSLSET_OK export CURLSSLSET_UNKNOWN_BACKEND export CURLSSLSET_TOO_LATE export CURLSSLSET_NO_BACKENDS export CURLINFO_STRING export CURLINFO_LONG export CURLINFO_DOUBLE export CURLINFO_SLIST export CURLINFO_PTR export CURLINFO_SOCKET export CURLINFO_OFF_T export CURLINFO_MASK export CURLINFO_TYPEMASK export CURLINFO export CURLINFO_NONE export CURLINFO_EFFECTIVE_URL export CURLINFO_RESPONSE_CODE export CURLINFO_TOTAL_TIME export CURLINFO_NAMELOOKUP_TIME export CURLINFO_CONNECT_TIME export CURLINFO_PRETRANSFER_TIME export CURLINFO_SIZE_UPLOAD export CURLINFO_SIZE_UPLOAD_T export CURLINFO_SIZE_DOWNLOAD export CURLINFO_SIZE_DOWNLOAD_T export CURLINFO_SPEED_DOWNLOAD export CURLINFO_SPEED_DOWNLOAD_T export CURLINFO_SPEED_UPLOAD export CURLINFO_SPEED_UPLOAD_T export CURLINFO_HEADER_SIZE export CURLINFO_REQUEST_SIZE export CURLINFO_SSL_VERIFYRESULT export CURLINFO_FILETIME export CURLINFO_FILETIME_T export CURLINFO_CONTENT_LENGTH_DOWNLOAD export CURLINFO_CONTENT_LENGTH_DOWNLOAD_T export CURLINFO_CONTENT_LENGTH_UPLOAD export CURLINFO_CONTENT_LENGTH_UPLOAD_T export CURLINFO_STARTTRANSFER_TIME export CURLINFO_CONTENT_TYPE export CURLINFO_REDIRECT_TIME export CURLINFO_REDIRECT_COUNT export CURLINFO_PRIVATE export CURLINFO_HTTP_CONNECTCODE export CURLINFO_HTTPAUTH_AVAIL export CURLINFO_PROXYAUTH_AVAIL export CURLINFO_OS_ERRNO export CURLINFO_NUM_CONNECTS export CURLINFO_SSL_ENGINES export CURLINFO_COOKIELIST export CURLINFO_LASTSOCKET export CURLINFO_FTP_ENTRY_PATH export CURLINFO_REDIRECT_URL export CURLINFO_PRIMARY_IP export CURLINFO_APPCONNECT_TIME export CURLINFO_CERTINFO export CURLINFO_CONDITION_UNMET export CURLINFO_RTSP_SESSION_ID export CURLINFO_RTSP_CLIENT_CSEQ export CURLINFO_RTSP_SERVER_CSEQ export CURLINFO_RTSP_CSEQ_RECV export CURLINFO_PRIMARY_PORT export CURLINFO_LOCAL_IP export CURLINFO_LOCAL_PORT export CURLINFO_TLS_SESSION export CURLINFO_ACTIVESOCKET export CURLINFO_TLS_SSL_PTR export CURLINFO_HTTP_VERSION export CURLINFO_PROXY_SSL_VERIFYRESULT export CURLINFO_PROTOCOL export CURLINFO_SCHEME export CURLINFO_TOTAL_TIME_T export CURLINFO_NAMELOOKUP_TIME_T export CURLINFO_CONNECT_TIME_T export CURLINFO_PRETRANSFER_TIME_T export CURLINFO_STARTTRANSFER_TIME_T export CURLINFO_REDIRECT_TIME_T export CURLINFO_APPCONNECT_TIME_T export CURLINFO_LASTONE export CURLINFO_HTTP_CODE export curl_closepolicy export CURLCLOSEPOLICY_NONE export CURLCLOSEPOLICY_OLDEST export CURLCLOSEPOLICY_LEAST_RECENTLY_USED export CURLCLOSEPOLICY_LEAST_TRAFFIC export CURLCLOSEPOLICY_SLOWEST export CURLCLOSEPOLICY_CALLBACK export CURLCLOSEPOLICY_LAST export CURL_GLOBAL_SSL export CURL_GLOBAL_WIN32 export CURL_GLOBAL_ALL export CURL_GLOBAL_NOTHING export CURL_GLOBAL_DEFAULT export CURL_GLOBAL_ACK_EINTR export curl_lock_data export CURL_LOCK_DATA_NONE export CURL_LOCK_DATA_SHARE export CURL_LOCK_DATA_COOKIE export CURL_LOCK_DATA_DNS export CURL_LOCK_DATA_SSL_SESSION export CURL_LOCK_DATA_CONNECT export CURL_LOCK_DATA_PSL export CURL_LOCK_DATA_LAST export curl_lock_access export CURL_LOCK_ACCESS_NONE export CURL_LOCK_ACCESS_SHARED export CURL_LOCK_ACCESS_SINGLE export CURL_LOCK_ACCESS_LAST export curl_lock_function export curl_unlock_function export CURLSHcode export CURLSHE_OK export CURLSHE_BAD_OPTION export CURLSHE_IN_USE export CURLSHE_INVALID export CURLSHE_NOMEM export CURLSHE_NOT_BUILT_IN export CURLSHE_LAST export CURLSHoption export CURLSHOPT_NONE export CURLSHOPT_SHARE export CURLSHOPT_UNSHARE export CURLSHOPT_LOCKFUNC export CURLSHOPT_UNLOCKFUNC export CURLSHOPT_USERDATA export CURLSHOPT_LAST export CURLversion export CURLVERSION_FIRST export CURLVERSION_SECOND export CURLVERSION_THIRD export CURLVERSION_FOURTH export CURLVERSION_FIFTH export CURLVERSION_LAST export CURLVERSION_NOW export CURL_VERSION_IPV6 export CURL_VERSION_KERBEROS4 export CURL_VERSION_SSL export CURL_VERSION_LIBZ export CURL_VERSION_NTLM export CURL_VERSION_GSSNEGOTIATE export CURL_VERSION_DEBUG export CURL_VERSION_ASYNCHDNS export CURL_VERSION_SPNEGO export CURL_VERSION_LARGEFILE export CURL_VERSION_IDN export CURL_VERSION_SSPI export CURL_VERSION_CONV export CURL_VERSION_CURLDEBUG export CURL_VERSION_TLSAUTH_SRP export CURL_VERSION_NTLM_WB export CURL_VERSION_HTTP2 export CURL_VERSION_GSSAPI export CURL_VERSION_KERBEROS5 export CURL_VERSION_UNIX_SOCKETS export CURL_VERSION_PSL export CURL_VERSION_HTTPS_PROXY export CURL_VERSION_MULTI_SSL export CURL_VERSION_BROTLI export CURLPAUSE_RECV export CURLPAUSE_RECV_CONT export CURLPAUSE_SEND export CURLPAUSE_SEND_CONT export CURLPAUSE_ALL export CURLPAUSE_CONT export CURLE_OBSOLETE16 export CURLE_OBSOLETE10 export CURLE_OBSOLETE12 export CURLOPT_ENCODING export CURLE_FTP_WEIRD_SERVER_REPLY export CURLE_UNKNOWN_TELNET_OPTION export CURLE_SSL_PEER_CERTIFICATE export CURLE_OBSOLETE export CURLE_BAD_PASSWORD_ENTERED export CURLE_BAD_CALLING_ORDER export CURLE_FTP_USER_PASSWORD_INCORRECT export CURLE_FTP_CANT_RECONNECT export CURLE_FTP_COULDNT_GET_SIZE export CURLE_FTP_COULDNT_SET_ASCII export CURLE_FTP_WEIRD_USER_REPLY export CURLE_FTP_WRITE_ERROR export CURLE_LIBRARY_NOT_FOUND export CURLE_MALFORMAT_USER export CURLE_SHARE_IN_USE export CURLE_URL_MALFORMAT_USER export CURLE_FTP_ACCESS_DENIED export CURLE_FTP_COULDNT_SET_BINARY export CURLE_FTP_QUOTE_ERROR export CURLE_TFTP_DISKFULL export CURLE_TFTP_EXISTS export CURLE_HTTP_RANGE_ERROR export CURLE_FTP_SSL_FAILED export CURLE_OPERATION_TIMEOUTED export CURLE_HTTP_NOT_FOUND export CURLE_HTTP_PORT_FAILED export CURLE_FTP_COULDNT_STOR_FILE export CURLE_FTP_PARTIAL_FILE export CURLE_FTP_BAD_DOWNLOAD_RESUME export CURLE_ALREADY_COMPLETE export CURLOPT_FILE export CURLOPT_INFILE export CURLOPT_WRITEHEADER export CURLOPT_WRITEINFO export CURLOPT_CLOSEPOLICY export CURLM export CURLMcode export CURLM_CALL_MULTI_PERFORM export CURLM_OK export CURLM_BAD_HANDLE export CURLM_BAD_EASY_HANDLE export CURLM_OUT_OF_MEMORY export CURLM_INTERNAL_ERROR export CURLM_BAD_SOCKET export CURLM_UNKNOWN_OPTION export CURLM_ADDED_ALREADY export CURLM_RECURSIVE_API_CALL export CURLM_LAST export CURLM_CALL_MULTI_SOCKET export CURLPIPE_NOTHING export CURLPIPE_HTTP1 export CURLPIPE_MULTIPLEX export CURLMSG export CURLMSG_NONE export CURLMSG_DONE export CURLMSG_LAST export CURL_WAIT_POLLIN export CURL_WAIT_POLLPRI export CURL_WAIT_POLLOUT export CURL_POLL_NONE export CURL_POLL_IN export CURL_POLL_OUT export CURL_POLL_INOUT export CURL_POLL_REMOVE export CURL_SOCKET_TIMEOUT export CURL_CSELECT_IN export CURL_CSELECT_OUT export CURL_CSELECT_ERR export curl_socket_callback export curl_multi_timer_callback export CURLMoption export CURLMOPT_SOCKETFUNCTION export CURLMOPT_SOCKETDATA export CURLMOPT_PIPELINING export CURLMOPT_TIMERFUNCTION export CURLMOPT_TIMERDATA export CURLMOPT_MAXCONNECTS export CURLMOPT_MAX_HOST_CONNECTIONS export CURLMOPT_MAX_PIPELINE_LENGTH export CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE export CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE export CURLMOPT_PIPELINING_SITE_BL export CURLMOPT_PIPELINING_SERVER_BL export CURLMOPT_MAX_TOTAL_CONNECTIONS export CURLMOPT_PUSHFUNCTION export CURLMOPT_PUSHDATA export CURLMOPT_LASTENTRY export CURL_PUSH_OK export CURL_PUSH_DENY export curl_push_callback export CURL_TYPEOF_CURL_OFF_T export CURL_FORMAT_CURL_OFF_T export CURL_FORMAT_CURL_OFF_TU export CURL_SUFFIX_CURL_OFF_T export CURL_SUFFIX_CURL_OFF_TU export CURL_TYPEOF_CURL_SOCKLEN_T export CURL_PULL_SYS_TYPES_H export CURL_PULL_SYS_SOCKET_H export CURL_SOCKET_BAD export CURLSSLBACKEND_LIBRESSL export CURLSSLBACKEND_BORINGSSL export CURLSSLBACKEND_CYASSL export CURL_HTTPPOST_FILENAME export CURL_HTTPPOST_READFILE export CURL_HTTPPOST_PTRNAME export CURL_HTTPPOST_PTRCONTENTS export CURL_HTTPPOST_BUFFER export CURL_HTTPPOST_PTRBUFFER export CURL_HTTPPOST_CALLBACK export CURL_HTTPPOST_LARGE export CURL_MAX_READ_SIZE export CURL_MAX_WRITE_SIZE export CURL_MAX_HTTP_HEADER export CURL_WRITEFUNC_PAUSE export CURLFINFOFLAG_KNOWN_FILENAME export CURLFINFOFLAG_KNOWN_FILETYPE export CURLFINFOFLAG_KNOWN_TIME export CURLFINFOFLAG_KNOWN_PERM export CURLFINFOFLAG_KNOWN_UID export CURLFINFOFLAG_KNOWN_GID export CURLFINFOFLAG_KNOWN_SIZE export CURLFINFOFLAG_KNOWN_HLINKCOUNT export CURL_CHUNK_BGN_FUNC_OK export CURL_CHUNK_BGN_FUNC_FAIL export CURL_CHUNK_BGN_FUNC_SKIP export CURL_CHUNK_END_FUNC_OK export CURL_CHUNK_END_FUNC_FAIL export CURL_FNMATCHFUNC_MATCH export CURL_FNMATCHFUNC_NOMATCH export CURL_FNMATCHFUNC_FAIL export CURL_SEEKFUNC_OK export CURL_SEEKFUNC_FAIL export CURL_SEEKFUNC_CANTSEEK export CURL_READFUNC_ABORT export CURL_READFUNC_PAUSE export CURL_SOCKOPT_OK export CURL_SOCKOPT_ERROR export CURL_SOCKOPT_ALREADY_CONNECTED export CURLE_OBSOLETE16 export CURLE_OBSOLETE10 export CURLE_OBSOLETE12 export CURLOPT_ENCODING export CURLE_FTP_WEIRD_SERVER_REPLY export CURLE_UNKNOWN_TELNET_OPTION export CURLE_SSL_PEER_CERTIFICATE export CURLE_OBSOLETE export CURLE_BAD_PASSWORD_ENTERED export CURLE_BAD_CALLING_ORDER export CURLE_FTP_USER_PASSWORD_INCORRECT export CURLE_FTP_CANT_RECONNECT export CURLE_FTP_COULDNT_GET_SIZE export CURLE_FTP_COULDNT_SET_ASCII export CURLE_FTP_WEIRD_USER_REPLY export CURLE_FTP_WRITE_ERROR export CURLE_LIBRARY_NOT_FOUND export CURLE_MALFORMAT_USER export CURLE_SHARE_IN_USE export CURLE_URL_MALFORMAT_USER export CURLE_FTP_ACCESS_DENIED export CURLE_FTP_COULDNT_SET_BINARY export CURLE_FTP_QUOTE_ERROR export CURLE_TFTP_DISKFULL export CURLE_TFTP_EXISTS export CURLE_HTTP_RANGE_ERROR export CURLE_FTP_SSL_FAILED export CURLE_OPERATION_TIMEOUTED export CURLE_HTTP_NOT_FOUND export CURLE_HTTP_PORT_FAILED export CURLE_FTP_COULDNT_STOR_FILE export CURLE_FTP_PARTIAL_FILE export CURLE_FTP_BAD_DOWNLOAD_RESUME export CURLE_ALREADY_COMPLETE export CURLOPT_FILE export CURLOPT_INFILE export CURLOPT_WRITEHEADER export CURLOPT_WRITEINFO export CURLOPT_CLOSEPOLICY export CURLAUTH_NONE export CURLAUTH_BASIC export CURLAUTH_DIGEST export CURLAUTH_NEGOTIATE export CURLAUTH_GSSNEGOTIATE export CURLAUTH_GSSAPI export CURLAUTH_NTLM export CURLAUTH_DIGEST_IE export CURLAUTH_NTLM_WB export CURLAUTH_BEARER export CURLAUTH_ONLY export CURLAUTH_ANY export CURLAUTH_ANYSAFE export CURLSSH_AUTH_ANY export CURLSSH_AUTH_NONE export CURLSSH_AUTH_PUBLICKEY export CURLSSH_AUTH_PASSWORD export CURLSSH_AUTH_HOST export CURLSSH_AUTH_KEYBOARD export CURLSSH_AUTH_AGENT export CURLSSH_AUTH_GSSAPI export CURLSSH_AUTH_DEFAULT export CURLGSSAPI_DELEGATION_NONE export CURLGSSAPI_DELEGATION_POLICY_FLAG export CURLGSSAPI_DELEGATION_FLAG export CURL_ERROR_SIZE export CURLSSLOPT_ALLOW_BEAST export CURLSSLOPT_NO_REVOKE export CURL_HET_DEFAULT export CURLFTPSSL_NONE export CURLFTPSSL_TRY export CURLFTPSSL_CONTROL export CURLFTPSSL_ALL export CURLFTPSSL_LAST export CURLHEADER_UNIFIED export CURLHEADER_SEPARATE export CURLPROTO_HTTP export CURLPROTO_HTTPS export CURLPROTO_FTP export CURLPROTO_FTPS export CURLPROTO_SCP export CURLPROTO_SFTP export CURLPROTO_TELNET export CURLPROTO_LDAP export CURLPROTO_LDAPS export CURLPROTO_DICT export CURLPROTO_FILE export CURLPROTO_TFTP export CURLPROTO_IMAP export CURLPROTO_IMAPS export CURLPROTO_POP3 export CURLPROTO_POP3S export CURLPROTO_SMTP export CURLPROTO_SMTPS export CURLPROTO_RTSP export CURLPROTO_RTMP export CURLPROTO_RTMPT export CURLPROTO_RTMPE export CURLPROTO_RTMPTE export CURLPROTO_RTMPS export CURLPROTO_RTMPTS export CURLPROTO_GOPHER export CURLPROTO_SMB export CURLPROTO_SMBS export CURLPROTO_ALL export CURLOPTTYPE_LONG export CURLOPTTYPE_OBJECTPOINT export CURLOPTTYPE_STRINGPOINT export CURLOPTTYPE_FUNCTIONPOINT export CURLOPTTYPE_OFF_T export CURLOPT_XFERINFODATA export CURLOPT_SERVER_RESPONSE_TIMEOUT export CURLOPT_POST301 export CURLOPT_SSLKEYPASSWD export CURLOPT_FTPAPPEND export CURLOPT_FTPLISTONLY export CURLOPT_FTP_SSL export CURLOPT_SSLCERTPASSWD export CURLOPT_KRB4LEVEL export CURL_IPRESOLVE_WHATEVER export CURL_IPRESOLVE_V4 export CURL_IPRESOLVE_V6 export CURLOPT_RTSPHEADER export CURL_HTTP_VERSION_2 export CURL_REDIR_GET_ALL export CURL_REDIR_POST_301 export CURL_REDIR_POST_302 export CURL_REDIR_POST_303 export CURL_REDIR_POST_ALL export CURL_ZERO_TERMINATED export CURLINFO_STRING export CURLINFO_LONG export CURLINFO_DOUBLE export CURLINFO_SLIST export CURLINFO_PTR export CURLINFO_SOCKET export CURLINFO_OFF_T export CURLINFO_MASK export CURLINFO_TYPEMASK export CURLINFO_HTTP_CODE export CURL_GLOBAL_SSL export CURL_GLOBAL_WIN32 export CURL_GLOBAL_ALL export CURL_GLOBAL_NOTHING export CURL_GLOBAL_DEFAULT export CURL_GLOBAL_ACK_EINTR export CURLVERSION_NOW export CURL_VERSION_IPV6 export CURL_VERSION_KERBEROS4 export CURL_VERSION_SSL export CURL_VERSION_LIBZ export CURL_VERSION_NTLM export CURL_VERSION_GSSNEGOTIATE export CURL_VERSION_DEBUG export CURL_VERSION_ASYNCHDNS export CURL_VERSION_SPNEGO export CURL_VERSION_LARGEFILE export CURL_VERSION_IDN export CURL_VERSION_SSPI export CURL_VERSION_CONV export CURL_VERSION_CURLDEBUG export CURL_VERSION_TLSAUTH_SRP export CURL_VERSION_NTLM_WB export CURL_VERSION_HTTP2 export CURL_VERSION_GSSAPI export CURL_VERSION_KERBEROS5 export CURL_VERSION_UNIX_SOCKETS export CURL_VERSION_PSL export CURL_VERSION_HTTPS_PROXY export CURL_VERSION_MULTI_SSL export CURL_VERSION_BROTLI export CURLPAUSE_RECV export CURLPAUSE_RECV_CONT export CURLPAUSE_SEND export CURLPAUSE_SEND_CONT export CURLPAUSE_ALL export CURLPAUSE_CONT export CURLM_CALL_MULTI_SOCKET export CURLPIPE_NOTHING export CURLPIPE_HTTP1 export CURLPIPE_MULTIPLEX export CURL_WAIT_POLLIN export CURL_WAIT_POLLPRI export CURL_WAIT_POLLOUT export CURL_POLL_NONE export CURL_POLL_IN export CURL_POLL_OUT export CURL_POLL_INOUT export CURL_POLL_REMOVE export CURL_SOCKET_TIMEOUT export CURL_CSELECT_IN export CURL_CSELECT_OUT export CURL_CSELECT_ERR export CURL_PUSH_OK export CURL_PUSH_DENY export CURLOPT_MAXAGE_CONN v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/lC_common_h.jl-ฉ������# Automatically generated using Clang.jl wrap_c, version 0.0.0 const CURL = Cvoid const CURLSH = Cvoid const curl_socket_t = Cint const CURL_SOCKET_BAD = -1 # begin enum curl_sslbackend const curl_sslbackend = UInt32 const CURLSSLBACKEND_NONE = (UInt32)(0) const CURLSSLBACKEND_OPENSSL = (UInt32)(1) const CURLSSLBACKEND_GNUTLS = (UInt32)(2) const CURLSSLBACKEND_NSS = (UInt32)(3) const CURLSSLBACKEND_OBSOLETE4 = (UInt32)(4) const CURLSSLBACKEND_GSKIT = (UInt32)(5) const CURLSSLBACKEND_POLARSSL = (UInt32)(6) const CURLSSLBACKEND_WOLFSSL = (UInt32)(7) const CURLSSLBACKEND_SCHANNEL = (UInt32)(8) const CURLSSLBACKEND_DARWINSSL = (UInt32)(9) const CURLSSLBACKEND_AXTLS = (UInt32)(10) const CURLSSLBACKEND_MBEDTLS = (UInt32)(11) # end enum curl_sslbackend const CURLSSLBACKEND_LIBRESSL = CURLSSLBACKEND_OPENSSL const CURLSSLBACKEND_BORINGSSL = CURLSSLBACKEND_OPENSSL const CURLSSLBACKEND_CYASSL = CURLSSLBACKEND_WOLFSSL mutable struct curl_httppost next::Ptr{Cvoid} name::Ptr{UInt8} namelength::Clong contents::Ptr{UInt8} contentslength::Clong buffer::Ptr{UInt8} bufferlength::Clong contenttype::Ptr{UInt8} contentheader::Ptr{Cvoid} more::Ptr{Cvoid} flags::Clong showfilename::Ptr{UInt8} userp::Ptr{Cvoid} contentlen::curl_off_t end const CURL_HTTPPOST_FILENAME = 1 << 0 const CURL_HTTPPOST_READFILE = 1 << 1 const CURL_HTTPPOST_PTRNAME = 1 << 2 const CURL_HTTPPOST_PTRCONTENTS = 1 << 3 const CURL_HTTPPOST_BUFFER = 1 << 4 const CURL_HTTPPOST_PTRBUFFER = 1 << 5 const CURL_HTTPPOST_CALLBACK = 1 << 6 const CURL_HTTPPOST_LARGE = 1 << 7 const curl_progress_callback = Ptr{Cvoid} const curl_xferinfo_callback = Ptr{Cvoid} const CURL_MAX_READ_SIZE = 524288 const CURL_MAX_WRITE_SIZE = 16384 const CURL_MAX_HTTP_HEADER = 100 * 1024 const CURL_WRITEFUNC_PAUSE = 0x10000001 const curl_write_callback = Ptr{Cvoid} const curl_resolver_start_callback = Ptr{Cvoid} const CURLFINFOFLAG_KNOWN_FILENAME = 1 << 0 const CURLFINFOFLAG_KNOWN_FILETYPE = 1 << 1 const CURLFINFOFLAG_KNOWN_TIME = 1 << 2 const CURLFINFOFLAG_KNOWN_PERM = 1 << 3 const CURLFINFOFLAG_KNOWN_UID = 1 << 4 const CURLFINFOFLAG_KNOWN_GID = 1 << 5 const CURLFINFOFLAG_KNOWN_SIZE = 1 << 6 const CURLFINFOFLAG_KNOWN_HLINKCOUNT = 1 << 7 const CURL_CHUNK_BGN_FUNC_OK = 0 const CURL_CHUNK_BGN_FUNC_FAIL = 1 const CURL_CHUNK_BGN_FUNC_SKIP = 2 const CURL_CHUNK_END_FUNC_OK = 0 const CURL_CHUNK_END_FUNC_FAIL = 1 const CURL_FNMATCHFUNC_MATCH = 0 const CURL_FNMATCHFUNC_NOMATCH = 1 const CURL_FNMATCHFUNC_FAIL = 2 const CURL_SEEKFUNC_OK = 0 const CURL_SEEKFUNC_FAIL = 1 const CURL_SEEKFUNC_CANTSEEK = 2 const CURL_READFUNC_ABORT = 0x10000000 const CURL_READFUNC_PAUSE = 0x10000001 const CURL_SOCKOPT_OK = 0 const CURL_SOCKOPT_ERROR = 1 const CURL_SOCKOPT_ALREADY_CONNECTED = 2 # begin enum curlfiletype const curlfiletype = UInt32 const CURLFILETYPE_FILE = (UInt32)(0) const CURLFILETYPE_DIRECTORY = (UInt32)(1) const CURLFILETYPE_SYMLINK = (UInt32)(2) const CURLFILETYPE_DEVICE_BLOCK = (UInt32)(3) const CURLFILETYPE_DEVICE_CHAR = (UInt32)(4) const CURLFILETYPE_NAMEDPIPE = (UInt32)(5) const CURLFILETYPE_SOCKET = (UInt32)(6) const CURLFILETYPE_DOOR = (UInt32)(7) const CURLFILETYPE_UNKNOWN = (UInt32)(8) # end enum curlfiletype mutable struct curl_fileinfo filename::Ptr{UInt8} filetype::curlfiletype time::time_t perm::UInt32 uid::Cint gid::Cint size::curl_off_t hardlinks::Clong strings::Cvoid flags::UInt32 b_data::Ptr{UInt8} b_size::Csize_t b_used::Csize_t end const curl_chunk_bgn_callback = Ptr{Cvoid} const curl_chunk_end_callback = Ptr{Cvoid} const curl_fnmatch_callback = Ptr{Cvoid} const curl_seek_callback = Ptr{Cvoid} const curl_read_callback = Ptr{Cvoid} # begin enum curlsocktype const curlsocktype = UInt32 const CURLSOCKTYPE_IPCXN = (UInt32)(0) const CURLSOCKTYPE_ACCEPT = (UInt32)(1) const CURLSOCKTYPE_LAST = (UInt32)(2) # end enum curlsocktype const curl_sockopt_callback = Ptr{Cvoid} mutable struct curl_sockaddr family::Cint socktype::Cint protocol::Cint addrlen::UInt32 addr::Cvoid end const curl_opensocket_callback = Ptr{Cvoid} const curl_closesocket_callback = Ptr{Cvoid} # begin enum curlioerr const curlioerr = UInt32 const CURLIOE_OK = (UInt32)(0) const CURLIOE_UNKNOWNCMD = (UInt32)(1) const CURLIOE_FAILRESTART = (UInt32)(2) const CURLIOE_LAST = (UInt32)(3) # end enum curlioerr # begin enum curliocmd const curliocmd = UInt32 const CURLIOCMD_NOP = (UInt32)(0) const CURLIOCMD_RESTARTREAD = (UInt32)(1) const CURLIOCMD_LAST = (UInt32)(2) # end enum curliocmd const curl_ioctl_callback = Ptr{Cvoid} const curl_malloc_callback = Ptr{Cvoid} const curl_free_callback = Ptr{Cvoid} const curl_realloc_callback = Ptr{Cvoid} const curl_strdup_callback = Ptr{Cvoid} const curl_calloc_callback = Ptr{Cvoid} # begin enum curl_infotype const curl_infotype = UInt32 const CURLINFO_TEXT = (UInt32)(0) const CURLINFO_HEADER_IN = (UInt32)(1) const CURLINFO_HEADER_OUT = (UInt32)(2) const CURLINFO_DATA_IN = (UInt32)(3) const CURLINFO_DATA_OUT = (UInt32)(4) const CURLINFO_SSL_DATA_IN = (UInt32)(5) const CURLINFO_SSL_DATA_OUT = (UInt32)(6) const CURLINFO_END = (UInt32)(7) # end enum curl_infotype const curl_debug_callback = Ptr{Cvoid} # begin enum CURLcode const CURLcode = UInt32 const CURLE_OK = (UInt32)(0) const CURLE_UNSUPPORTED_PROTOCOL = (UInt32)(1) const CURLE_FAILED_INIT = (UInt32)(2) const CURLE_URL_MALFORMAT = (UInt32)(3) const CURLE_NOT_BUILT_IN = (UInt32)(4) const CURLE_COULDNT_RESOLVE_PROXY = (UInt32)(5) const CURLE_COULDNT_RESOLVE_HOST = (UInt32)(6) const CURLE_COULDNT_CONNECT = (UInt32)(7) const CURLE_WEIRD_SERVER_REPLY = (UInt32)(8) const CURLE_REMOTE_ACCESS_DENIED = (UInt32)(9) const CURLE_FTP_ACCEPT_FAILED = (UInt32)(10) const CURLE_FTP_WEIRD_PASS_REPLY = (UInt32)(11) const CURLE_FTP_ACCEPT_TIMEOUT = (UInt32)(12) const CURLE_FTP_WEIRD_PASV_REPLY = (UInt32)(13) const CURLE_FTP_WEIRD_227_FORMAT = (UInt32)(14) const CURLE_FTP_CANT_GET_HOST = (UInt32)(15) const CURLE_HTTP2 = (UInt32)(16) const CURLE_FTP_COULDNT_SET_TYPE = (UInt32)(17) const CURLE_PARTIAL_FILE = (UInt32)(18) const CURLE_FTP_COULDNT_RETR_FILE = (UInt32)(19) const CURLE_OBSOLETE20 = (UInt32)(20) const CURLE_QUOTE_ERROR = (UInt32)(21) const CURLE_HTTP_RETURNED_ERROR = (UInt32)(22) const CURLE_WRITE_ERROR = (UInt32)(23) const CURLE_OBSOLETE24 = (UInt32)(24) const CURLE_UPLOAD_FAILED = (UInt32)(25) const CURLE_READ_ERROR = (UInt32)(26) const CURLE_OUT_OF_MEMORY = (UInt32)(27) const CURLE_OPERATION_TIMEDOUT = (UInt32)(28) const CURLE_OBSOLETE29 = (UInt32)(29) const CURLE_FTP_PORT_FAILED = (UInt32)(30) const CURLE_FTP_COULDNT_USE_REST = (UInt32)(31) const CURLE_OBSOLETE32 = (UInt32)(32) const CURLE_RANGE_ERROR = (UInt32)(33) const CURLE_HTTP_POST_ERROR = (UInt32)(34) const CURLE_SSL_CONNECT_ERROR = (UInt32)(35) const CURLE_BAD_DOWNLOAD_RESUME = (UInt32)(36) const CURLE_FILE_COULDNT_READ_FILE = (UInt32)(37) const CURLE_LDAP_CANNOT_BIND = (UInt32)(38) const CURLE_LDAP_SEARCH_FAILED = (UInt32)(39) const CURLE_OBSOLETE40 = (UInt32)(40) const CURLE_FUNCTION_NOT_FOUND = (UInt32)(41) const CURLE_ABORTED_BY_CALLBACK = (UInt32)(42) const CURLE_BAD_FUNCTION_ARGUMENT = (UInt32)(43) const CURLE_OBSOLETE44 = (UInt32)(44) const CURLE_INTERFACE_FAILED = (UInt32)(45) const CURLE_OBSOLETE46 = (UInt32)(46) const CURLE_TOO_MANY_REDIRECTS = (UInt32)(47) const CURLE_UNKNOWN_OPTION = (UInt32)(48) const CURLE_TELNET_OPTION_SYNTAX = (UInt32)(49) const CURLE_OBSOLETE50 = (UInt32)(50) const CURLE_PEER_FAILED_VERIFICATION = (UInt32)(51) const CURLE_GOT_NOTHING = (UInt32)(52) const CURLE_SSL_ENGINE_NOTFOUND = (UInt32)(53) const CURLE_SSL_ENGINE_SETFAILED = (UInt32)(54) const CURLE_SEND_ERROR = (UInt32)(55) const CURLE_RECV_ERROR = (UInt32)(56) const CURLE_OBSOLETE57 = (UInt32)(57) const CURLE_SSL_CERTPROBLEM = (UInt32)(58) const CURLE_SSL_CIPHER = (UInt32)(59) const CURLE_SSL_CACERT = (UInt32)(60) const CURLE_BAD_CONTENT_ENCODING = (UInt32)(61) const CURLE_LDAP_INVALID_URL = (UInt32)(62) const CURLE_FILESIZE_EXCEEDED = (UInt32)(63) const CURLE_USE_SSL_FAILED = (UInt32)(64) const CURLE_SEND_FAIL_REWIND = (UInt32)(65) const CURLE_SSL_ENGINE_INITFAILED = (UInt32)(66) const CURLE_LOGIN_DENIED = (UInt32)(67) const CURLE_TFTP_NOTFOUND = (UInt32)(68) const CURLE_TFTP_PERM = (UInt32)(69) const CURLE_REMOTE_DISK_FULL = (UInt32)(70) const CURLE_TFTP_ILLEGAL = (UInt32)(71) const CURLE_TFTP_UNKNOWNID = (UInt32)(72) const CURLE_REMOTE_FILE_EXISTS = (UInt32)(73) const CURLE_TFTP_NOSUCHUSER = (UInt32)(74) const CURLE_CONV_FAILED = (UInt32)(75) const CURLE_CONV_REQD = (UInt32)(76) const CURLE_SSL_CACERT_BADFILE = (UInt32)(77) const CURLE_REMOTE_FILE_NOT_FOUND = (UInt32)(78) const CURLE_SSH = (UInt32)(79) const CURLE_SSL_SHUTDOWN_FAILED = (UInt32)(80) const CURLE_AGAIN = (UInt32)(81) const CURLE_SSL_CRL_BADFILE = (UInt32)(82) const CURLE_SSL_ISSUER_ERROR = (UInt32)(83) const CURLE_FTP_PRET_FAILED = (UInt32)(84) const CURLE_RTSP_CSEQ_ERROR = (UInt32)(85) const CURLE_RTSP_SESSION_ERROR = (UInt32)(86) const CURLE_FTP_BAD_FILE_LIST = (UInt32)(87) const CURLE_CHUNK_FAILED = (UInt32)(88) const CURLE_NO_CONNECTION_AVAILABLE = (UInt32)(89) const CURLE_SSL_PINNEDPUBKEYNOTMATCH = (UInt32)(90) const CURLE_SSL_INVALIDCERTSTATUS = (UInt32)(91) const CURLE_HTTP2_STREAM = (UInt32)(92) const CURLE_RECURSIVE_API_CALL = (UInt32)(93) const CURL_LAST = (UInt32)(94) # end enum CURLcode const curl_conv_callback = Ptr{Cvoid} const curl_ssl_ctx_callback = Ptr{Cvoid} # begin enum curl_proxytype const curl_proxytype = UInt32 const CURLPROXY_HTTP = (UInt32)(0) const CURLPROXY_HTTP_1_0 = (UInt32)(1) const CURLPROXY_HTTPS = (UInt32)(2) const CURLPROXY_SOCKS4 = (UInt32)(4) const CURLPROXY_SOCKS5 = (UInt32)(5) const CURLPROXY_SOCKS4A = (UInt32)(6) const CURLPROXY_SOCKS5_HOSTNAME = (UInt32)(7) # end enum curl_proxytype const CURLSSH_AUTH_ANY = ~0 const CURLSSH_AUTH_NONE = 0 const CURLSSH_AUTH_PUBLICKEY = 1 << 0 const CURLSSH_AUTH_PASSWORD = 1 << 1 const CURLSSH_AUTH_HOST = 1 << 2 const CURLSSH_AUTH_KEYBOARD = 1 << 3 const CURLSSH_AUTH_AGENT = 1 << 4 const CURLSSH_AUTH_GSSAPI = 1 << 5 const CURLSSH_AUTH_DEFAULT = CURLSSH_AUTH_ANY const CURLGSSAPI_DELEGATION_NONE = 0 const CURLGSSAPI_DELEGATION_POLICY_FLAG = 1 << 0 const CURLGSSAPI_DELEGATION_FLAG = 1 << 1 const CURL_ERROR_SIZE = 256 # begin enum curl_khtype const curl_khtype = UInt32 const CURLKHTYPE_UNKNOWN = (UInt32)(0) const CURLKHTYPE_RSA1 = (UInt32)(1) const CURLKHTYPE_RSA = (UInt32)(2) const CURLKHTYPE_DSS = (UInt32)(3) const CURLKHTYPE_ECDSA = (UInt32)(4) const CURLKHTYPE_ED25519 = (UInt32)(5) # end enum curl_khtype mutable struct curl_khkey key::Ptr{UInt8} len::Csize_t keytype::Cvoid end # begin enum curl_khstat const curl_khstat = UInt32 const CURLKHSTAT_FINE_ADD_TO_FILE = (UInt32)(0) const CURLKHSTAT_FINE = (UInt32)(1) const CURLKHSTAT_REJECT = (UInt32)(2) const CURLKHSTAT_DEFER = (UInt32)(3) const CURLKHSTAT_LAST = (UInt32)(4) # end enum curl_khstat # begin enum curl_khmatch const curl_khmatch = UInt32 const CURLKHMATCH_OK = (UInt32)(0) const CURLKHMATCH_MISMATCH = (UInt32)(1) const CURLKHMATCH_MISSING = (UInt32)(2) const CURLKHMATCH_LAST = (UInt32)(3) # end enum curl_khmatch const curl_sshkeycallback = Ptr{Cvoid} # begin enum curl_usessl const curl_usessl = UInt32 const CURLUSESSL_NONE = (UInt32)(0) const CURLUSESSL_TRY = (UInt32)(1) const CURLUSESSL_CONTROL = (UInt32)(2) const CURLUSESSL_ALL = (UInt32)(3) const CURLUSESSL_LAST = (UInt32)(4) # end enum curl_usessl const CURLSSLOPT_ALLOW_BEAST = 1 << 0 const CURLSSLOPT_NO_REVOKE = 1 << 1 const CURL_HET_DEFAULT = Clong(200) const curl_ftpssl = curl_usessl const CURLFTPSSL_NONE = CURLUSESSL_NONE const CURLFTPSSL_TRY = CURLUSESSL_TRY const CURLFTPSSL_CONTROL = CURLUSESSL_CONTROL const CURLFTPSSL_ALL = CURLUSESSL_ALL const CURLFTPSSL_LAST = CURLUSESSL_LAST # begin enum curl_ftpccc const curl_ftpccc = UInt32 const CURLFTPSSL_CCC_NONE = (UInt32)(0) const CURLFTPSSL_CCC_PASSIVE = (UInt32)(1) const CURLFTPSSL_CCC_ACTIVE = (UInt32)(2) const CURLFTPSSL_CCC_LAST = (UInt32)(3) # end enum curl_ftpccc # begin enum curl_ftpauth const curl_ftpauth = UInt32 const CURLFTPAUTH_DEFAULT = (UInt32)(0) const CURLFTPAUTH_SSL = (UInt32)(1) const CURLFTPAUTH_TLS = (UInt32)(2) const CURLFTPAUTH_LAST = (UInt32)(3) # end enum curl_ftpauth # begin enum curl_ftpcreatedir const curl_ftpcreatedir = UInt32 const CURLFTP_CREATE_DIR_NONE = (UInt32)(0) const CURLFTP_CREATE_DIR = (UInt32)(1) const CURLFTP_CREATE_DIR_RETRY = (UInt32)(2) const CURLFTP_CREATE_DIR_LAST = (UInt32)(3) # end enum curl_ftpcreatedir # begin enum curl_ftpmethod const curl_ftpmethod = UInt32 const CURLFTPMETHOD_DEFAULT = (UInt32)(0) const CURLFTPMETHOD_MULTICWD = (UInt32)(1) const CURLFTPMETHOD_NOCWD = (UInt32)(2) const CURLFTPMETHOD_SINGLECWD = (UInt32)(3) const CURLFTPMETHOD_LAST = (UInt32)(4) # end enum curl_ftpmethod const CURLHEADER_UNIFIED = 0 const CURLHEADER_SEPARATE = 1 << 0 const CURLPROTO_HTTP = 1 << 0 const CURLPROTO_HTTPS = 1 << 1 const CURLPROTO_FTP = 1 << 2 const CURLPROTO_FTPS = 1 << 3 const CURLPROTO_SCP = 1 << 4 const CURLPROTO_SFTP = 1 << 5 const CURLPROTO_TELNET = 1 << 6 const CURLPROTO_LDAP = 1 << 7 const CURLPROTO_LDAPS = 1 << 8 const CURLPROTO_DICT = 1 << 9 const CURLPROTO_FILE = 1 << 10 const CURLPROTO_TFTP = 1 << 11 const CURLPROTO_IMAP = 1 << 12 const CURLPROTO_IMAPS = 1 << 13 const CURLPROTO_POP3 = 1 << 14 const CURLPROTO_POP3S = 1 << 15 const CURLPROTO_SMTP = 1 << 16 const CURLPROTO_SMTPS = 1 << 17 const CURLPROTO_RTSP = 1 << 18 const CURLPROTO_RTMP = 1 << 19 const CURLPROTO_RTMPT = 1 << 20 const CURLPROTO_RTMPE = 1 << 21 const CURLPROTO_RTMPTE = 1 << 22 const CURLPROTO_RTMPS = 1 << 23 const CURLPROTO_RTMPTS = 1 << 24 const CURLPROTO_GOPHER = 1 << 25 const CURLPROTO_SMB = 1 << 26 const CURLPROTO_SMBS = 1 << 27 const CURLPROTO_ALL = ~0 const CURLOPTTYPE_LONG = 0 const CURLOPTTYPE_OBJECTPOINT = 10000 const CURLOPTTYPE_STRINGPOINT = 10000 const CURLOPTTYPE_FUNCTIONPOINT = 20000 const CURLOPTTYPE_OFF_T = 30000 # begin enum CURLoption const CURLoption = UInt32 const CURLOPT_WRITEDATA = (UInt32)(10001) const CURLOPT_URL = (UInt32)(10002) const CURLOPT_PORT = (UInt32)(3) const CURLOPT_PROXY = (UInt32)(10004) const CURLOPT_USERPWD = (UInt32)(10005) const CURLOPT_PROXYUSERPWD = (UInt32)(10006) const CURLOPT_RANGE = (UInt32)(10007) const CURLOPT_READDATA = (UInt32)(10009) const CURLOPT_ERRORBUFFER = (UInt32)(10010) const CURLOPT_WRITEFUNCTION = (UInt32)(20011) const CURLOPT_READFUNCTION = (UInt32)(20012) const CURLOPT_TIMEOUT = (UInt32)(13) const CURLOPT_INFILESIZE = (UInt32)(14) const CURLOPT_POSTFIELDS = (UInt32)(10015) const CURLOPT_REFERER = (UInt32)(10016) const CURLOPT_FTPPORT = (UInt32)(10017) const CURLOPT_USERAGENT = (UInt32)(10018) const CURLOPT_LOW_SPEED_LIMIT = (UInt32)(19) const CURLOPT_LOW_SPEED_TIME = (UInt32)(20) const CURLOPT_RESUME_FROM = (UInt32)(21) const CURLOPT_COOKIE = (UInt32)(10022) const CURLOPT_HTTPHEADER = (UInt32)(10023) const CURLOPT_HTTPPOST = (UInt32)(10024) const CURLOPT_SSLCERT = (UInt32)(10025) const CURLOPT_KEYPASSWD = (UInt32)(10026) const CURLOPT_CRLF = (UInt32)(27) const CURLOPT_QUOTE = (UInt32)(10028) const CURLOPT_HEADERDATA = (UInt32)(10029) const CURLOPT_COOKIEFILE = (UInt32)(10031) const CURLOPT_SSLVERSION = (UInt32)(32) const CURLOPT_TIMECONDITION = (UInt32)(33) const CURLOPT_TIMEVALUE = (UInt32)(34) const CURLOPT_CUSTOMREQUEST = (UInt32)(10036) const CURLOPT_STDERR = (UInt32)(10037) const CURLOPT_POSTQUOTE = (UInt32)(10039) const CURLOPT_OBSOLETE40 = (UInt32)(10040) const CURLOPT_VERBOSE = (UInt32)(41) const CURLOPT_HEADER = (UInt32)(42) const CURLOPT_NOPROGRESS = (UInt32)(43) const CURLOPT_NOBODY = (UInt32)(44) const CURLOPT_FAILONERROR = (UInt32)(45) const CURLOPT_UPLOAD = (UInt32)(46) const CURLOPT_POST = (UInt32)(47) const CURLOPT_DIRLISTONLY = (UInt32)(48) const CURLOPT_APPEND = (UInt32)(50) const CURLOPT_NETRC = (UInt32)(51) const CURLOPT_FOLLOWLOCATION = (UInt32)(52) const CURLOPT_TRANSFERTEXT = (UInt32)(53) const CURLOPT_PUT = (UInt32)(54) const CURLOPT_PROGRESSFUNCTION = (UInt32)(20056) const CURLOPT_PROGRESSDATA = (UInt32)(10057) const CURLOPT_AUTOREFERER = (UInt32)(58) const CURLOPT_PROXYPORT = (UInt32)(59) const CURLOPT_POSTFIELDSIZE = (UInt32)(60) const CURLOPT_HTTPPROXYTUNNEL = (UInt32)(61) const CURLOPT_INTERFACE = (UInt32)(10062) const CURLOPT_KRBLEVEL = (UInt32)(10063) const CURLOPT_SSL_VERIFYPEER = (UInt32)(64) const CURLOPT_CAINFO = (UInt32)(10065) const CURLOPT_MAXREDIRS = (UInt32)(68) const CURLOPT_FILETIME = (UInt32)(69) const CURLOPT_TELNETOPTIONS = (UInt32)(10070) const CURLOPT_MAXCONNECTS = (UInt32)(71) const CURLOPT_OBSOLETE72 = (UInt32)(72) const CURLOPT_FRESH_CONNECT = (UInt32)(74) const CURLOPT_FORBID_REUSE = (UInt32)(75) const CURLOPT_RANDOM_FILE = (UInt32)(10076) const CURLOPT_EGDSOCKET = (UInt32)(10077) const CURLOPT_CONNECTTIMEOUT = (UInt32)(78) const CURLOPT_HEADERFUNCTION = (UInt32)(20079) const CURLOPT_HTTPGET = (UInt32)(80) const CURLOPT_SSL_VERIFYHOST = (UInt32)(81) const CURLOPT_COOKIEJAR = (UInt32)(10082) const CURLOPT_SSL_CIPHER_LIST = (UInt32)(10083) const CURLOPT_HTTP_VERSION = (UInt32)(84) const CURLOPT_FTP_USE_EPSV = (UInt32)(85) const CURLOPT_SSLCERTTYPE = (UInt32)(10086) const CURLOPT_SSLKEY = (UInt32)(10087) const CURLOPT_SSLKEYTYPE = (UInt32)(10088) const CURLOPT_SSLENGINE = (UInt32)(10089) const CURLOPT_SSLENGINE_DEFAULT = (UInt32)(90) const CURLOPT_DNS_USE_GLOBAL_CACHE = (UInt32)(91) const CURLOPT_DNS_CACHE_TIMEOUT = (UInt32)(92) const CURLOPT_PREQUOTE = (UInt32)(10093) const CURLOPT_DEBUGFUNCTION = (UInt32)(20094) const CURLOPT_DEBUGDATA = (UInt32)(10095) const CURLOPT_COOKIESESSION = (UInt32)(96) const CURLOPT_CAPATH = (UInt32)(10097) const CURLOPT_BUFFERSIZE = (UInt32)(98) const CURLOPT_NOSIGNAL = (UInt32)(99) const CURLOPT_SHARE = (UInt32)(10100) const CURLOPT_PROXYTYPE = (UInt32)(101) const CURLOPT_ACCEPT_ENCODING = (UInt32)(10102) const CURLOPT_PRIVATE = (UInt32)(10103) const CURLOPT_HTTP200ALIASES = (UInt32)(10104) const CURLOPT_UNRESTRICTED_AUTH = (UInt32)(105) const CURLOPT_FTP_USE_EPRT = (UInt32)(106) const CURLOPT_HTTPAUTH = (UInt32)(107) const CURLOPT_SSL_CTX_FUNCTION = (UInt32)(20108) const CURLOPT_SSL_CTX_DATA = (UInt32)(10109) const CURLOPT_FTP_CREATE_MISSING_DIRS = (UInt32)(110) const CURLOPT_PROXYAUTH = (UInt32)(111) const CURLOPT_FTP_RESPONSE_TIMEOUT = (UInt32)(112) const CURLOPT_IPRESOLVE = (UInt32)(113) const CURLOPT_MAXFILESIZE = (UInt32)(114) const CURLOPT_INFILESIZE_LARGE = (UInt32)(30115) const CURLOPT_RESUME_FROM_LARGE = (UInt32)(30116) const CURLOPT_MAXFILESIZE_LARGE = (UInt32)(30117) const CURLOPT_NETRC_FILE = (UInt32)(10118) const CURLOPT_USE_SSL = (UInt32)(119) const CURLOPT_POSTFIELDSIZE_LARGE = (UInt32)(30120) const CURLOPT_TCP_NODELAY = (UInt32)(121) const CURLOPT_FTPSSLAUTH = (UInt32)(129) const CURLOPT_IOCTLFUNCTION = (UInt32)(20130) const CURLOPT_IOCTLDATA = (UInt32)(10131) const CURLOPT_FTP_ACCOUNT = (UInt32)(10134) const CURLOPT_COOKIELIST = (UInt32)(10135) const CURLOPT_IGNORE_CONTENT_LENGTH = (UInt32)(136) const CURLOPT_FTP_SKIP_PASV_IP = (UInt32)(137) const CURLOPT_FTP_FILEMETHOD = (UInt32)(138) const CURLOPT_LOCALPORT = (UInt32)(139) const CURLOPT_LOCALPORTRANGE = (UInt32)(140) const CURLOPT_CONNECT_ONLY = (UInt32)(141) const CURLOPT_CONV_FROM_NETWORK_FUNCTION = (UInt32)(20142) const CURLOPT_CONV_TO_NETWORK_FUNCTION = (UInt32)(20143) const CURLOPT_CONV_FROM_UTF8_FUNCTION = (UInt32)(20144) const CURLOPT_MAX_SEND_SPEED_LARGE = (UInt32)(30145) const CURLOPT_MAX_RECV_SPEED_LARGE = (UInt32)(30146) const CURLOPT_FTP_ALTERNATIVE_TO_USER = (UInt32)(10147) const CURLOPT_SOCKOPTFUNCTION = (UInt32)(20148) const CURLOPT_SOCKOPTDATA = (UInt32)(10149) const CURLOPT_SSL_SESSIONID_CACHE = (UInt32)(150) const CURLOPT_SSH_AUTH_TYPES = (UInt32)(151) const CURLOPT_SSH_PUBLIC_KEYFILE = (UInt32)(10152) const CURLOPT_SSH_PRIVATE_KEYFILE = (UInt32)(10153) const CURLOPT_FTP_SSL_CCC = (UInt32)(154) const CURLOPT_TIMEOUT_MS = (UInt32)(155) const CURLOPT_CONNECTTIMEOUT_MS = (UInt32)(156) const CURLOPT_HTTP_TRANSFER_DECODING = (UInt32)(157) const CURLOPT_HTTP_CONTENT_DECODING = (UInt32)(158) const CURLOPT_NEW_FILE_PERMS = (UInt32)(159) const CURLOPT_NEW_DIRECTORY_PERMS = (UInt32)(160) const CURLOPT_POSTREDIR = (UInt32)(161) const CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 = (UInt32)(10162) const CURLOPT_OPENSOCKETFUNCTION = (UInt32)(20163) const CURLOPT_OPENSOCKETDATA = (UInt32)(10164) const CURLOPT_COPYPOSTFIELDS = (UInt32)(10165) const CURLOPT_PROXY_TRANSFER_MODE = (UInt32)(166) const CURLOPT_SEEKFUNCTION = (UInt32)(20167) const CURLOPT_SEEKDATA = (UInt32)(10168) const CURLOPT_CRLFILE = (UInt32)(10169) const CURLOPT_ISSUERCERT = (UInt32)(10170) const CURLOPT_ADDRESS_SCOPE = (UInt32)(171) const CURLOPT_CERTINFO = (UInt32)(172) const CURLOPT_USERNAME = (UInt32)(10173) const CURLOPT_PASSWORD = (UInt32)(10174) const CURLOPT_PROXYUSERNAME = (UInt32)(10175) const CURLOPT_PROXYPASSWORD = (UInt32)(10176) const CURLOPT_NOPROXY = (UInt32)(10177) const CURLOPT_TFTP_BLKSIZE = (UInt32)(178) const CURLOPT_SOCKS5_GSSAPI_SERVICE = (UInt32)(10179) const CURLOPT_SOCKS5_GSSAPI_NEC = (UInt32)(180) const CURLOPT_PROTOCOLS = (UInt32)(181) const CURLOPT_REDIR_PROTOCOLS = (UInt32)(182) const CURLOPT_SSH_KNOWNHOSTS = (UInt32)(10183) const CURLOPT_SSH_KEYFUNCTION = (UInt32)(20184) const CURLOPT_SSH_KEYDATA = (UInt32)(10185) const CURLOPT_MAIL_FROM = (UInt32)(10186) const CURLOPT_MAIL_RCPT = (UInt32)(10187) const CURLOPT_FTP_USE_PRET = (UInt32)(188) const CURLOPT_RTSP_REQUEST = (UInt32)(189) const CURLOPT_RTSP_SESSION_ID = (UInt32)(10190) const CURLOPT_RTSP_STREAM_URI = (UInt32)(10191) const CURLOPT_RTSP_TRANSPORT = (UInt32)(10192) const CURLOPT_RTSP_CLIENT_CSEQ = (UInt32)(193) const CURLOPT_RTSP_SERVER_CSEQ = (UInt32)(194) const CURLOPT_INTERLEAVEDATA = (UInt32)(10195) const CURLOPT_INTERLEAVEFUNCTION = (UInt32)(20196) const CURLOPT_WILDCARDMATCH = (UInt32)(197) const CURLOPT_CHUNK_BGN_FUNCTION = (UInt32)(20198) const CURLOPT_CHUNK_END_FUNCTION = (UInt32)(20199) const CURLOPT_FNMATCH_FUNCTION = (UInt32)(20200) const CURLOPT_CHUNK_DATA = (UInt32)(10201) const CURLOPT_FNMATCH_DATA = (UInt32)(10202) const CURLOPT_RESOLVE = (UInt32)(10203) const CURLOPT_TLSAUTH_USERNAME = (UInt32)(10204) const CURLOPT_TLSAUTH_PASSWORD = (UInt32)(10205) const CURLOPT_TLSAUTH_TYPE = (UInt32)(10206) const CURLOPT_TRANSFER_ENCODING = (UInt32)(207) const CURLOPT_CLOSESOCKETFUNCTION = (UInt32)(20208) const CURLOPT_CLOSESOCKETDATA = (UInt32)(10209) const CURLOPT_GSSAPI_DELEGATION = (UInt32)(210) const CURLOPT_DNS_SERVERS = (UInt32)(10211) const CURLOPT_ACCEPTTIMEOUT_MS = (UInt32)(212) const CURLOPT_TCP_KEEPALIVE = (UInt32)(213) const CURLOPT_TCP_KEEPIDLE = (UInt32)(214) const CURLOPT_TCP_KEEPINTVL = (UInt32)(215) const CURLOPT_SSL_OPTIONS = (UInt32)(216) const CURLOPT_MAIL_AUTH = (UInt32)(10217) const CURLOPT_SASL_IR = (UInt32)(218) const CURLOPT_XFERINFOFUNCTION = (UInt32)(20219) const CURLOPT_XOAUTH2_BEARER = (UInt32)(10220) const CURLOPT_DNS_INTERFACE = (UInt32)(10221) const CURLOPT_DNS_LOCAL_IP4 = (UInt32)(10222) const CURLOPT_DNS_LOCAL_IP6 = (UInt32)(10223) const CURLOPT_LOGIN_OPTIONS = (UInt32)(10224) const CURLOPT_SSL_ENABLE_NPN = (UInt32)(225) const CURLOPT_SSL_ENABLE_ALPN = (UInt32)(226) const CURLOPT_EXPECT_100_TIMEOUT_MS = (UInt32)(227) const CURLOPT_PROXYHEADER = (UInt32)(10228) const CURLOPT_HEADEROPT = (UInt32)(229) const CURLOPT_PINNEDPUBLICKEY = (UInt32)(10230) const CURLOPT_UNIX_SOCKET_PATH = (UInt32)(10231) const CURLOPT_SSL_VERIFYSTATUS = (UInt32)(232) const CURLOPT_SSL_FALSESTART = (UInt32)(233) const CURLOPT_PATH_AS_IS = (UInt32)(234) const CURLOPT_PROXY_SERVICE_NAME = (UInt32)(10235) const CURLOPT_SERVICE_NAME = (UInt32)(10236) const CURLOPT_PIPEWAIT = (UInt32)(237) const CURLOPT_DEFAULT_PROTOCOL = (UInt32)(10238) const CURLOPT_STREAM_WEIGHT = (UInt32)(239) const CURLOPT_STREAM_DEPENDS = (UInt32)(10240) const CURLOPT_STREAM_DEPENDS_E = (UInt32)(10241) const CURLOPT_TFTP_NO_OPTIONS = (UInt32)(242) const CURLOPT_CONNECT_TO = (UInt32)(10243) const CURLOPT_TCP_FASTOPEN = (UInt32)(244) const CURLOPT_KEEP_SENDING_ON_ERROR = (UInt32)(245) const CURLOPT_PROXY_CAINFO = (UInt32)(10246) const CURLOPT_PROXY_CAPATH = (UInt32)(10247) const CURLOPT_PROXY_SSL_VERIFYPEER = (UInt32)(248) const CURLOPT_PROXY_SSL_VERIFYHOST = (UInt32)(249) const CURLOPT_PROXY_SSLVERSION = (UInt32)(250) const CURLOPT_PROXY_TLSAUTH_USERNAME = (UInt32)(10251) const CURLOPT_PROXY_TLSAUTH_PASSWORD = (UInt32)(10252) const CURLOPT_PROXY_TLSAUTH_TYPE = (UInt32)(10253) const CURLOPT_PROXY_SSLCERT = (UInt32)(10254) const CURLOPT_PROXY_SSLCERTTYPE = (UInt32)(10255) const CURLOPT_PROXY_SSLKEY = (UInt32)(10256) const CURLOPT_PROXY_SSLKEYTYPE = (UInt32)(10257) const CURLOPT_PROXY_KEYPASSWD = (UInt32)(10258) const CURLOPT_PROXY_SSL_CIPHER_LIST = (UInt32)(10259) const CURLOPT_PROXY_CRLFILE = (UInt32)(10260) const CURLOPT_PROXY_SSL_OPTIONS = (UInt32)(261) const CURLOPT_PRE_PROXY = (UInt32)(10262) const CURLOPT_PROXY_PINNEDPUBLICKEY = (UInt32)(10263) const CURLOPT_ABSTRACT_UNIX_SOCKET = (UInt32)(10264) const CURLOPT_SUPPRESS_CONNECT_HEADERS = (UInt32)(265) const CURLOPT_REQUEST_TARGET = (UInt32)(10266) const CURLOPT_SOCKS5_AUTH = (UInt32)(267) const CURLOPT_SSH_COMPRESSION = (UInt32)(268) const CURLOPT_MIMEPOST = (UInt32)(10269) const CURLOPT_TIMEVALUE_LARGE = (UInt32)(30270) const CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS = (UInt32)(271) const CURLOPT_RESOLVER_START_FUNCTION = (UInt32)(20272) const CURLOPT_RESOLVER_START_DATA = (UInt32)(10273) const CURLOPT_HAPROXYPROTOCOL = (UInt32)(274) const CURLOPT_DNS_SHUFFLE_ADDRESSES = (UInt32)(275) const CURLOPT_TLS13_CIPHERS = (UInt32)(10276) const CURLOPT_PROXY_TLS13_CIPHERS = (UInt32)(10277) const CURLOPT_DISALLOW_USERNAME_IN_URL = (UInt32)(278) const CURLOPT_MAXAGE_CONN = UInt32(288) const CURLOPT_LASTENTRY = (UInt32)(10299) # end enum CURLoption const CURLOPT_XFERINFODATA = CURLOPT_PROGRESSDATA const CURLOPT_SERVER_RESPONSE_TIMEOUT = CURLOPT_FTP_RESPONSE_TIMEOUT const CURLOPT_POST301 = CURLOPT_POSTREDIR const CURLOPT_SSLKEYPASSWD = CURLOPT_KEYPASSWD const CURLOPT_FTPAPPEND = CURLOPT_APPEND const CURLOPT_FTPLISTONLY = CURLOPT_DIRLISTONLY const CURLOPT_FTP_SSL = CURLOPT_USE_SSL const CURLOPT_SSLCERTPASSWD = CURLOPT_KEYPASSWD const CURLOPT_KRB4LEVEL = CURLOPT_KRBLEVEL const CURL_IPRESOLVE_WHATEVER = 0 const CURL_IPRESOLVE_V4 = 1 const CURL_IPRESOLVE_V6 = 2 const CURLOPT_RTSPHEADER = CURLOPT_HTTPHEADER # begin enum ANONYMOUS const CURL_HTTP_VERSION_NONE = (UInt32)(0) const CURL_HTTP_VERSION_1_0 = (UInt32)(1) const CURL_HTTP_VERSION_1_1 = (UInt32)(2) const CURL_HTTP_VERSION_2_0 = (UInt32)(3) const CURL_HTTP_VERSION_2TLS = (UInt32)(4) const CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE = (UInt32)(5) const CURL_HTTP_VERSION_LAST = (UInt32)(6) # end enum ANONYMOUS const CURL_HTTP_VERSION_2 = CURL_HTTP_VERSION_2_0 # begin enum ANONYMOUS const CURL_RTSPREQ_NONE = (UInt32)(0) const CURL_RTSPREQ_OPTIONS = (UInt32)(1) const CURL_RTSPREQ_DESCRIBE = (UInt32)(2) const CURL_RTSPREQ_ANNOUNCE = (UInt32)(3) const CURL_RTSPREQ_SETUP = (UInt32)(4) const CURL_RTSPREQ_PLAY = (UInt32)(5) const CURL_RTSPREQ_PAUSE = (UInt32)(6) const CURL_RTSPREQ_TEARDOWN = (UInt32)(7) const CURL_RTSPREQ_GET_PARAMETER = (UInt32)(8) const CURL_RTSPREQ_SET_PARAMETER = (UInt32)(9) const CURL_RTSPREQ_RECORD = (UInt32)(10) const CURL_RTSPREQ_RECEIVE = (UInt32)(11) const CURL_RTSPREQ_LAST = (UInt32)(12) # end enum ANONYMOUS # begin enum CURL_NETRC_OPTION const CURL_NETRC_OPTION = UInt32 const CURL_NETRC_IGNORED = (UInt32)(0) const CURL_NETRC_OPTIONAL = (UInt32)(1) const CURL_NETRC_REQUIRED = (UInt32)(2) const CURL_NETRC_LAST = (UInt32)(3) # end enum CURL_NETRC_OPTION # begin enum ANONYMOUS const CURL_SSLVERSION_DEFAULT = (UInt32)(0) const CURL_SSLVERSION_TLSv1 = (UInt32)(1) const CURL_SSLVERSION_SSLv2 = (UInt32)(2) const CURL_SSLVERSION_SSLv3 = (UInt32)(3) const CURL_SSLVERSION_TLSv1_0 = (UInt32)(4) const CURL_SSLVERSION_TLSv1_1 = (UInt32)(5) const CURL_SSLVERSION_TLSv1_2 = (UInt32)(6) const CURL_SSLVERSION_TLSv1_3 = (UInt32)(7) const CURL_SSLVERSION_LAST = (UInt32)(8) # end enum ANONYMOUS # begin enum ANONYMOUS const CURL_SSLVERSION_MAX_NONE = (UInt32)(0) const CURL_SSLVERSION_MAX_DEFAULT = (UInt32)(65536) const CURL_SSLVERSION_MAX_TLSv1_0 = (UInt32)(262144) const CURL_SSLVERSION_MAX_TLSv1_1 = (UInt32)(327680) const CURL_SSLVERSION_MAX_TLSv1_2 = (UInt32)(393216) const CURL_SSLVERSION_MAX_TLSv1_3 = (UInt32)(458752) const CURL_SSLVERSION_MAX_LAST = (UInt32)(524288) # end enum ANONYMOUS # begin enum CURL_TLSAUTH const CURL_TLSAUTH = UInt32 const CURL_TLSAUTH_NONE = (UInt32)(0) const CURL_TLSAUTH_SRP = (UInt32)(1) const CURL_TLSAUTH_LAST = (UInt32)(2) # end enum CURL_TLSAUTH const CURL_REDIR_GET_ALL = 0 const CURL_REDIR_POST_301 = 1 const CURL_REDIR_POST_302 = 2 const CURL_REDIR_POST_303 = 4 const CURL_REDIR_POST_ALL = (CURL_REDIR_POST_301 | CURL_REDIR_POST_302) | CURL_REDIR_POST_303 # begin enum curl_TimeCond const curl_TimeCond = UInt32 const CURL_TIMECOND_NONE = (UInt32)(0) const CURL_TIMECOND_IFMODSINCE = (UInt32)(1) const CURL_TIMECOND_IFUNMODSINCE = (UInt32)(2) const CURL_TIMECOND_LASTMOD = (UInt32)(3) const CURL_TIMECOND_LAST = (UInt32)(4) # end enum curl_TimeCond struct curl_mime_s end const curl_mime = Cvoid struct curl_mimepart_s end const curl_mimepart = Cvoid # begin enum CURLformoption const CURLformoption = UInt32 const CURLFORM_NOTHING = (UInt32)(0) const CURLFORM_COPYNAME = (UInt32)(1) const CURLFORM_PTRNAME = (UInt32)(2) const CURLFORM_NAMELENGTH = (UInt32)(3) const CURLFORM_COPYCONTENTS = (UInt32)(4) const CURLFORM_PTRCONTENTS = (UInt32)(5) const CURLFORM_CONTENTSLENGTH = (UInt32)(6) const CURLFORM_FILECONTENT = (UInt32)(7) const CURLFORM_ARRAY = (UInt32)(8) const CURLFORM_OBSOLETE = (UInt32)(9) const CURLFORM_FILE = (UInt32)(10) const CURLFORM_BUFFER = (UInt32)(11) const CURLFORM_BUFFERPTR = (UInt32)(12) const CURLFORM_BUFFERLENGTH = (UInt32)(13) const CURLFORM_CONTENTTYPE = (UInt32)(14) const CURLFORM_CONTENTHEADER = (UInt32)(15) const CURLFORM_FILENAME = (UInt32)(16) const CURLFORM_END = (UInt32)(17) const CURLFORM_OBSOLETE2 = (UInt32)(18) const CURLFORM_STREAM = (UInt32)(19) const CURLFORM_CONTENTLEN = (UInt32)(20) const CURLFORM_LASTENTRY = (UInt32)(21) # end enum CURLformoption mutable struct curl_forms option::CURLformoption value::Ptr{UInt8} end # begin enum CURLFORMcode const CURLFORMcode = UInt32 const CURL_FORMADD_OK = (UInt32)(0) const CURL_FORMADD_MEMORY = (UInt32)(1) const CURL_FORMADD_OPTION_TWICE = (UInt32)(2) const CURL_FORMADD_NULL = (UInt32)(3) const CURL_FORMADD_UNKNOWN_OPTION = (UInt32)(4) const CURL_FORMADD_INCOMPLETE = (UInt32)(5) const CURL_FORMADD_ILLEGAL_ARRAY = (UInt32)(6) const CURL_FORMADD_DISABLED = (UInt32)(7) const CURL_FORMADD_LAST = (UInt32)(8) # end enum CURLFORMcode const curl_formget_callback = Ptr{Cvoid} mutable struct curl_slist data::Ptr{UInt8} next::Ptr{Cvoid} end const curl_ssl_backend = Cvoid # begin enum CURLsslset const CURLsslset = UInt32 const CURLSSLSET_OK = (UInt32)(0) const CURLSSLSET_UNKNOWN_BACKEND = (UInt32)(1) const CURLSSLSET_TOO_LATE = (UInt32)(2) const CURLSSLSET_NO_BACKENDS = (UInt32)(3) # end enum CURLsslset mutable struct curl_certinfo num_of_certs::Cint certinfo::Ptr{Ptr{Cvoid}} end mutable struct curl_tlssessioninfo backend::curl_sslbackend internals::Ptr{Cvoid} end const CURLINFO_STRING = 0x00100000 const CURLINFO_LONG = 0x00200000 const CURLINFO_DOUBLE = 0x00300000 const CURLINFO_SLIST = 0x00400000 const CURLINFO_PTR = 0x00400000 const CURLINFO_SOCKET = 0x00500000 const CURLINFO_OFF_T = 0x00600000 const CURLINFO_MASK = 0x000fffff const CURLINFO_TYPEMASK = 0x00f00000 # begin enum CURLINFO const CURLINFO = UInt32 const CURLINFO_NONE = (UInt32)(0) const CURLINFO_EFFECTIVE_URL = (UInt32)(1048577) const CURLINFO_RESPONSE_CODE = (UInt32)(2097154) const CURLINFO_TOTAL_TIME = (UInt32)(3145731) const CURLINFO_NAMELOOKUP_TIME = (UInt32)(3145732) const CURLINFO_CONNECT_TIME = (UInt32)(3145733) const CURLINFO_PRETRANSFER_TIME = (UInt32)(3145734) const CURLINFO_SIZE_UPLOAD = (UInt32)(3145735) const CURLINFO_SIZE_UPLOAD_T = (UInt32)(6291463) const CURLINFO_SIZE_DOWNLOAD = (UInt32)(3145736) const CURLINFO_SIZE_DOWNLOAD_T = (UInt32)(6291464) const CURLINFO_SPEED_DOWNLOAD = (UInt32)(3145737) const CURLINFO_SPEED_DOWNLOAD_T = (UInt32)(6291465) const CURLINFO_SPEED_UPLOAD = (UInt32)(3145738) const CURLINFO_SPEED_UPLOAD_T = (UInt32)(6291466) const CURLINFO_HEADER_SIZE = (UInt32)(2097163) const CURLINFO_REQUEST_SIZE = (UInt32)(2097164) const CURLINFO_SSL_VERIFYRESULT = (UInt32)(2097165) const CURLINFO_FILETIME = (UInt32)(2097166) const CURLINFO_FILETIME_T = (UInt32)(6291470) const CURLINFO_CONTENT_LENGTH_DOWNLOAD = (UInt32)(3145743) const CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = (UInt32)(6291471) const CURLINFO_CONTENT_LENGTH_UPLOAD = (UInt32)(3145744) const CURLINFO_CONTENT_LENGTH_UPLOAD_T = (UInt32)(6291472) const CURLINFO_STARTTRANSFER_TIME = (UInt32)(3145745) const CURLINFO_CONTENT_TYPE = (UInt32)(1048594) const CURLINFO_REDIRECT_TIME = (UInt32)(3145747) const CURLINFO_REDIRECT_COUNT = (UInt32)(2097172) const CURLINFO_PRIVATE = (UInt32)(1048597) const CURLINFO_HTTP_CONNECTCODE = (UInt32)(2097174) const CURLINFO_HTTPAUTH_AVAIL = (UInt32)(2097175) const CURLINFO_PROXYAUTH_AVAIL = (UInt32)(2097176) const CURLINFO_OS_ERRNO = (UInt32)(2097177) const CURLINFO_NUM_CONNECTS = (UInt32)(2097178) const CURLINFO_SSL_ENGINES = (UInt32)(4194331) const CURLINFO_COOKIELIST = (UInt32)(4194332) const CURLINFO_LASTSOCKET = (UInt32)(2097181) const CURLINFO_FTP_ENTRY_PATH = (UInt32)(1048606) const CURLINFO_REDIRECT_URL = (UInt32)(1048607) const CURLINFO_PRIMARY_IP = (UInt32)(1048608) const CURLINFO_APPCONNECT_TIME = (UInt32)(3145761) const CURLINFO_CERTINFO = (UInt32)(4194338) const CURLINFO_CONDITION_UNMET = (UInt32)(2097187) const CURLINFO_RTSP_SESSION_ID = (UInt32)(1048612) const CURLINFO_RTSP_CLIENT_CSEQ = (UInt32)(2097189) const CURLINFO_RTSP_SERVER_CSEQ = (UInt32)(2097190) const CURLINFO_RTSP_CSEQ_RECV = (UInt32)(2097191) const CURLINFO_PRIMARY_PORT = (UInt32)(2097192) const CURLINFO_LOCAL_IP = (UInt32)(1048617) const CURLINFO_LOCAL_PORT = (UInt32)(2097194) const CURLINFO_TLS_SESSION = (UInt32)(4194347) const CURLINFO_ACTIVESOCKET = (UInt32)(5242924) const CURLINFO_TLS_SSL_PTR = (UInt32)(4194349) const CURLINFO_HTTP_VERSION = (UInt32)(2097198) const CURLINFO_PROXY_SSL_VERIFYRESULT = (UInt32)(2097199) const CURLINFO_PROTOCOL = (UInt32)(2097200) const CURLINFO_SCHEME = (UInt32)(1048625) const CURLINFO_TOTAL_TIME_T = (UInt32)(6291506) const CURLINFO_NAMELOOKUP_TIME_T = (UInt32)(6291507) const CURLINFO_CONNECT_TIME_T = (UInt32)(6291508) const CURLINFO_PRETRANSFER_TIME_T = (UInt32)(6291509) const CURLINFO_STARTTRANSFER_TIME_T = (UInt32)(6291510) const CURLINFO_REDIRECT_TIME_T = (UInt32)(6291511) const CURLINFO_APPCONNECT_TIME_T = (UInt32)(6291512) const CURLINFO_LASTONE = (UInt32)(56) # end enum CURLINFO const CURLINFO_HTTP_CODE = CURLINFO_RESPONSE_CODE # begin enum curl_closepolicy const curl_closepolicy = UInt32 const CURLCLOSEPOLICY_NONE = (UInt32)(0) const CURLCLOSEPOLICY_OLDEST = (UInt32)(1) const CURLCLOSEPOLICY_LEAST_RECENTLY_USED = (UInt32)(2) const CURLCLOSEPOLICY_LEAST_TRAFFIC = (UInt32)(3) const CURLCLOSEPOLICY_SLOWEST = (UInt32)(4) const CURLCLOSEPOLICY_CALLBACK = (UInt32)(5) const CURLCLOSEPOLICY_LAST = (UInt32)(6) # end enum curl_closepolicy const CURL_GLOBAL_SSL = 1 << 0 const CURL_GLOBAL_WIN32 = 1 << 1 const CURL_GLOBAL_ALL = CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32 const CURL_GLOBAL_NOTHING = 0 const CURL_GLOBAL_DEFAULT = CURL_GLOBAL_ALL const CURL_GLOBAL_ACK_EINTR = 1 << 2 # begin enum curl_lock_data const curl_lock_data = UInt32 const CURL_LOCK_DATA_NONE = (UInt32)(0) const CURL_LOCK_DATA_SHARE = (UInt32)(1) const CURL_LOCK_DATA_COOKIE = (UInt32)(2) const CURL_LOCK_DATA_DNS = (UInt32)(3) const CURL_LOCK_DATA_SSL_SESSION = (UInt32)(4) const CURL_LOCK_DATA_CONNECT = (UInt32)(5) const CURL_LOCK_DATA_PSL = (UInt32)(6) const CURL_LOCK_DATA_LAST = (UInt32)(7) # end enum curl_lock_data # begin enum curl_lock_access const curl_lock_access = UInt32 const CURL_LOCK_ACCESS_NONE = (UInt32)(0) const CURL_LOCK_ACCESS_SHARED = (UInt32)(1) const CURL_LOCK_ACCESS_SINGLE = (UInt32)(2) const CURL_LOCK_ACCESS_LAST = (UInt32)(3) # end enum curl_lock_access const curl_lock_function = Ptr{Cvoid} const curl_unlock_function = Ptr{Cvoid} # begin enum CURLSHcode const CURLSHcode = UInt32 const CURLSHE_OK = (UInt32)(0) const CURLSHE_BAD_OPTION = (UInt32)(1) const CURLSHE_IN_USE = (UInt32)(2) const CURLSHE_INVALID = (UInt32)(3) const CURLSHE_NOMEM = (UInt32)(4) const CURLSHE_NOT_BUILT_IN = (UInt32)(5) const CURLSHE_LAST = (UInt32)(6) # end enum CURLSHcode # begin enum CURLSHoption const CURLSHoption = UInt32 const CURLSHOPT_NONE = (UInt32)(0) const CURLSHOPT_SHARE = (UInt32)(1) const CURLSHOPT_UNSHARE = (UInt32)(2) const CURLSHOPT_LOCKFUNC = (UInt32)(3) const CURLSHOPT_UNLOCKFUNC = (UInt32)(4) const CURLSHOPT_USERDATA = (UInt32)(5) const CURLSHOPT_LAST = (UInt32)(6) # end enum CURLSHoption # begin enum CURLversion const CURLversion = UInt32 const CURLVERSION_FIRST = (UInt32)(0) const CURLVERSION_SECOND = (UInt32)(1) const CURLVERSION_THIRD = (UInt32)(2) const CURLVERSION_FOURTH = (UInt32)(3) const CURLVERSION_FIFTH = (UInt32)(4) const CURLVERSION_LAST = (UInt32)(5) # end enum CURLversion const CURLVERSION_NOW = CURLVERSION_FIFTH mutable struct curl_version_info_data age::CURLversion version::Ptr{UInt8} version_num::UInt32 host::Ptr{UInt8} features::Int32 ssl_version::Ptr{UInt8} ssl_version_num::Int32 libz_version::Ptr{UInt8} protocols::Ptr{Ptr{UInt8}} ares::Ptr{UInt8} ares_num::Int32 libidn::Ptr{UInt8} iconv_ver_num::Int32 libssh_version::Ptr{UInt8} brotli_ver_num::UInt32 brotli_version::Ptr{UInt8} end const CURL_VERSION_IPV6 = 1 << 0 const CURL_VERSION_KERBEROS4 = 1 << 1 const CURL_VERSION_SSL = 1 << 2 const CURL_VERSION_LIBZ = 1 << 3 const CURL_VERSION_NTLM = 1 << 4 const CURL_VERSION_GSSNEGOTIATE = 1 << 5 const CURL_VERSION_DEBUG = 1 << 6 const CURL_VERSION_ASYNCHDNS = 1 << 7 const CURL_VERSION_SPNEGO = 1 << 8 const CURL_VERSION_LARGEFILE = 1 << 9 const CURL_VERSION_IDN = 1 << 10 const CURL_VERSION_SSPI = 1 << 11 const CURL_VERSION_CONV = 1 << 12 const CURL_VERSION_CURLDEBUG = 1 << 13 const CURL_VERSION_TLSAUTH_SRP = 1 << 14 const CURL_VERSION_NTLM_WB = 1 << 15 const CURL_VERSION_HTTP2 = 1 << 16 const CURL_VERSION_GSSAPI = 1 << 17 const CURL_VERSION_KERBEROS5 = 1 << 18 const CURL_VERSION_UNIX_SOCKETS = 1 << 19 const CURL_VERSION_PSL = 1 << 20 const CURL_VERSION_HTTPS_PROXY = 1 << 21 const CURL_VERSION_MULTI_SSL = 1 << 22 const CURL_VERSION_BROTLI = 1 << 23 const CURLPAUSE_RECV = 1 << 0 const CURLPAUSE_RECV_CONT = 0 const CURLPAUSE_SEND = 1 << 2 const CURLPAUSE_SEND_CONT = 0 const CURLPAUSE_ALL = CURLPAUSE_RECV | CURLPAUSE_SEND const CURLPAUSE_CONT = CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT const CURLE_OBSOLETE16 = CURLE_HTTP2 const CURLE_OBSOLETE10 = CURLE_FTP_ACCEPT_FAILED const CURLE_OBSOLETE12 = CURLE_FTP_ACCEPT_TIMEOUT const CURLOPT_ENCODING = CURLOPT_ACCEPT_ENCODING const CURLE_FTP_WEIRD_SERVER_REPLY = CURLE_WEIRD_SERVER_REPLY const CURLE_UNKNOWN_TELNET_OPTION = CURLE_UNKNOWN_OPTION const CURLE_SSL_PEER_CERTIFICATE = CURLE_PEER_FAILED_VERIFICATION const CURLE_OBSOLETE = CURLE_OBSOLETE50 const CURLE_BAD_PASSWORD_ENTERED = CURLE_OBSOLETE46 const CURLE_BAD_CALLING_ORDER = CURLE_OBSOLETE44 const CURLE_FTP_USER_PASSWORD_INCORRECT = CURLE_OBSOLETE10 const CURLE_FTP_CANT_RECONNECT = CURLE_OBSOLETE16 const CURLE_FTP_COULDNT_GET_SIZE = CURLE_OBSOLETE32 const CURLE_FTP_COULDNT_SET_ASCII = CURLE_OBSOLETE29 const CURLE_FTP_WEIRD_USER_REPLY = CURLE_OBSOLETE12 const CURLE_FTP_WRITE_ERROR = CURLE_OBSOLETE20 const CURLE_LIBRARY_NOT_FOUND = CURLE_OBSOLETE40 const CURLE_MALFORMAT_USER = CURLE_OBSOLETE24 const CURLE_SHARE_IN_USE = CURLE_OBSOLETE57 const CURLE_URL_MALFORMAT_USER = CURLE_NOT_BUILT_IN const CURLE_FTP_ACCESS_DENIED = CURLE_REMOTE_ACCESS_DENIED const CURLE_FTP_COULDNT_SET_BINARY = CURLE_FTP_COULDNT_SET_TYPE const CURLE_FTP_QUOTE_ERROR = CURLE_QUOTE_ERROR const CURLE_TFTP_DISKFULL = CURLE_REMOTE_DISK_FULL const CURLE_TFTP_EXISTS = CURLE_REMOTE_FILE_EXISTS const CURLE_HTTP_RANGE_ERROR = CURLE_RANGE_ERROR const CURLE_FTP_SSL_FAILED = CURLE_USE_SSL_FAILED const CURLE_OPERATION_TIMEOUTED = CURLE_OPERATION_TIMEDOUT const CURLE_HTTP_NOT_FOUND = CURLE_HTTP_RETURNED_ERROR const CURLE_HTTP_PORT_FAILED = CURLE_INTERFACE_FAILED const CURLE_FTP_COULDNT_STOR_FILE = CURLE_UPLOAD_FAILED const CURLE_FTP_PARTIAL_FILE = CURLE_PARTIAL_FILE const CURLE_FTP_BAD_DOWNLOAD_RESUME = CURLE_BAD_DOWNLOAD_RESUME const CURLE_ALREADY_COMPLETE = 99999 const CURLOPT_FILE = CURLOPT_WRITEDATA const CURLOPT_INFILE = CURLOPT_READDATA const CURLOPT_WRITEHEADER = CURLOPT_HEADERDATA const CURLOPT_WRITEINFO = CURLOPT_OBSOLETE40 const CURLOPT_CLOSEPOLICY = CURLOPT_OBSOLETE72 # multi.h const CURLM = Cvoid # begin enum CURLMcode const CURLMcode = Cint const CURLM_CALL_MULTI_PERFORM = (Int32)(-1) const CURLM_OK = (Int32)(0) const CURLM_BAD_HANDLE = (Int32)(1) const CURLM_BAD_EASY_HANDLE = (Int32)(2) const CURLM_OUT_OF_MEMORY = (Int32)(3) const CURLM_INTERNAL_ERROR = (Int32)(4) const CURLM_BAD_SOCKET = (Int32)(5) const CURLM_UNKNOWN_OPTION = (Int32)(6) const CURLM_ADDED_ALREADY = (Int32)(7) const CURLM_RECURSIVE_API_CALL = (Int32)(8) const CURLM_LAST = (Int32)(9) # end enum CURLMcode const CURLM_CALL_MULTI_SOCKET = CURLM_CALL_MULTI_PERFORM const CURLPIPE_NOTHING = Clong(0) const CURLPIPE_HTTP1 = Clong(1) const CURLPIPE_MULTIPLEX = Clong(2) # begin enum CURLMSG const CURLMSG = UInt32 const CURLMSG_NONE = (UInt32)(0) const CURLMSG_DONE = (UInt32)(1) const CURLMSG_LAST = (UInt32)(2) # end enum CURLMSG mutable struct CURLMsg msg::CURLMSG easy_handle::Ptr{CURL} data::Cvoid end const CURL_WAIT_POLLIN = 0x0001 const CURL_WAIT_POLLPRI = 0x0002 const CURL_WAIT_POLLOUT = 0x0004 mutable struct curl_waitfd fd::curl_socket_t events::Int16 revents::Int16 end const CURL_POLL_NONE = 0 const CURL_POLL_IN = 1 const CURL_POLL_OUT = 2 const CURL_POLL_INOUT = 3 const CURL_POLL_REMOVE = 4 const CURL_SOCKET_TIMEOUT = CURL_SOCKET_BAD const CURL_CSELECT_IN = 0x01 const CURL_CSELECT_OUT = 0x02 const CURL_CSELECT_ERR = 0x04 const curl_socket_callback = Ptr{Cvoid} const curl_multi_timer_callback = Ptr{Cvoid} # begin enum CURLMoption const CURLMoption = UInt32 const CURLMOPT_SOCKETFUNCTION = (UInt32)(20001) const CURLMOPT_SOCKETDATA = (UInt32)(10002) const CURLMOPT_PIPELINING = (UInt32)(3) const CURLMOPT_TIMERFUNCTION = (UInt32)(20004) const CURLMOPT_TIMERDATA = (UInt32)(10005) const CURLMOPT_MAXCONNECTS = (UInt32)(6) const CURLMOPT_MAX_HOST_CONNECTIONS = (UInt32)(7) const CURLMOPT_MAX_PIPELINE_LENGTH = (UInt32)(8) const CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE = (UInt32)(30009) const CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE = (UInt32)(30010) const CURLMOPT_PIPELINING_SITE_BL = (UInt32)(10011) const CURLMOPT_PIPELINING_SERVER_BL = (UInt32)(10012) const CURLMOPT_MAX_TOTAL_CONNECTIONS = (UInt32)(13) const CURLMOPT_PUSHFUNCTION = (UInt32)(20014) const CURLMOPT_PUSHDATA = (UInt32)(10015) const CURLMOPT_LASTENTRY = (UInt32)(10016) # end enum CURLMoption const CURL_PUSH_OK = 0 const CURL_PUSH_DENY = 1 struct curl_pushheaders end const curl_push_callback = Ptr{Cvoid} t���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/lC_curl_h.jl{$������# Julia wrapper for header: /usr/local/opt/curl/include/curl/curl.h # Automatically generated using Clang.jl wrap_c, version 0.0.0 function curl_strequal(s1, s2) ccall((:curl_strequal, libcurl), Cint, (Ptr{UInt8}, Ptr{UInt8}), s1, s2) end function curl_strnequal(s1, s2, n) ccall((:curl_strnequal, libcurl), Cint, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), s1, s2, n) end function curl_mime_init(easy) ccall((:curl_mime_init, libcurl), Ptr{curl_mime}, (Ptr{CURL},), easy) end function curl_mime_free(mime) ccall((:curl_mime_free, libcurl), Cvoid, (Ptr{curl_mime},), mime) end function curl_mime_addpart(mime) ccall((:curl_mime_addpart, libcurl), Ptr{curl_mimepart}, (Ptr{curl_mime},), mime) end function curl_mime_name(part, name) ccall((:curl_mime_name, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}), part, name) end function curl_mime_filename(part, filename) ccall((:curl_mime_filename, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}), part, filename) end function curl_mime_type(part, mimetype) ccall((:curl_mime_type, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}), part, mimetype) end function curl_mime_encoder(part, encoding) ccall((:curl_mime_encoder, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}), part, encoding) end function curl_mime_data(part, data, datasize) ccall((:curl_mime_data, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}, Csize_t), part, data, datasize) end function curl_mime_filedata(part, filename) ccall((:curl_mime_filedata, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{UInt8}), part, filename) end function curl_mime_data_cb(part, datasize, readfunc, seekfunc, freefunc, arg) ccall((:curl_mime_data_cb, libcurl), CURLcode, (Ptr{curl_mimepart}, curl_off_t, curl_read_callback, curl_seek_callback, curl_free_callback, Ptr{Cvoid}), part, datasize, readfunc, seekfunc, freefunc, arg) end function curl_mime_subparts(part, subparts) ccall((:curl_mime_subparts, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{curl_mime}), part, subparts) end function curl_mime_headers(part, headers, take_ownership) ccall((:curl_mime_headers, libcurl), CURLcode, (Ptr{curl_mimepart}, Ptr{Cvoid}, Cint), part, headers, take_ownership) end function curl_formget(form, arg, append) ccall((:curl_formget, libcurl), Cint, (Ptr{Cvoid}, Ptr{Cvoid}, curl_formget_callback), form, arg, append) end function curl_formfree(form) ccall((:curl_formfree, libcurl), Cvoid, (Ptr{Cvoid},), form) end function curl_getenv(variable) ccall((:curl_getenv, libcurl), Ptr{UInt8}, (Ptr{UInt8},), variable) end function curl_version() ccall((:curl_version, libcurl), Ptr{UInt8}, ()) end function curl_easy_escape(handle, string, length) ccall((:curl_easy_escape, libcurl), Ptr{UInt8}, (Ptr{CURL}, Ptr{UInt8}, Cint), handle, string, length) end function curl_escape(string, length) ccall((:curl_escape, libcurl), Ptr{UInt8}, (Ptr{UInt8}, Cint), string, length) end function curl_easy_unescape(handle, string, length, outlength) ccall((:curl_easy_unescape, libcurl), Ptr{UInt8}, (Ptr{CURL}, Ptr{UInt8}, Cint, Ptr{Cint}), handle, string, length, outlength) end function curl_unescape(string, length) ccall((:curl_unescape, libcurl), Ptr{UInt8}, (Ptr{UInt8}, Cint), string, length) end function curl_free(p) ccall((:curl_free, libcurl), Cvoid, (Ptr{Cvoid},), p) end function curl_global_init(flags) ccall((:curl_global_init, libcurl), CURLcode, (Clong,), flags) end function curl_global_init_mem(flags, m, f, r, s, c) ccall((:curl_global_init_mem, libcurl), CURLcode, (Clong, curl_malloc_callback, curl_free_callback, curl_realloc_callback, curl_strdup_callback, curl_calloc_callback), flags, m, f, r, s, c) end function curl_global_cleanup() ccall((:curl_global_cleanup, libcurl), Cvoid, ()) end function curl_global_sslset(id, name, avail) ccall((:curl_global_sslset, libcurl), CURLsslset, (curl_sslbackend, Ptr{UInt8}, Ptr{Ptr{Ptr{curl_ssl_backend}}}), id, name, avail) end function curl_slist_append(arg1, arg2) ccall((:curl_slist_append, libcurl), Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{UInt8}), arg1, arg2) end function curl_slist_free_all(arg1) ccall((:curl_slist_free_all, libcurl), Cvoid, (Ptr{Cvoid},), arg1) end function curl_getdate(p, unused) ccall((:curl_getdate, libcurl), time_t, (Ptr{UInt8}, Ptr{time_t}), p, unused) end function curl_share_init() ccall((:curl_share_init, libcurl), Ptr{CURLSH}, ()) end function curl_share_setopt(handle, opt, param) ccall((:curl_share_setopt, libcurl), CURLSHcode, (Ptr{CURLSH}, CURLSHoption, Any...), handle, opt, param) end function curl_share_cleanup(arg1) ccall((:curl_share_cleanup, libcurl), CURLSHcode, (Ptr{CURLSH},), arg1) end function curl_version_info(arg1) ccall((:curl_version_info, libcurl), Ptr{curl_version_info_data}, (CURLversion,), arg1) end function curl_easy_strerror(arg1) ccall((:curl_easy_strerror, libcurl), Ptr{UInt8}, (CURLcode,), arg1) end function curl_share_strerror(arg1) ccall((:curl_share_strerror, libcurl), Ptr{UInt8}, (CURLSHcode,), arg1) end function curl_easy_pause(handle, bitmask) ccall((:curl_easy_pause, libcurl), CURLcode, (Ptr{CURL}, Cint), handle, bitmask) end function curl_easy_init() ccall((:curl_easy_init, libcurl), Ptr{CURL}, ()) end function curl_easy_setopt(handle, opt, param) ccall((:curl_easy_setopt, libcurl), CURLcode, (Ptr{CURL}, CURLoption, Any...), handle, opt, param) end function curl_easy_perform(curl) ccall((:curl_easy_perform, libcurl), CURLcode, (Ptr{CURL},), curl) end function curl_easy_cleanup(curl) ccall((:curl_easy_cleanup, libcurl), Cvoid, (Ptr{CURL},), curl) end function curl_easy_getinfo(handle, info, arg) ccall((:curl_easy_getinfo, libcurl), CURLcode, (Ptr{CURL}, CURLINFO, Any...), handle, info, arg) end function curl_easy_duphandle(curl) ccall((:curl_easy_duphandle, libcurl), Ptr{CURL}, (Ptr{CURL},), curl) end function curl_easy_reset(curl) ccall((:curl_easy_reset, libcurl), Cvoid, (Ptr{CURL},), curl) end function curl_easy_recv(curl, buffer, buflen, n) ccall((:curl_easy_recv, libcurl), CURLcode, (Ptr{CURL}, Ptr{Cvoid}, Csize_t, Ptr{Csize_t}), curl, buffer, buflen, n) end function curl_easy_send(curl, buffer, buflen, n) ccall((:curl_easy_send, libcurl), CURLcode, (Ptr{CURL}, Ptr{Cvoid}, Csize_t, Ptr{Csize_t}), curl, buffer, buflen, n) end function curl_multi_init() ccall((:curl_multi_init, libcurl), Ptr{CURLM}, ()) end function curl_multi_add_handle(multi_handle, curl_handle) ccall((:curl_multi_add_handle, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{CURL}), multi_handle, curl_handle) end function curl_multi_remove_handle(multi_handle, curl_handle) ccall((:curl_multi_remove_handle, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{CURL}), multi_handle, curl_handle) end function curl_multi_fdset(multi_handle, read_fd_set, write_fd_set, exc_fd_set, max_fd) ccall((:curl_multi_fdset, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{fd_set}, Ptr{fd_set}, Ptr{fd_set}, Ptr{Cint}), multi_handle, read_fd_set, write_fd_set, exc_fd_set, max_fd) end function curl_multi_wait(multi_handle, extra_fds, extra_nfds, timeout_ms, ret) ccall((:curl_multi_wait, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{Cvoid}, UInt32, Cint, Ptr{Cint}), multi_handle, extra_fds, extra_nfds, timeout_ms, ret) end function curl_multi_perform(multi_handle, running_handles) ccall((:curl_multi_perform, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{Cint}), multi_handle, running_handles) end function curl_multi_cleanup(multi_handle) ccall((:curl_multi_cleanup, libcurl), CURLMcode, (Ptr{CURLM},), multi_handle) end function curl_multi_info_read(multi_handle, msgs_in_queue) ccall((:curl_multi_info_read, libcurl), Ptr{CURLMsg}, (Ptr{CURLM}, Ptr{Cint}), multi_handle, msgs_in_queue) end function curl_multi_strerror(arg1) ccall((:curl_multi_strerror, libcurl), Ptr{UInt8}, (CURLMcode,), arg1) end function curl_multi_socket(multi_handle, s, running_handles) ccall((:curl_multi_socket, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Ptr{Cint}), multi_handle, s, running_handles) end function curl_multi_socket_action(multi_handle, s, ev_bitmask, running_handles) ccall((:curl_multi_socket_action, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Cint, Ptr{Cint}), multi_handle, s, ev_bitmask, running_handles) end function curl_multi_socket_all(multi_handle, running_handles) ccall((:curl_multi_socket_all, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{Cint}), multi_handle, running_handles) end function curl_multi_timeout(multi_handle, milliseconds) ccall((:curl_multi_timeout, libcurl), CURLMcode, (Ptr{CURLM}, Ptr{Clong}), multi_handle, milliseconds) end function curl_multi_setopt(multi_handle, opt, param) ccall((:curl_multi_setopt, libcurl), CURLMcode, (Ptr{CURLM}, CURLMoption, Any...), multi_handle, opt, param) end function curl_multi_assign(multi_handle, sockfd, sockp) ccall((:curl_multi_assign, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Ptr{Cvoid}), multi_handle, sockfd, sockp) end function curl_pushheader_bynum(h, num) ccall((:curl_pushheader_bynum, libcurl), Ptr{UInt8}, (Ptr{Cvoid}, Csize_t), h, num) end function curl_pushheader_byname(h, name) ccall((:curl_pushheader_byname, libcurl), Ptr{UInt8}, (Ptr{Cvoid}, Ptr{UInt8}), h, name) end w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/lC_defines_h.jl3������# Generating #define constants const CURL_TYPEOF_CURL_OFF_T = Clong const CURL_FORMAT_CURL_OFF_T = "ld" const CURL_FORMAT_CURL_OFF_TU = "lu" const CURL_SUFFIX_CURL_OFF_T = Clong const CURL_SUFFIX_CURL_OFF_TU = Culong const CURL_TYPEOF_CURL_SOCKLEN_T = socklen_t const CURL_PULL_SYS_TYPES_H = 1 const CURL_PULL_SYS_SOCKET_H = 1 const CURL_SOCKET_BAD = -1 const CURLSSLBACKEND_LIBRESSL = CURLSSLBACKEND_OPENSSL const CURLSSLBACKEND_BORINGSSL = CURLSSLBACKEND_OPENSSL const CURLSSLBACKEND_CYASSL = CURLSSLBACKEND_WOLFSSL const CURL_HTTPPOST_FILENAME = (1<<0) const CURL_HTTPPOST_READFILE = (1<<1) const CURL_HTTPPOST_PTRNAME = (1<<2) const CURL_HTTPPOST_PTRCONTENTS = (1<<3) const CURL_HTTPPOST_BUFFER = (1<<4) const CURL_HTTPPOST_PTRBUFFER = (1<<5) const CURL_HTTPPOST_CALLBACK = (1<<6) const CURL_HTTPPOST_LARGE = (1<<7) const CURL_MAX_READ_SIZE = 524288 const CURL_MAX_WRITE_SIZE = 16384 const CURL_MAX_HTTP_HEADER = (100*1024) const CURL_WRITEFUNC_PAUSE = 0x10000001 const CURLFINFOFLAG_KNOWN_FILENAME = (1<<0) const CURLFINFOFLAG_KNOWN_FILETYPE = (1<<1) const CURLFINFOFLAG_KNOWN_TIME = (1<<2) const CURLFINFOFLAG_KNOWN_PERM = (1<<3) const CURLFINFOFLAG_KNOWN_UID = (1<<4) const CURLFINFOFLAG_KNOWN_GID = (1<<5) const CURLFINFOFLAG_KNOWN_SIZE = (1<<6) const CURLFINFOFLAG_KNOWN_HLINKCOUNT = (1<<7) const CURL_CHUNK_BGN_FUNC_OK = 0 const CURL_CHUNK_BGN_FUNC_FAIL = 1 const CURL_CHUNK_BGN_FUNC_SKIP = 2 const CURL_CHUNK_END_FUNC_OK = 0 const CURL_CHUNK_END_FUNC_FAIL = 1 const CURL_FNMATCHFUNC_MATCH = 0 const CURL_FNMATCHFUNC_NOMATCH = 1 const CURL_FNMATCHFUNC_FAIL = 2 const CURL_SEEKFUNC_OK = 0 const CURL_SEEKFUNC_FAIL = 1 const CURL_SEEKFUNC_CANTSEEK = 2 const CURL_READFUNC_ABORT = 0x10000000 const CURL_READFUNC_PAUSE = 0x10000001 const CURL_SOCKOPT_OK = 0 const CURL_SOCKOPT_ERROR = 1 const CURL_SOCKOPT_ALREADY_CONNECTED = 2 const CURLE_OBSOLETE16 = CURLE_HTTP2 const CURLE_OBSOLETE10 = CURLE_FTP_ACCEPT_FAILED const CURLE_OBSOLETE12 = CURLE_FTP_ACCEPT_TIMEOUT const CURLOPT_ENCODING = CURLOPT_ACCEPT_ENCODING const CURLE_FTP_WEIRD_SERVER_REPLY = CURLE_WEIRD_SERVER_REPLY const CURLE_UNKNOWN_TELNET_OPTION = CURLE_UNKNOWN_OPTION const CURLE_SSL_PEER_CERTIFICATE = CURLE_PEER_FAILED_VERIFICATION const CURLE_OBSOLETE = CURLE_OBSOLETE50 const CURLE_BAD_PASSWORD_ENTERED = CURLE_OBSOLETE46 const CURLE_BAD_CALLING_ORDER = CURLE_OBSOLETE44 const CURLE_FTP_USER_PASSWORD_INCORRECT = CURLE_OBSOLETE10 const CURLE_FTP_CANT_RECONNECT = CURLE_OBSOLETE16 const CURLE_FTP_COULDNT_GET_SIZE = CURLE_OBSOLETE32 const CURLE_FTP_COULDNT_SET_ASCII = CURLE_OBSOLETE29 const CURLE_FTP_WEIRD_USER_REPLY = CURLE_OBSOLETE12 const CURLE_FTP_WRITE_ERROR = CURLE_OBSOLETE20 const CURLE_LIBRARY_NOT_FOUND = CURLE_OBSOLETE40 const CURLE_MALFORMAT_USER = CURLE_OBSOLETE24 const CURLE_SHARE_IN_USE = CURLE_OBSOLETE57 const CURLE_URL_MALFORMAT_USER = CURLE_NOT_BUILT_IN const CURLE_FTP_ACCESS_DENIED = CURLE_REMOTE_ACCESS_DENIED const CURLE_FTP_COULDNT_SET_BINARY = CURLE_FTP_COULDNT_SET_TYPE const CURLE_FTP_QUOTE_ERROR = CURLE_QUOTE_ERROR const CURLE_TFTP_DISKFULL = CURLE_REMOTE_DISK_FULL const CURLE_TFTP_EXISTS = CURLE_REMOTE_FILE_EXISTS const CURLE_HTTP_RANGE_ERROR = CURLE_RANGE_ERROR const CURLE_FTP_SSL_FAILED = CURLE_USE_SSL_FAILED const CURLE_OPERATION_TIMEOUTED = CURLE_OPERATION_TIMEDOUT const CURLE_HTTP_NOT_FOUND = CURLE_HTTP_RETURNED_ERROR const CURLE_HTTP_PORT_FAILED = CURLE_INTERFACE_FAILED const CURLE_FTP_COULDNT_STOR_FILE = CURLE_UPLOAD_FAILED const CURLE_FTP_PARTIAL_FILE = CURLE_PARTIAL_FILE const CURLE_FTP_BAD_DOWNLOAD_RESUME = CURLE_BAD_DOWNLOAD_RESUME const CURLE_ALREADY_COMPLETE = 99999 const CURLOPT_FILE = CURLOPT_WRITEDATA const CURLOPT_INFILE = CURLOPT_READDATA const CURLOPT_WRITEHEADER = CURLOPT_HEADERDATA const CURLOPT_WRITEINFO = CURLOPT_OBSOLETE40 const CURLOPT_CLOSEPOLICY = CURLOPT_OBSOLETE72 const CURLAUTH_NONE = (0) const CURLAUTH_BASIC = ((1)<<0) const CURLAUTH_DIGEST = ((1)<<1) const CURLAUTH_NEGOTIATE = ((1)<<2) const CURLAUTH_GSSNEGOTIATE = CURLAUTH_NEGOTIATE const CURLAUTH_GSSAPI = CURLAUTH_NEGOTIATE const CURLAUTH_NTLM = ((1)<<3) const CURLAUTH_DIGEST_IE = ((1)<<4) const CURLAUTH_NTLM_WB = ((1)<<5) const CURLAUTH_BEARER = ((1)<<6) const CURLAUTH_ONLY = ((1)<<31) const CURLAUTH_ANY = (~CURLAUTH_DIGEST_IE) const CURLAUTH_ANYSAFE = (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) const CURLSSH_AUTH_ANY = ~0 const CURLSSH_AUTH_NONE = 0 const CURLSSH_AUTH_PUBLICKEY = (1<<0) const CURLSSH_AUTH_PASSWORD = (1<<1) const CURLSSH_AUTH_HOST = (1<<2) const CURLSSH_AUTH_KEYBOARD = (1<<3) const CURLSSH_AUTH_AGENT = (1<<4) const CURLSSH_AUTH_GSSAPI = (1<<5) const CURLSSH_AUTH_DEFAULT = CURLSSH_AUTH_ANY const CURLGSSAPI_DELEGATION_NONE = 0 const CURLGSSAPI_DELEGATION_POLICY_FLAG = (1<<0) const CURLGSSAPI_DELEGATION_FLAG = (1<<1) const CURL_ERROR_SIZE = 256 const CURLSSLOPT_ALLOW_BEAST = (1<<0) const CURLSSLOPT_NO_REVOKE = (1<<1) const CURL_HET_DEFAULT = Clong(200) const CURLFTPSSL_NONE = CURLUSESSL_NONE const CURLFTPSSL_TRY = CURLUSESSL_TRY const CURLFTPSSL_CONTROL = CURLUSESSL_CONTROL const CURLFTPSSL_ALL = CURLUSESSL_ALL const CURLFTPSSL_LAST = CURLUSESSL_LAST const CURLHEADER_UNIFIED = 0 const CURLHEADER_SEPARATE = (1<<0) const CURLPROTO_HTTP = (1<<0) const CURLPROTO_HTTPS = (1<<1) const CURLPROTO_FTP = (1<<2) const CURLPROTO_FTPS = (1<<3) const CURLPROTO_SCP = (1<<4) const CURLPROTO_SFTP = (1<<5) const CURLPROTO_TELNET = (1<<6) const CURLPROTO_LDAP = (1<<7) const CURLPROTO_LDAPS = (1<<8) const CURLPROTO_DICT = (1<<9) const CURLPROTO_FILE = (1<<10) const CURLPROTO_TFTP = (1<<11) const CURLPROTO_IMAP = (1<<12) const CURLPROTO_IMAPS = (1<<13) const CURLPROTO_POP3 = (1<<14) const CURLPROTO_POP3S = (1<<15) const CURLPROTO_SMTP = (1<<16) const CURLPROTO_SMTPS = (1<<17) const CURLPROTO_RTSP = (1<<18) const CURLPROTO_RTMP = (1<<19) const CURLPROTO_RTMPT = (1<<20) const CURLPROTO_RTMPE = (1<<21) const CURLPROTO_RTMPTE = (1<<22) const CURLPROTO_RTMPS = (1<<23) const CURLPROTO_RTMPTS = (1<<24) const CURLPROTO_GOPHER = (1<<25) const CURLPROTO_SMB = (1<<26) const CURLPROTO_SMBS = (1<<27) const CURLPROTO_ALL = (~0) const CURLOPTTYPE_LONG = 0 const CURLOPTTYPE_OBJECTPOINT = 10000 const CURLOPTTYPE_STRINGPOINT = 10000 const CURLOPTTYPE_FUNCTIONPOINT = 20000 const CURLOPTTYPE_OFF_T = 30000 const CURLOPT_XFERINFODATA = CURLOPT_PROGRESSDATA const CURLOPT_SERVER_RESPONSE_TIMEOUT = CURLOPT_FTP_RESPONSE_TIMEOUT const CURLOPT_POST301 = CURLOPT_POSTREDIR const CURLOPT_SSLKEYPASSWD = CURLOPT_KEYPASSWD const CURLOPT_FTPAPPEND = CURLOPT_APPEND const CURLOPT_FTPLISTONLY = CURLOPT_DIRLISTONLY const CURLOPT_FTP_SSL = CURLOPT_USE_SSL const CURLOPT_SSLCERTPASSWD = CURLOPT_KEYPASSWD const CURLOPT_KRB4LEVEL = CURLOPT_KRBLEVEL const CURL_IPRESOLVE_WHATEVER = 0 const CURL_IPRESOLVE_V4 = 1 const CURL_IPRESOLVE_V6 = 2 const CURLOPT_RTSPHEADER = CURLOPT_HTTPHEADER const CURL_HTTP_VERSION_2 = CURL_HTTP_VERSION_2_0 const CURL_REDIR_GET_ALL = 0 const CURL_REDIR_POST_301 = 1 const CURL_REDIR_POST_302 = 2 const CURL_REDIR_POST_303 = 4 const CURL_REDIR_POST_ALL = (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) const CURL_ZERO_TERMINATED = reinterpret(Csize_t, -1) const CURLINFO_STRING = 0x100000 const CURLINFO_LONG = 0x200000 const CURLINFO_DOUBLE = 0x300000 const CURLINFO_SLIST = 0x400000 const CURLINFO_PTR = 0x400000 const CURLINFO_SOCKET = 0x500000 const CURLINFO_OFF_T = 0x600000 const CURLINFO_MASK = 0x0fffff const CURLINFO_TYPEMASK = 0xf00000 const CURLINFO_HTTP_CODE = CURLINFO_RESPONSE_CODE const CURL_GLOBAL_SSL = (1<<0) const CURL_GLOBAL_WIN32 = (1<<1) const CURL_GLOBAL_ALL = (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) const CURL_GLOBAL_NOTHING = 0 const CURL_GLOBAL_DEFAULT = CURL_GLOBAL_ALL const CURL_GLOBAL_ACK_EINTR = (1<<2) const CURLVERSION_NOW = CURLVERSION_FIFTH const CURL_VERSION_IPV6 = (1<<0) const CURL_VERSION_KERBEROS4 = (1<<1) const CURL_VERSION_SSL = (1<<2) const CURL_VERSION_LIBZ = (1<<3) const CURL_VERSION_NTLM = (1<<4) const CURL_VERSION_GSSNEGOTIATE = (1<<5) const CURL_VERSION_DEBUG = (1<<6) const CURL_VERSION_ASYNCHDNS = (1<<7) const CURL_VERSION_SPNEGO = (1<<8) const CURL_VERSION_LARGEFILE = (1<<9) const CURL_VERSION_IDN = (1<<10) const CURL_VERSION_SSPI = (1<<11) const CURL_VERSION_CONV = (1<<12) const CURL_VERSION_CURLDEBUG = (1<<13) const CURL_VERSION_TLSAUTH_SRP = (1<<14) const CURL_VERSION_NTLM_WB = (1<<15) const CURL_VERSION_HTTP2 = (1<<16) const CURL_VERSION_GSSAPI = (1<<17) const CURL_VERSION_KERBEROS5 = (1<<18) const CURL_VERSION_UNIX_SOCKETS = (1<<19) const CURL_VERSION_PSL = (1<<20) const CURL_VERSION_HTTPS_PROXY = (1<<21) const CURL_VERSION_MULTI_SSL = (1<<22) const CURL_VERSION_BROTLI = (1<<23) const CURLPAUSE_RECV = (1<<0) const CURLPAUSE_RECV_CONT = (0) const CURLPAUSE_SEND = (1<<2) const CURLPAUSE_SEND_CONT = (0) const CURLPAUSE_ALL = (CURLPAUSE_RECV|CURLPAUSE_SEND) const CURLPAUSE_CONT = (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) const CURLM_CALL_MULTI_SOCKET = CURLM_CALL_MULTI_PERFORM const CURLPIPE_NOTHING = Clong(0) const CURLPIPE_HTTP1 = Clong(1) const CURLPIPE_MULTIPLEX = Clong(2) const CURL_WAIT_POLLIN = 0x0001 const CURL_WAIT_POLLPRI = 0x0002 const CURL_WAIT_POLLOUT = 0x0004 const CURL_POLL_NONE = 0 const CURL_POLL_IN = 1 const CURL_POLL_OUT = 2 const CURL_POLL_INOUT = 3 const CURL_POLL_REMOVE = 4 const CURL_SOCKET_TIMEOUT = CURL_SOCKET_BAD const CURL_CSELECT_IN = 0x01 const CURL_CSELECT_OUT = 0x02 const CURL_CSELECT_ERR = 0x04 const CURL_PUSH_OK = 0 const CURL_PUSH_DENY = 1 s���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/LibCURL/src/Mime_ext.jlYP������module Mime_ext export MimeExt const MimeExt = Dict{String,String}( "ez" => "application/andrew-inset", "anx" => "application/annodex", "atom" => "application/atom+xml", "atomcat" => "application/atomcat+xml", "atomsrv" => "application/atomserv+xml", "lin" => "application/bbolin", "cu" => "application/cu-seeme", "davmount" => "application/davmount+xml", "dcm" => "application/dicom", "tsp" => "application/dsptype", "es" => "application/ecmascript", "spl" => "application/futuresplash", "hta" => "application/hta", "jar" => "application/java-archive", "ser" => "application/java-serialized-object", "class" => "application/java-vm", "js" => "application/javascript", "json" => "application/json", "m3g" => "application/m3g", "hqx" => "application/mac-binhex40", "cpt" => "application/mac-compactpro", "nb" => "application/mathematica", "nbp" => "application/mathematica", "mbox" => "application/mbox", "mdb" => "application/msaccess", "doc" => "application/msword", "dot" => "application/msword", "mxf" => "application/mxf", "bin" => "application/octet-stream", "oda" => "application/oda", "ogx" => "application/ogg", "one" => "application/onenote", "onetoc2" => "application/onenote", "onetmp" => "application/onenote", "onepkg" => "application/onenote", "pdf" => "application/pdf", "pgp" => "application/pgp-encrypted", "key" => "application/pgp-keys", "sig" => "application/pgp-signature", "prf" => "application/pics-rules", "ps" => "application/postscript", "ai" => "application/postscript", "eps" => "application/postscript", "epsi" => "application/postscript", "epsf" => "application/postscript", "eps2" => "application/postscript", "eps3" => "application/postscript", "rar" => "application/rar", "rdf" => "application/rdf+xml", "rtf" => "application/rtf", "stl" => "application/sla", "smi" => "application/smil", "smil" => "application/smil", "xhtml" => "application/xhtml+xml", "xht" => "application/xhtml+xml", "xml" => "application/xml", "xsl" => "application/xml", "xsd" => "application/xml", "xspf" => "application/xspf+xml", "zip" => "application/zip", "apk" => "application/vnd.android.package-archive", "cdy" => "application/vnd.cinderella", "kml" => "application/vnd.google-earth.kml+xml", "kmz" => "application/vnd.google-earth.kmz", "xul" => "application/vnd.mozilla.xul+xml", "xls" => "application/vnd.ms-excel", "xlb" => "application/vnd.ms-excel", "xlt" => "application/vnd.ms-excel", "xlam" => "application/vnd.ms-excel.addin.macroEnabled.12", "xlsb" => "application/vnd.ms-excel.sheet.binary.macroEnabled.12", "xlsm" => "application/vnd.ms-excel.sheet.macroEnabled.12", "xltm" => "application/vnd.ms-excel.template.macroEnabled.12", "eot" => "application/vnd.ms-fontobject", "thmx" => "application/vnd.ms-officetheme", "cat" => "application/vnd.ms-pki.seccat", "ppt" => "application/vnd.ms-powerpoint", "pps" => "application/vnd.ms-powerpoint", "ppam" => "application/vnd.ms-powerpoint.addin.macroEnabled.12", "pptm" => "application/vnd.ms-powerpoint.presentation.macroEnabled.12", "sldm" => "application/vnd.ms-powerpoint.slide.macroEnabled.12", "ppsm" => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", "potm" => "application/vnd.ms-powerpoint.template.macroEnabled.12", "docm" => "application/vnd.ms-word.document.macroEnabled.12", "dotm" => "application/vnd.ms-word.template.macroEnabled.12", "odc" => "application/vnd.oasis.opendocument.chart", "odb" => "application/vnd.oasis.opendocument.database", "odf" => "application/vnd.oasis.opendocument.formula", "odg" => "application/vnd.oasis.opendocument.graphics", "otg" => "application/vnd.oasis.opendocument.graphics-template", "odi" => "application/vnd.oasis.opendocument.image", "odp" => "application/vnd.oasis.opendocument.presentation", "otp" => "application/vnd.oasis.opendocument.presentation-template", "ods" => "application/vnd.oasis.opendocument.spreadsheet", "ots" => "application/vnd.oasis.opendocument.spreadsheet-template", "odt" => "application/vnd.oasis.opendocument.text", "odm" => "application/vnd.oasis.opendocument.text-master", "ott" => "application/vnd.oasis.opendocument.text-template", "oth" => "application/vnd.oasis.opendocument.text-web", "pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation", "sldx" => "application/vnd.openxmlformats-officedocument.presentationml.slide", "ppsx" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow", "potx" => "application/vnd.openxmlformats-officedocument.presentationml.template", "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template", "docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "dotx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.template", "cod" => "application/vnd.rim.cod", "mmf" => "application/vnd.smaf", "sdc" => "application/vnd.stardivision.calc", "sds" => "application/vnd.stardivision.chart", "sda" => "application/vnd.stardivision.draw", "sdd" => "application/vnd.stardivision.impress", "sdf" => "application/vnd.stardivision.math", "sdw" => "application/vnd.stardivision.writer", "sgl" => "application/vnd.stardivision.writer-global", "sxc" => "application/vnd.sun.xml.calc", "stc" => "application/vnd.sun.xml.calc.template", "sxd" => "application/vnd.sun.xml.draw", "std" => "application/vnd.sun.xml.draw.template", "sxi" => "application/vnd.sun.xml.impress", "sti" => "application/vnd.sun.xml.impress.template", "sxm" => "application/vnd.sun.xml.math", "sxw" => "application/vnd.sun.xml.writer", "sxg" => "application/vnd.sun.xml.writer.global", "stw" => "application/vnd.sun.xml.writer.template", "sis" => "application/vnd.symbian.install", "cap" => "application/vnd.tcpdump.pcap", "pcap" => "application/vnd.tcpdump.pcap", "vsd" => "application/vnd.visio", "wbxml" => "application/vnd.wap.wbxml", "wmlc" => "application/vnd.wap.wmlc", "wmlsc" => "application/vnd.wap.wmlscriptc", "wpd" => "application/vnd.wordperfect", "wp5" => "application/vnd.wordperfect5.1", "wk" => "application/x-123", "7z" => "application/x-7z-compressed", "abw" => "application/x-abiword", "dmg" => "application/x-apple-diskimage", "bcpio" => "application/x-bcpio", "torrent" => "application/x-bittorrent", "cab" => "application/x-cab", "cbr" => "application/x-cbr", "cbz" => "application/x-cbz", "cdf" => "application/x-cdf", "cda" => "application/x-cdf", "vcd" => "application/x-cdlink", "pgn" => "application/x-chess-pgn", "mph" => "application/x-comsol", "cpio" => "application/x-cpio", "csh" => "application/x-csh", "deb" => "application/x-debian-package", "udeb" => "application/x-debian-package", "dcr" => "application/x-director", "dir" => "application/x-director", "dxr" => "application/x-director", "dms" => "application/x-dms", "wad" => "application/x-doom", "dvi" => "application/x-dvi", "pfa" => "application/x-font", "pfb" => "application/x-font", "gsf" => "application/x-font", "pcf" => "application/x-font", "pcf.Z" => "application/x-font", "woff" => "application/x-font-woff", "mm" => "application/x-freemind", "spl" => "application/x-futuresplash", "gan" => "application/x-ganttproject", "gnumeric" => "application/x-gnumeric", "sgf" => "application/x-go-sgf", "gcf" => "application/x-graphing-calculator", "gtar" => "application/x-gtar", "tgz" => "application/x-gtar-compressed", "taz" => "application/x-gtar-compressed", "hdf" => "application/x-hdf", "hwp" => "application/x-hwp", "ica" => "application/x-ica", "info" => "application/x-info", "ins" => "application/x-internet-signup", "isp" => "application/x-internet-signup", "iii" => "application/x-iphone", "iso" => "application/x-iso9660-image", "jam" => "application/x-jam", "jnlp" => "application/x-java-jnlp-file", "jmz" => "application/x-jmol", "chrt" => "application/x-kchart", "kil" => "application/x-killustrator", "skp" => "application/x-koan", "skd" => "application/x-koan", "skt" => "application/x-koan", "skm" => "application/x-koan", "kpr" => "application/x-kpresenter", "kpt" => "application/x-kpresenter", "ksp" => "application/x-kspread", "kwd" => "application/x-kword", "kwt" => "application/x-kword", "latex" => "application/x-latex", "lha" => "application/x-lha", "lyx" => "application/x-lyx", "lzh" => "application/x-lzh", "lzx" => "application/x-lzx", "frm" => "application/x-maker", "maker" => "application/x-maker", "frame" => "application/x-maker", "fm" => "application/x-maker", "fb" => "application/x-maker", "book" => "application/x-maker", "fbdoc" => "application/x-maker", "md5" => "application/x-md5", "mif" => "application/x-mif", "m3u8" => "application/x-mpegURL", "wmd" => "application/x-ms-wmd", "wmz" => "application/x-ms-wmz", "com" => "application/x-msdos-program", "exe" => "application/x-msdos-program", "bat" => "application/x-msdos-program", "dll" => "application/x-msdos-program", "msi" => "application/x-msi", "nc" => "application/x-netcdf", "pac" => "application/x-ns-proxy-autoconfig", "dat" => "application/x-ns-proxy-autoconfig", "nwc" => "application/x-nwc", "o" => "application/x-object", "oza" => "application/x-oz-application", "p7r" => "application/x-pkcs7-certreqresp", "crl" => "application/x-pkcs7-crl", "pyc" => "application/x-python-code", "pyo" => "application/x-python-code", "qgs" => "application/x-qgis", "shp" => "application/x-qgis", "shx" => "application/x-qgis", "qtl" => "application/x-quicktimeplayer", "rdp" => "application/x-rdp", "rpm" => "application/x-redhat-package-manager", "rss" => "application/x-rss+xml", "rb" => "application/x-ruby", "sci" => "application/x-scilab", "sce" => "application/x-scilab", "xcos" => "application/x-scilab-xcos", "sh" => "application/x-sh", "sha1" => "application/x-sha1", "shar" => "application/x-shar", "swf" => "application/x-shockwave-flash", "swfl" => "application/x-shockwave-flash", "scr" => "application/x-silverlight", "sql" => "application/x-sql", "sit" => "application/x-stuffit", "sitx" => "application/x-stuffit", "sv4cpio" => "application/x-sv4cpio", "sv4crc" => "application/x-sv4crc", "tar" => "application/x-tar", "tcl" => "application/x-tcl", "gf" => "application/x-tex-gf", "pk" => "application/x-tex-pk", "texinfo" => "application/x-texinfo", "texi" => "application/x-texinfo", "~" => "application/x-trash", "%" => "application/x-trash", "bak" => "application/x-trash", "old" => "application/x-trash", "sik" => "application/x-trash", "t" => "application/x-troff", "tr" => "application/x-troff", "roff" => "application/x-troff", "man" => "application/x-troff-man", "me" => "application/x-troff-me", "ms" => "application/x-troff-ms", "ustar" => "application/x-ustar", "src" => "application/x-wais-source", "wz" => "application/x-wingz", "crt" => "application/x-x509-ca-cert", "xcf" => "application/x-xcf", "fig" => "application/x-xfig", "xpi" => "application/x-xpinstall", "amr" => "audio/amr", "awb" => "audio/amr-wb", "amr" => "audio/amr", "awb" => "audio/amr-wb", "axa" => "audio/annodex", "au" => "audio/basic", "snd" => "audio/basic", "csd" => "audio/csound", "orc" => "audio/csound", "sco" => "audio/csound", "flac" => "audio/flac", "mid" => "audio/midi", "midi" => "audio/midi", "kar" => "audio/midi", "mpga" => "audio/mpeg", "mpega" => "audio/mpeg", "mp2" => "audio/mpeg", "mp3" => "audio/mpeg", "m4a" => "audio/mpeg", "m3u" => "audio/mpegurl", "oga" => "audio/ogg", "ogg" => "audio/ogg", "spx" => "audio/ogg", "sid" => "audio/prs.sid", "aif" => "audio/x-aiff", "aiff" => "audio/x-aiff", "aifc" => "audio/x-aiff", "gsm" => "audio/x-gsm", "m3u" => "audio/x-mpegurl", "wma" => "audio/x-ms-wma", "wax" => "audio/x-ms-wax", "ra" => "audio/x-pn-realaudio", "rm" => "audio/x-pn-realaudio", "ram" => "audio/x-pn-realaudio", "ra" => "audio/x-realaudio", "pls" => "audio/x-scpls", "sd2" => "audio/x-sd2", "wav" => "audio/x-wav", "alc" => "chemical/x-alchemy", "cac" => "chemical/x-cache", "cache" => "chemical/x-cache", "csf" => "chemical/x-cache-csf", "cbin" => "chemical/x-cactvs-binary", "cascii" => "chemical/x-cactvs-binary", "ctab" => "chemical/x-cactvs-binary", "cdx" => "chemical/x-cdx", "cer" => "chemical/x-cerius", "c3d" => "chemical/x-chem3d", "chm" => "chemical/x-chemdraw", "cif" => "chemical/x-cif", "cmdf" => "chemical/x-cmdf", "cml" => "chemical/x-cml", "cpa" => "chemical/x-compass", "bsd" => "chemical/x-crossfire", "csml" => "chemical/x-csml", "csm" => "chemical/x-csml", "ctx" => "chemical/x-ctx", "cxf" => "chemical/x-cxf", "cef" => "chemical/x-cxf", "emb" => "chemical/x-embl-dl-nucleotide", "embl" => "chemical/x-embl-dl-nucleotide", "spc" => "chemical/x-galactic-spc", "inp" => "chemical/x-gamess-input", "gam" => "chemical/x-gamess-input", "gamin" => "chemical/x-gamess-input", "fch" => "chemical/x-gaussian-checkpoint", "fchk" => "chemical/x-gaussian-checkpoint", "cub" => "chemical/x-gaussian-cube", "gau" => "chemical/x-gaussian-input", "gjc" => "chemical/x-gaussian-input", "gjf" => "chemical/x-gaussian-input", "gal" => "chemical/x-gaussian-log", "gcg" => "chemical/x-gcg8-sequence", "gen" => "chemical/x-genbank", "hin" => "chemical/x-hin", "istr" => "chemical/x-isostar", "ist" => "chemical/x-isostar", "jdx" => "chemical/x-jcamp-dx", "dx" => "chemical/x-jcamp-dx", "kin" => "chemical/x-kinemage", "mcm" => "chemical/x-macmolecule", "mmd" => "chemical/x-macromodel-input", "mmod" => "chemical/x-macromodel-input", "mol" => "chemical/x-mdl-molfile", "rd" => "chemical/x-mdl-rdfile", "rxn" => "chemical/x-mdl-rxnfile", "sd" => "chemical/x-mdl-sdfile", "sdf" => "chemical/x-mdl-sdfile", "tgf" => "chemical/x-mdl-tgf", "mcif" => "chemical/x-mmcif", "mol2" => "chemical/x-mol2", "b" => "chemical/x-molconn-Z", "gpt" => "chemical/x-mopac-graph", "mop" => "chemical/x-mopac-input", "mopcrt" => "chemical/x-mopac-input", "mpc" => "chemical/x-mopac-input", "zmt" => "chemical/x-mopac-input", "moo" => "chemical/x-mopac-out", "mvb" => "chemical/x-mopac-vib", "asn" => "chemical/x-ncbi-asn1", "prt" => "chemical/x-ncbi-asn1-ascii", "ent" => "chemical/x-ncbi-asn1-ascii", "val" => "chemical/x-ncbi-asn1-binary", "aso" => "chemical/x-ncbi-asn1-binary", "asn" => "chemical/x-ncbi-asn1-spec", "pdb" => "chemical/x-pdb", "ent" => "chemical/x-pdb", "ros" => "chemical/x-rosdal", "sw" => "chemical/x-swissprot", "vms" => "chemical/x-vamas-iso14976", "vmd" => "chemical/x-vmd", "xtel" => "chemical/x-xtel", "xyz" => "chemical/x-xyz", "gif" => "image/gif", "ief" => "image/ief", "jpeg" => "image/jpeg", "jpg" => "image/jpeg", "jpe" => "image/jpeg", "pcx" => "image/pcx", "png" => "image/png", "svg" => "image/svg+xml", "svgz" => "image/svg+xml", "tiff" => "image/tiff", "tif" => "image/tiff", "djvu" => "image/vnd.djvu", "djv" => "image/vnd.djvu", "ico" => "image/vnd.microsoft.icon", "wbmp" => "image/vnd.wap.wbmp", "cr2" => "image/x-canon-cr2", "crw" => "image/x-canon-crw", "ras" => "image/x-cmu-raster", "cdr" => "image/x-coreldraw", "pat" => "image/x-coreldrawpattern", "cdt" => "image/x-coreldrawtemplate", "cpt" => "image/x-corelphotopaint", "erf" => "image/x-epson-erf", "art" => "image/x-jg", "jng" => "image/x-jng", "bmp" => "image/x-ms-bmp", "nef" => "image/x-nikon-nef", "orf" => "image/x-olympus-orf", "psd" => "image/x-photoshop", "pnm" => "image/x-portable-anymap", "pbm" => "image/x-portable-bitmap", "pgm" => "image/x-portable-graymap", "ppm" => "image/x-portable-pixmap", "rgb" => "image/x-rgb", "xbm" => "image/x-xbitmap", "xpm" => "image/x-xpixmap", "xwd" => "image/x-xwindowdump", "eml" => "message/rfc822", "igs" => "model/iges", "iges" => "model/iges", "msh" => "model/mesh", "mesh" => "model/mesh", "silo" => "model/mesh", "wrl" => "model/vrml", "vrml" => "model/vrml", "x3dv" => "model/x3d+vrml", "x3d" => "model/x3d+xml", "x3db" => "model/x3d+binary", "appcache" => "text/cache-manifest", "ics" => "text/calendar", "icz" => "text/calendar", "css" => "text/css", "csv" => "text/csv", "323" => "text/h323", "html" => "text/html", "htm" => "text/html", "shtml" => "text/html", "uls" => "text/iuls", "mml" => "text/mathml", "asc" => "text/plain", "txt" => "text/plain", "text" => "text/plain", "pot" => "text/plain", "brf" => "text/plain", "srt" => "text/plain", "rtx" => "text/richtext", "sct" => "text/scriptlet", "wsc" => "text/scriptlet", "tm" => "text/texmacs", "tsv" => "text/tab-separated-values", "jad" => "text/vnd.sun.j2me.app-descriptor", "wml" => "text/vnd.wap.wml", "wmls" => "text/vnd.wap.wmlscript", "bib" => "text/x-bibtex", "boo" => "text/x-boo", "h++" => "text/x-c++hdr", "hpp" => "text/x-c++hdr", "hxx" => "text/x-c++hdr", "hh" => "text/x-c++hdr", "c++" => "text/x-c++src", "cpp" => "text/x-c++src", "cxx" => "text/x-c++src", "cc" => "text/x-c++src", "h" => "text/x-chdr", "htc" => "text/x-component", "csh" => "text/x-csh", "c" => "text/x-csrc", "d" => "text/x-dsrc", "diff" => "text/x-diff", "patch" => "text/x-diff", "hs" => "text/x-haskell", "java" => "text/x-java", "ly" => "text/x-lilypond", "lhs" => "text/x-literate-haskell", "moc" => "text/x-moc", "p" => "text/x-pascal", "pas" => "text/x-pascal", "gcd" => "text/x-pcs-gcd", "pl" => "text/x-perl", "pm" => "text/x-perl", "py" => "text/x-python", "scala" => "text/x-scala", "etx" => "text/x-setext", "sfv" => "text/x-sfv", "sh" => "text/x-sh", "tcl" => "text/x-tcl", "tk" => "text/x-tcl", "tex" => "text/x-tex", "ltx" => "text/x-tex", "sty" => "text/x-tex", "cls" => "text/x-tex", "vcs" => "text/x-vcalendar", "vcf" => "text/x-vcard", "3gp" => "video/3gpp", "axv" => "video/annodex", "dl" => "video/dl", "dif" => "video/dv", "dv" => "video/dv", "fli" => "video/fli", "gl" => "video/gl", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpe" => "video/mpeg", "ts" => "video/MP2T", "mp4" => "video/mp4", "qt" => "video/quicktime", "mov" => "video/quicktime", "ogv" => "video/ogg", "webm" => "video/webm", "mxu" => "video/vnd.mpegurl", "flv" => "video/x-flv", "lsf" => "video/x-la-asf", "lsx" => "video/x-la-asf", "mng" => "video/x-mng", "asf" => "video/x-ms-asf", "asx" => "video/x-ms-asf", "wm" => "video/x-ms-wm", "wmv" => "video/x-ms-wmv", "wmx" => "video/x-ms-wmx", "wvx" => "video/x-ms-wvx", "avi" => "video/x-msvideo", "movie" => "video/x-sgi-movie", "mpv" => "video/x-matroska", "mkv" => "video/x-matroska", "ice" => "x-conference/x-cooltalk", "sisx" => "x-epoc/x-sisx-app", "vrm" => "x-world/x-vrml", "vrml" => "x-world/x-vrml", "wrl" => "x-world/x-vrml", "" => "" ) end v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Downloads/src/Downloads.jl!J������""" The `Downloads` module exports a function [`download`](@ref), which provides cross-platform, multi-protocol, in-process download functionality implemented with [libcurl](https://curl.haxx.se/libcurl/). It is used for the `Base.download` function in Julia 1.6 or later. More generally, the module exports functions and types that provide lower-level control and diagnostic information for file downloading: - [`download`](@ref) โ€” download a file from a URL, erroring if it can't be downloaded - [`request`](@ref) โ€” request a URL, returning a `Response` object indicating success - [`Response`](@ref) โ€” a type capturing the status and other metadata about a request - [`RequestError`](@ref) โ€” an error type thrown by `download` and `request` on error - [`Downloader`](@ref) โ€” an object encapsulating shared resources for downloading """ module Downloads using Base.Experimental: @sync using NetworkOptions using ArgTools include("Curl/Curl.jl") using .Curl export download, request, Downloader, Response, RequestError, default_downloader! ## public API types ## """ Downloader(; [ grace::Real = 30 ]) `Downloader` objects are used to perform individual `download` operations. Connections, name lookups and other resources are shared within a `Downloader`. These connections and resources are cleaned up after a configurable grace period (default: 30 seconds) since anything was downloaded with it, or when it is garbage collected, whichever comes first. If the grace period is set to zero, all resources will be cleaned up immediately as soon as there are no more ongoing downloads in progress. If the grace period is set to `Inf` then resources are not cleaned up until `Downloader` is garbage collected. """ mutable struct Downloader multi::Multi ca_roots::Union{String, Nothing} easy_hook::Union{Function, Nothing} Downloader(multi::Multi) = new(multi, get_ca_roots(), EASY_HOOK[]) end Downloader(; grace::Real=30) = Downloader(Multi(grace_ms(grace))) function grace_ms(grace::Real) grace < 0 && throw(ArgumentError("grace period cannot be negative: $grace")) grace <= typemax(UInt64) รท 1000 ? round(UInt64, 1000*grace) : typemax(UInt64) end function easy_hook(downloader::Downloader, easy::Easy, info::NamedTuple) downloader.easy_hook !== nothing && downloader.easy_hook(easy, info) end get_ca_roots() = Curl.SYSTEM_SSL ? ca_roots() : ca_roots_path() function set_ca_roots(downloader::Downloader, easy::Easy) ca_roots = downloader.ca_roots ca_roots !== nothing && set_ca_roots_path(easy, ca_roots) end const DOWNLOAD_LOCK = ReentrantLock() const DOWNLOADER = Ref{Union{Downloader, Nothing}}(nothing) """ `EASY_HOOK` is a modifable global hook to used as the default `easy_hook` on new `Downloader` objects. This supplies a mechanism to set options for the `Downloader` via `Curl.setopt` It is expected to be function taking two arguments: an `Easy` struct and an `info` NamedTuple with names `url`, `method` and `headers`. """ const EASY_HOOK = Ref{Union{Function, Nothing}}(nothing) """ struct Response proto :: String url :: String status :: Int message :: String headers :: Vector{Pair{String,String}} end `Response` is a type capturing the properties of a successful response to a request as an object. It has the following fields: - `proto`: the protocol that was used to get the response - `url`: the URL that was ultimately requested after following redirects - `status`: the status code of the response, indicating success, failure, etc. - `message`: a textual message describing the nature of the response - `headers`: any headers that were returned with the response The meaning and availability of some of these responses depends on the protocol used for the request. For many protocols, including HTTP/S and S/FTP, a 2xx status code indicates a successful response. For responses in protocols that do not support headers, the headers vector will be empty. HTTP/2 does not include a status message, only a status code, so the message will be empty. """ struct Response proto :: Union{String, Nothing} url :: String # redirected URL status :: Int message :: String headers :: Vector{Pair{String,String}} end Curl.status_ok(response::Response) = status_ok(response.proto, response.status) """ struct RequestError <: ErrorException url :: String code :: Int message :: String response :: Response end `RequestError` is a type capturing the properties of a failed response to a request as an exception object: - `url`: the original URL that was requested without any redirects - `code`: the libcurl error code; `0` if a protocol-only error occurred - `message`: the libcurl error message indicating what went wrong - `response`: response object capturing what response info is available The same `RequestError` type is thrown by `download` if the request was successful but there was a protocol-level error indicated by a status code that is not in the 2xx range, in which case `code` will be zero and the `message` field will be the empty string. The `request` API only throws a `RequestError` if the libcurl error `code` is non-zero, in which case the included `response` object is likely to have a `status` of zero and an empty message. There are, however, situations where a curl-level error is thrown due to a protocol error, in which case both the inner and outer code and message may be of interest. """ struct RequestError <: Exception url :: String # original URL code :: Int message :: String response :: Response end function Base.showerror(io::IO, err::RequestError) print(io, "RequestError: $(error_message(err)) while requesting $(err.url)") end function error_message(err::RequestError) errstr = err.message status = err.response.status message = err.response.message status_re = Regex(status == 0 ? "" : "\\b$status\\b") err.code == Curl.CURLE_OK && return isempty(message) ? "Error status $status" : contains(message, status_re) ? message : "$message (status $status)" isempty(message) && !isempty(errstr) && return status == 0 ? errstr : "$errstr (status $status)" isempty(message) && (message = "Status $status") isempty(errstr) && (errstr = "curl error $(err.code)") !contains(message, status_re) && !contains(errstr, status_re) && (errstr = "status $status; $errstr") return "$message ($errstr)" end ## download API ## """ download(url, [ output = tempname() ]; [ method = "GET", ] [ headers = <none>, ] [ timeout = <none>, ] [ progress = <none>, ] [ verbose = false, ] [ debug = <none>, ] [ downloader = <default>, ] ) -> output url :: AbstractString output :: Union{AbstractString, AbstractCmd, IO} method :: AbstractString headers :: Union{AbstractVector, AbstractDict} timeout :: Real progress :: (total::Integer, now::Integer) --> Any verbose :: Bool debug :: (type, message) --> Any downloader :: Downloader Download a file from the given url, saving it to `output` or if not specified, a temporary path. The `output` can also be an `IO` handle, in which case the body of the response is streamed to that handle and the handle is returned. If `output` is a command, the command is run and output is sent to it on stdin. If the `downloader` keyword argument is provided, it must be a `Downloader` object. Resources and connections will be shared between downloads performed by the same `Downloader` and cleaned up automatically when the object is garbage collected or there have been no downloads performed with it for a grace period. See `Downloader` for more info about configuration and usage. If the `headers` keyword argument is provided, it must be a vector or dictionary whose elements are all pairs of strings. These pairs are passed as headers when downloading URLs with protocols that supports them, such as HTTP/S. The `timeout` keyword argument specifies a timeout for the download to complete in seconds, with a resolution of milliseconds. By default no timeout is set, but this can also be explicitly requested by passing a timeout value of `Inf`. Separately, if 20 seconds elapse without receiving any data, the download will timeout. See extended help for how to disable this timeout. If the `progress` keyword argument is provided, it must be a callback function which will be called whenever there are updates about the size and status of the ongoing download. The callback must take two integer arguments: `total` and `now` which are the total size of the download in bytes, and the number of bytes which have been downloaded so far. Note that `total` starts out as zero and remains zero until the server gives an indication of the total size of the download (e.g. with a `Content-Length` header), which may never happen. So a well-behaved progress callback should handle a total size of zero gracefully. If the `verbose` option is set to true, `libcurl`, which is used to implement the download functionality will print debugging information to `stderr`. If the `debug` option is set to a function accepting two `String` arguments, then the verbose option is ignored and instead the data that would have been printed to `stderr` is passed to the `debug` callback with `type` and `message` arguments. The `type` argument indicates what kind of event has occurred, and is one of: `TEXT`, `HEADER IN`, `HEADER OUT`, `DATA IN`, `DATA OUT`, `SSL DATA IN` or `SSL DATA OUT`. The `message` argument is the description of the debug event. ## Extended Help For further customization, use a [`Downloader`](@ref) and [`easy_hook`s](https://github.com/JuliaLang/Downloads.jl#mutual-tls-using-downloads). For example, to disable the 20 second timeout when no data is received, you may use the following: ```jl downloader = Downloads.Downloader() downloader.easy_hook = (easy, info) -> Downloads.Curl.setopt(easy, Downloads.Curl.CURLOPT_LOW_SPEED_TIME, 0) Downloads.download("https://httpbingo.julialang.org/delay/30"; downloader) ``` """ function download( url :: AbstractString, output :: Union{ArgWrite, Nothing} = nothing; method :: Union{AbstractString, Nothing} = nothing, headers :: Union{AbstractVector, AbstractDict} = Pair{String,String}[], timeout :: Real = Inf, progress :: Union{Function, Nothing} = nothing, verbose :: Bool = false, debug :: Union{Function, Nothing} = nothing, downloader :: Union{Downloader, Nothing} = nothing, ) :: ArgWrite arg_write(output) do output response = request( url, output = output, method = method, headers = headers, timeout = timeout, progress = progress, verbose = verbose, debug = debug, downloader = downloader, )::Response status_ok(response) && return output throw(RequestError(url, Curl.CURLE_OK, "", response)) end end ## request API ## """ request(url; [ input = <none>, ] [ output = <none>, ] [ method = input ? "PUT" : output ? "GET" : "HEAD", ] [ headers = <none>, ] [ timeout = <none>, ] [ progress = <none>, ] [ verbose = false, ] [ debug = <none>, ] [ throw = true, ] [ downloader = <default>, ] ) -> Union{Response, RequestError} url :: AbstractString input :: Union{AbstractString, AbstractCmd, IO} output :: Union{AbstractString, AbstractCmd, IO} method :: AbstractString headers :: Union{AbstractVector, AbstractDict} timeout :: Real progress :: (dl_total, dl_now, ul_total, ul_now) --> Any verbose :: Bool debug :: (type, message) --> Any throw :: Bool downloader :: Downloader Make a request to the given url, returning a `Response` object capturing the status, headers and other information about the response. The body of the response is written to `output` if specified and discarded otherwise. For HTTP/S requests, if an `input` stream is given, a `PUT` request is made; otherwise if an `output` stream is given, a `GET` request is made; if neither is given a `HEAD` request is made. For other protocols, appropriate default methods are used based on what combination of input and output are requested. The following options differ from the `download` function: - `input` allows providing a request body; if provided default to `PUT` request - `progress` is a callback taking four integers for upload and download progress - `throw` controls whether to throw or return a `RequestError` on request error Note that unlike `download` which throws an error if the requested URL could not be downloaded (indicated by non-2xx status code), `request` returns a `Response` object no matter what the status code of the response is. If there is an error with getting a response at all, then a `RequestError` is thrown or returned. """ function request( url :: AbstractString; input :: Union{ArgRead, Nothing} = nothing, output :: Union{ArgWrite, Nothing} = nothing, method :: Union{AbstractString, Nothing} = nothing, headers :: Union{AbstractVector, AbstractDict} = Pair{String,String}[], timeout :: Real = Inf, progress :: Union{Function, Nothing} = nothing, verbose :: Bool = false, debug :: Union{Function, Nothing} = nothing, throw :: Bool = true, downloader :: Union{Downloader, Nothing} = nothing, ) :: Union{Response, RequestError} if downloader === nothing lock(DOWNLOAD_LOCK) do downloader = DOWNLOADER[] if downloader === nothing downloader = DOWNLOADER[] = Downloader() end end end local response have_input = input !== nothing have_output = output !== nothing input = something(input, devnull) output = something(output, devnull) input_size = arg_read_size(input) if input_size === nothing # take input_size from content-length header if one is supplied input_size = content_length(headers) end progress = p_func(progress, input, output) arg_read(input) do input arg_write(output) do output with_handle(Easy()) do easy # setup the request set_url(easy, url) set_timeout(easy, timeout) set_verbose(easy, verbose) set_debug(easy, debug) add_headers(easy, headers) # libcurl does not set the default header reliably so set it # explicitly unless user has specified it, xref # https://github.com/JuliaLang/Pkg.jl/pull/2357 if !any(kv -> lowercase(kv[1]) == "user-agent", headers) Curl.add_header(easy, "User-Agent", Curl.USER_AGENT) end if have_input enable_upload(easy) if input_size !== nothing set_upload_size(easy, input_size) end if applicable(seek, input, 0) set_seeker(easy) do offset seek(input, Int(offset)) end end else set_body(easy, have_output && method != "HEAD") end method !== nothing && set_method(easy, method) progress !== nothing && enable_progress(easy) set_ca_roots(downloader, easy) info = (url = url, method = method, headers = headers) easy_hook(downloader, easy, info) # do the request add_handle(downloader.multi, easy) try # ensure handle is removed @sync begin @async for buf in easy.output write(output, buf) end if progress !== nothing @async for prog in easy.progress progress(prog...) end end if have_input @async upload_data(easy, input) end end finally remove_handle(downloader.multi, easy) end # return the response or throw an error response = Response(get_response_info(easy)...) easy.code == Curl.CURLE_OK && return response message = get_curl_errstr(easy) response = RequestError(url, easy.code, message, response) throw && Base.throw(response) end end end return response end ## helper functions ## function p_func(progress::Function, input::ArgRead, output::ArgWrite) hasmethod(progress, NTuple{4,Int}) && return progress hasmethod(progress, NTuple{2,Int}) || throw(ArgumentError("invalid progress callback")) input === devnull && output !== devnull && return (total, now, _, _) -> progress(total, now) input !== devnull && output === devnull && return (_, _, total, now) -> progress(total, now) (dl_total, dl_now, ul_total, ul_now) -> progress(dl_total + ul_total, dl_now + ul_now) end p_func(progress::Nothing, input::ArgRead, output::ArgWrite) = nothing arg_read_size(path::AbstractString) = filesize(path) arg_read_size(io::Base.GenericIOBuffer) = bytesavailable(io) arg_read_size(::Base.DevNull) = 0 arg_read_size(::Any) = nothing function content_length(headers::Union{AbstractVector, AbstractDict}) for (key, value) in headers if lowercase(key) == "content-length" && isa(value, AbstractString) return tryparse(Int, value) end end return nothing end """ default_downloader!( downloader = <none> ) downloader :: Downloader Set the default `Downloader`. If no argument is provided, resets the default downloader so that a fresh one is created the next time the default downloader is needed. """ function default_downloader!( downloader :: Union{Downloader, Nothing} = nothing ) lock(DOWNLOAD_LOCK) do DOWNLOADER[] = downloader end end # Precompile let d = Downloader() download("file://" * @__FILE__; downloader=d) precompile(Tuple{typeof(Downloads.download), String, String}) precompile(Tuple{typeof(Downloads.Curl.status_2xx_ok), Int64}) end end # module v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Downloads/src/Curl/Curl.jlฑ ������module Curl export with_handle, Easy, set_url, set_method, set_verbose, set_debug, set_body, set_upload_size, set_seeker, set_ca_roots_path, set_timeout, add_headers, enable_upload, enable_progress, upload_data, get_protocol, get_effective_url, get_response_status, get_response_info, get_curl_errstr, status_ok, Multi, add_handle, remove_handle using Base: @lock using LibCURL using LibCURL: curl_off_t, libcurl # not exported: https://github.com/JuliaWeb/LibCURL.jl/issues/87 # constants that LibCURL should have but doesn't const CURLE_PEER_FAILED_VERIFICATION = 60 const CURLSSLOPT_REVOKE_BEST_EFFORT = 1 << 3 const CURLOPT_PREREQFUNCTION = 20000 + 312 const CURLOPT_PREREQDATA = 10000 + 313 # these are incorrectly defined on Windows by LibCURL: if Sys.iswindows() const curl_socket_t = Base.OS_HANDLE const CURL_SOCKET_TIMEOUT = Base.INVALID_OS_HANDLE else const curl_socket_t = Cint const CURL_SOCKET_TIMEOUT = -1 end # definitions affected by incorrect curl_socket_t (copied verbatim): function curl_multi_socket_action(multi_handle, s, ev_bitmask, running_handles) ccall((:curl_multi_socket_action, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Cint, Ptr{Cint}), multi_handle, s, ev_bitmask, running_handles) end function curl_multi_assign(multi_handle, sockfd, sockp) ccall((:curl_multi_assign, libcurl), CURLMcode, (Ptr{CURLM}, curl_socket_t, Ptr{Cvoid}), multi_handle, sockfd, sockp) end # additional curl_multi_socket_action method function curl_multi_socket_action(multi_handle, s, ev_bitmask) curl_multi_socket_action(multi_handle, s, ev_bitmask, Ref{Cint}()) end using FileWatching using NetworkOptions using Base: OS_HANDLE, preserve_handle, unpreserve_handle include("utils.jl") function __init__() @check curl_global_init(CURL_GLOBAL_ALL) end const CURL_VERSION_INFO = unsafe_load(curl_version_info(CURLVERSION_NOW)) if CURL_VERSION_INFO.ssl_version == Base.C_NULL const SSL_VERSION = "" else const SSL_VERSION = unsafe_string(CURL_VERSION_INFO.ssl_version)::String end const SYSTEM_SSL = Sys.isapple() && startswith(SSL_VERSION, "SecureTransport") || Sys.iswindows() && startswith(SSL_VERSION, "Schannel") const CURL_VERSION_STR = unsafe_string(curl_version()) let m = match(r"^libcurl/(\d+\.\d+\.\d+)\b", CURL_VERSION_STR) m !== nothing || error("unexpected CURL_VERSION_STR value") curl = m.captures[1] julia = "$(VERSION.major).$(VERSION.minor)" global const CURL_VERSION = VersionNumber(curl) global const USER_AGENT = "curl/$curl julia/$julia" end include("Easy.jl") include("Multi.jl") function with_handle(f, handle::Union{Multi, Easy}) try f(handle) finally Curl.done!(handle) end end """ setopt(easy::Easy, option::Integer, value) Sets options on libcurl's "easy" interface. `option` corresponds to libcurl options on https://curl.se/libcurl/c/curl_easy_setopt.html """ setopt(easy::Easy, option::Integer, value) = @check curl_easy_setopt(easy.handle, option, value) """ setopt(multi::Multi, option::Integer, value) Sets options on libcurl's "multi" interface. `option` corresponds to libcurl options on https://curl.se/libcurl/c/curl_multi_setopt.html """ setopt(multi::Multi, option::Integer, value) = @check curl_multi_setopt(multi.handle, option, value) end # module w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Downloads/src/Curl/utils.jlU ������if !@isdefined(contains) contains(haystack, needle) = occursin(needle, haystack) export contains end # basic C stuff puts(s::Union{String,SubString{String}}) = ccall(:puts, Cint, (Ptr{Cchar},), s) jl_malloc(n::Integer) = ccall(:jl_malloc, Ptr{Cvoid}, (Csize_t,), n) # check if a call failed macro check(ex::Expr) ex.head == :call || error("@check: not a call: $ex") arg1 = ex.args[1] :: Symbol if arg1 == :ccall arg2 = ex.args[2] arg2 isa QuoteNode || error("@check: ccallee must be a symbol") f = arg2.value :: Symbol else f = arg1 end prefix = "$f: " if f in (:curl_easy_setopt, :curl_multi_setopt) unknown_option = f == :curl_easy_setopt ? CURLE_UNKNOWN_OPTION : f == :curl_multi_setopt ? CURLM_UNKNOWN_OPTION : error() quote r = $(esc(ex)) if r == $unknown_option @async @error $prefix * string(r) * """\n You may be using an old system libcurl library that doesn't understand options that Julia uses. You can try the following Julia code to see which libcurl library you are using: using Libdl filter!(contains("curl"), dllist()) If this indicates that Julia is not using the libcurl library that is shipped with Julia, then that is likely to be the problem. This either means: 1. You are using an unofficial Julia build which is configured to use a system libcurl library that is not recent enough; you may be able to fix this by upgrading the system libcurl. You should complain to your distro maintainers for allowing Julia to use a too-old libcurl version and consider using official Julia binaries instead. 2. You are overriding the library load path by setting `LD_LIBRARY_PATH`, in which case you are in advanced usage territory. You can try upgrading the system libcurl, unsetting `LD_LIBRARY_PATH`, or otherwise arranging for Julia to load a recent libcurl library. If neither of these is the case and Julia is picking up a too old libcurl, please file an issue with the `Downloads.jl` package. """ maxlog=1_000 elseif !iszero(r) @async @error $prefix * string(r) maxlog=1_000 end r end else quote r = $(esc(ex)) iszero(r) || @async @error $prefix * string(r) maxlog=1_000 r end end end # curl string list structure struct curl_slist_t data::Ptr{Cchar} next::Ptr{curl_slist_t} end v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Downloads/src/Curl/Easy.jlkF������mutable struct Easy handle :: Ptr{Cvoid} input :: Union{Vector{UInt8},Nothing} ready :: Threads.Event seeker :: Union{Function,Nothing} output :: Channel{Vector{UInt8}} progress :: Channel{NTuple{4,Int}} req_hdrs :: Ptr{curl_slist_t} res_hdrs :: Vector{String} code :: CURLcode errbuf :: Vector{UInt8} debug :: Union{Function,Nothing} consem :: Bool end const EMPTY_BYTE_VECTOR = UInt8[] function Easy() easy = Easy( curl_easy_init(), EMPTY_BYTE_VECTOR, Threads.Event(), nothing, Channel{Vector{UInt8}}(Inf), Channel{NTuple{4,Int}}(Inf), C_NULL, String[], typemax(CURLcode), zeros(UInt8, CURL_ERROR_SIZE), nothing, false, ) finalizer(done!, easy) add_callbacks(easy) set_defaults(easy) return easy end function done!(easy::Easy) connect_semaphore_release(easy) easy.handle == C_NULL && return curl_easy_cleanup(easy.handle) curl_slist_free_all(easy.req_hdrs) easy.handle = C_NULL return end # connect semaphore # This semaphore limits the number of requests that can be in the connecting # state at any given time, globally. Throttling this prevents libcurl from # trying to start too many DNS resolver threads concurrently. It also helps # ensure that not-yet-started requests get รŸa chance to make some progress # before adding more events from new requests to the system's workload. const CONNECT_SEMAPHORE = Base.Semaphore(16) # empirically chosen (ie guessed) function connect_semaphore_acquire(easy::Easy) @assert !easy.consem Base.acquire(CONNECT_SEMAPHORE) easy.consem = true return end function connect_semaphore_release(easy::Easy) easy.consem || return Base.release(CONNECT_SEMAPHORE) easy.consem = false return end # request options function set_defaults(easy::Easy) # curl options setopt(easy, CURLOPT_NOSIGNAL, true) setopt(easy, CURLOPT_FOLLOWLOCATION, true) setopt(easy, CURLOPT_MAXREDIRS, 50) setopt(easy, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL) setopt(easy, CURLOPT_USERAGENT, USER_AGENT) setopt(easy, CURLOPT_NETRC, CURL_NETRC_OPTIONAL) setopt(easy, CURLOPT_COOKIEFILE, "") setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT) # prevent downloads that hang forever: # - timeout no response on connect (more than 30s) # - if server transmits nothing for 20s, bail out setopt(easy, CURLOPT_CONNECTTIMEOUT, 30) setopt(easy, CURLOPT_LOW_SPEED_TIME, 20) setopt(easy, CURLOPT_LOW_SPEED_LIMIT, 1) # ssh-related options setopt(easy, CURLOPT_SSH_PRIVATE_KEYFILE, ssh_key_path()) setopt(easy, CURLOPT_SSH_PUBLIC_KEYFILE, ssh_pub_key_path()) key_pass = something(ssh_key_pass(), C_NULL) setopt(easy, CURLOPT_KEYPASSWD, key_pass) end function set_ca_roots_path(easy::Easy, path::AbstractString) Base.unsafe_convert(Cstring, path) # error checking opt = isdir(path) ? CURLOPT_CAPATH : CURLOPT_CAINFO setopt(easy, opt, path) end function set_url(easy::Easy, url::Union{String, SubString{String}}) # TODO: ideally, Clang would generate Cstring signatures Base.unsafe_convert(Cstring, url) # error checking setopt(easy, CURLOPT_URL, url) set_ssl_verify(easy, verify_host(url, "ssl")) set_ssh_verify(easy, verify_host(url, "ssh")) end set_url(easy::Easy, url::AbstractString) = set_url(easy, String(url)) function set_ssl_verify(easy::Easy, verify::Bool) setopt(easy, CURLOPT_SSL_VERIFYPEER, verify) end function set_ssh_verify(easy::Easy, verify::Bool) if !verify setopt(easy, CURLOPT_SSH_KNOWNHOSTS, C_NULL) else file = ssh_known_hosts_file() setopt(easy, CURLOPT_SSH_KNOWNHOSTS, file) end end function set_method(easy::Easy, method::Union{String, SubString{String}}) # TODO: ideally, Clang would generate Cstring signatures Base.unsafe_convert(Cstring, method) # error checking setopt(easy, CURLOPT_CUSTOMREQUEST, method) end set_method(easy::Easy, method::AbstractString) = set_method(easy, String(method)) function set_verbose(easy::Easy, verbose::Bool) setopt(easy, CURLOPT_VERBOSE, verbose) end function set_debug(easy::Easy, debug::Function) hasmethod(debug, Tuple{String,String}) || throw(ArgumentError("debug callback must take (::String, ::String)")) easy.debug = debug add_debug_callback(easy) set_verbose(easy, true) end function set_debug(easy::Easy, debug::Nothing) easy.debug = nothing remove_debug_callback(easy) end function set_body(easy::Easy, body::Bool) setopt(easy, CURLOPT_NOBODY, !body) end function set_upload_size(easy::Easy, size::Integer) opt = Sys.WORD_SIZE โ‰ฅ 64 ? CURLOPT_INFILESIZE_LARGE : CURLOPT_INFILESIZE setopt(easy, opt, size) end function set_seeker(seeker::Function, easy::Easy) add_seek_callback(easy) easy.seeker = seeker end function set_timeout(easy::Easy, timeout::Real) timeout > 0 || throw(ArgumentError("timeout must be positive, got $timeout")) if timeout โ‰ค typemax(Clong) รท 1000 timeout_ms = round(Clong, timeout * 1000) setopt(easy, CURLOPT_TIMEOUT_MS, timeout_ms) else timeout = timeout โ‰ค typemax(Clong) ? round(Clong, timeout) : Clong(0) setopt(easy, CURLOPT_TIMEOUT, timeout) end end function add_header(easy::Easy, hdr::Union{String, SubString{String}}) # TODO: ideally, Clang would generate Cstring signatures Base.unsafe_convert(Cstring, hdr) # error checking easy.req_hdrs = curl_slist_append(easy.req_hdrs, hdr) setopt(easy, CURLOPT_HTTPHEADER, easy.req_hdrs) end add_header(easy::Easy, hdr::AbstractString) = add_header(easy, string(hdr)::String) add_header(easy::Easy, key::AbstractString, val::AbstractString) = add_header(easy, isempty(val) ? "$key;" : "$key: $val") add_header(easy::Easy, key::AbstractString, val::Nothing) = add_header(easy, "$key:") add_header(easy::Easy, pair::Pair) = add_header(easy, pair...) function add_headers(easy::Easy, headers::Union{AbstractVector, AbstractDict}) for hdr in headers hdr isa Pair{<:AbstractString, <:Union{AbstractString, Nothing}} || throw(ArgumentError("invalid header: $(repr(hdr))")) add_header(easy, hdr) end end function enable_progress(easy::Easy, on::Bool=true) setopt(easy, CURLOPT_NOPROGRESS, !on) end function enable_upload(easy::Easy) add_upload_callback(easy::Easy) setopt(easy, CURLOPT_UPLOAD, true) end # response info function get_protocol(easy::Easy) proto_ref = Ref{Clong}() r = @check curl_easy_getinfo(easy.handle, CURLINFO_PROTOCOL, proto_ref) r == CURLE_UNKNOWN_OPTION && error("The `libcurl` version you are using is too old and does not include the `CURLINFO_PROTOCOL` feature. Please upgrade or use a Julia build that uses its own `libcurl` library.") proto = proto_ref[] proto == CURLPROTO_DICT && return "dict" proto == CURLPROTO_FILE && return "file" proto == CURLPROTO_FTP && return "ftp" proto == CURLPROTO_FTPS && return "ftps" proto == CURLPROTO_GOPHER && return "gopher" proto == CURLPROTO_HTTP && return "http" proto == CURLPROTO_HTTPS && return "https" proto == CURLPROTO_IMAP && return "imap" proto == CURLPROTO_IMAPS && return "imaps" proto == CURLPROTO_LDAP && return "ldap" proto == CURLPROTO_LDAPS && return "ldaps" proto == CURLPROTO_POP3 && return "pop3" proto == CURLPROTO_POP3S && return "pop3s" proto == CURLPROTO_RTMP && return "rtmp" proto == CURLPROTO_RTMPE && return "rtmpe" proto == CURLPROTO_RTMPS && return "rtmps" proto == CURLPROTO_RTMPT && return "rtmpt" proto == CURLPROTO_RTMPTE && return "rtmpte" proto == CURLPROTO_RTMPTS && return "rtmpts" proto == CURLPROTO_RTSP && return "rtsp" proto == CURLPROTO_SCP && return "scp" proto == CURLPROTO_SFTP && return "sftp" proto == CURLPROTO_SMB && return "smb" proto == CURLPROTO_SMBS && return "smbs" proto == CURLPROTO_SMTP && return "smtp" proto == CURLPROTO_SMTPS && return "smtps" proto == CURLPROTO_TELNET && return "telnet" proto == CURLPROTO_TFTP && return "tftp" return nothing end status_2xx_ok(status::Integer) = 200 โ‰ค status < 300 status_zero_ok(status::Integer) = status == 0 const PROTOCOL_STATUS = Dict{String,Function}( "dict" => status_2xx_ok, "file" => status_zero_ok, "ftp" => status_2xx_ok, "ftps" => status_2xx_ok, "http" => status_2xx_ok, "https" => status_2xx_ok, "ldap" => status_zero_ok, "ldaps" => status_zero_ok, "pop3" => status_2xx_ok, "pop3s" => status_2xx_ok, "rtsp" => status_2xx_ok, "scp" => status_zero_ok, "sftp" => status_zero_ok, "smtp" => status_2xx_ok, "smtps" => status_2xx_ok, ) function status_ok(proto::AbstractString, status::Integer) test = get(PROTOCOL_STATUS, proto, nothing) test !== nothing && return test(status)::Bool error("Downloads.jl doesn't know the correct request success criterion for $proto: you can use `request` and check the `status` field yourself or open an issue with Downloads with details an example URL that you are trying to download.") end status_ok(proto::Nothing, status::Integer) = false function info_type(type::curl_infotype) type == 0 ? "TEXT" : type == 1 ? "HEADER IN" : type == 2 ? "HEADER OUT" : type == 3 ? "DATA IN" : type == 4 ? "DATA OUT" : type == 5 ? "SSL DATA IN" : type == 6 ? "SSL DATA OUT" : "UNKNOWN" end function get_effective_url(easy::Easy) url_ref = Ref{Ptr{Cchar}}() @check curl_easy_getinfo(easy.handle, CURLINFO_EFFECTIVE_URL, url_ref) return unsafe_string(url_ref[]) end function get_response_status(easy::Easy) code_ref = Ref{Clong}() @check curl_easy_getinfo(easy.handle, CURLINFO_RESPONSE_CODE, code_ref) return Int(code_ref[]) end function get_response_info(easy::Easy) proto = get_protocol(easy) url = get_effective_url(easy) status = get_response_status(easy) message = "" headers = Pair{String,String}[] if proto in ("http", "https") message = isempty(easy.res_hdrs) ? "" : easy.res_hdrs[1] for hdr in easy.res_hdrs if contains(hdr, r"^\s*$") # ignore elseif (m = match(r"^(HTTP/\d+(?:.\d+)?\s+\d+\b.*?)\s*$", hdr); m) !== nothing message = m.captures[1]::SubString{String} empty!(headers) elseif (m = match(r"^(\S[^:]*?)\s*:\s*(.*?)\s*$", hdr); m) !== nothing key = lowercase(m.captures[1]::SubString{String}) val = m.captures[2]::SubString{String} push!(headers, key => val) else @warn "malformed HTTP header" url status header=hdr end end elseif proto in ("ftp", "ftps", "sftp") message = isempty(easy.res_hdrs) ? "" : easy.res_hdrs[end] else # TODO: parse headers of other protocols end message = chomp(message) endswith(message, '.') && (message = chop(message)) return proto, url, status, message, headers end function get_curl_errstr(easy::Easy) easy.code == Curl.CURLE_OK && return "" errstr = easy.errbuf[1] == 0 ? unsafe_string(Curl.curl_easy_strerror(easy.code)) : GC.@preserve easy unsafe_string(pointer(easy.errbuf)) return chomp(errstr) end # callbacks function prereq_callback( easy_p :: Ptr{Cvoid}, conn_remote_ip :: Ptr{Cchar}, conn_local_ip :: Ptr{Cchar}, conn_remote_port :: Cint, conn_local_port :: Cint, )::Cint easy = unsafe_pointer_to_objref(easy_p)::Easy connect_semaphore_release(easy) return 0 end function header_callback( data :: Ptr{Cchar}, size :: Csize_t, count :: Csize_t, easy_p :: Ptr{Cvoid}, )::Csize_t try easy = unsafe_pointer_to_objref(easy_p)::Easy n = size * count hdr = unsafe_string(data, n) push!(easy.res_hdrs, hdr) return n catch err @async @error("header_callback: unexpected error", err=err, maxlog=1_000) return typemax(Csize_t) end end # feed data to read_callback function upload_data(easy::Easy, input::IO) while true data = eof(input) ? nothing : readavailable(input) easy.input === nothing && break easy.input = data curl_easy_pause(easy.handle, Curl.CURLPAUSE_CONT) wait(easy.ready) easy.input === nothing && break easy.ready = Threads.Event() end end function read_callback( data :: Ptr{Cchar}, size :: Csize_t, count :: Csize_t, easy_p :: Ptr{Cvoid}, )::Csize_t try easy = unsafe_pointer_to_objref(easy_p)::Easy buf = easy.input if buf === nothing notify(easy.ready) return 0 # done uploading end if isempty(buf) notify(easy.ready) return CURL_READFUNC_PAUSE # wait for more data end n = min(size * count, length(buf)) ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), data, buf, n) deleteat!(buf, 1:n) return n catch err @async @error("read_callback: unexpected error", err=err, maxlog=1_000) return CURL_READFUNC_ABORT end end function seek_callback( easy_p :: Ptr{Cvoid}, offset :: curl_off_t, origin :: Cint, )::Cint try if origin != 0 @async @error("seek_callback: unsupported seek origin", origin, maxlog=1_000) return CURL_SEEKFUNC_CANTSEEK end easy = unsafe_pointer_to_objref(easy_p)::Easy easy.seeker === nothing && return CURL_SEEKFUNC_CANTSEEK try easy.seeker(offset) catch err @async @error("seek_callback: seeker failed", err, maxlog=1_000) return CURL_SEEKFUNC_FAIL end return CURL_SEEKFUNC_OK catch err @async @error("seek_callback: unexpected error", err=err, maxlog=1_000) return CURL_SEEKFUNC_FAIL end end function write_callback( data :: Ptr{Cchar}, size :: Csize_t, count :: Csize_t, easy_p :: Ptr{Cvoid}, )::Csize_t try easy = unsafe_pointer_to_objref(easy_p)::Easy n = size * count buf = Array{UInt8}(undef, n) ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), buf, data, n) put!(easy.output, buf) return n catch err @async @error("write_callback: unexpected error", err=err, maxlog=1_000) return typemax(Csize_t) end end function progress_callback( easy_p :: Ptr{Cvoid}, dl_total :: curl_off_t, dl_now :: curl_off_t, ul_total :: curl_off_t, ul_now :: curl_off_t, )::Cint try easy = unsafe_pointer_to_objref(easy_p)::Easy put!(easy.progress, (dl_total, dl_now, ul_total, ul_now)) return 0 catch err @async @error("progress_callback: unexpected error", err=err, maxlog=1_000) return -1 end end function debug_callback( handle :: Ptr{Cvoid}, type :: curl_infotype, data :: Ptr{Cchar}, size :: Csize_t, easy_p :: Ptr{Cvoid}, )::Cint try easy = unsafe_pointer_to_objref(easy_p)::Easy @assert easy.handle == handle easy.debug(info_type(type), unsafe_string(data, size)) return 0 catch err @async @error("debug_callback: unexpected error", err=err, maxlog=1_000) return -1 end end function add_callbacks(easy::Easy) # pointer to easy object easy_p = pointer_from_objref(easy) setopt(easy, CURLOPT_PRIVATE, easy_p) # pointer to error buffer errbuf_p = pointer(easy.errbuf) setopt(easy, CURLOPT_ERRORBUFFER, errbuf_p) # set pre-request callback prereq_cb = @cfunction(prereq_callback, Cint, (Ptr{Cvoid}, Ptr{Cchar}, Ptr{Cchar}, Cint, Cint)) setopt(easy, CURLOPT_PREREQFUNCTION, prereq_cb) setopt(easy, CURLOPT_PREREQDATA, easy_p) # set header callback header_cb = @cfunction(header_callback, Csize_t, (Ptr{Cchar}, Csize_t, Csize_t, Ptr{Cvoid})) setopt(easy, CURLOPT_HEADERFUNCTION, header_cb) setopt(easy, CURLOPT_HEADERDATA, easy_p) # set write callback write_cb = @cfunction(write_callback, Csize_t, (Ptr{Cchar}, Csize_t, Csize_t, Ptr{Cvoid})) setopt(easy, CURLOPT_WRITEFUNCTION, write_cb) setopt(easy, CURLOPT_WRITEDATA, easy_p) # set progress callback progress_cb = @cfunction(progress_callback, Cint, (Ptr{Cvoid}, curl_off_t, curl_off_t, curl_off_t, curl_off_t)) setopt(easy, CURLOPT_XFERINFOFUNCTION, progress_cb) setopt(easy, CURLOPT_XFERINFODATA, easy_p) end function add_upload_callback(easy::Easy) # pointer to easy object easy_p = pointer_from_objref(easy) # set read callback read_cb = @cfunction(read_callback, Csize_t, (Ptr{Cchar}, Csize_t, Csize_t, Ptr{Cvoid})) setopt(easy, CURLOPT_READFUNCTION, read_cb) setopt(easy, CURLOPT_READDATA, easy_p) end function add_seek_callback(easy::Easy) # pointer to easy object easy_p = pointer_from_objref(easy) # set seek callback seek_cb = @cfunction(seek_callback, Cint, (Ptr{Cvoid}, curl_off_t, Cint)) setopt(easy, CURLOPT_SEEKFUNCTION, seek_cb) setopt(easy, CURLOPT_SEEKDATA, easy_p) end function add_debug_callback(easy::Easy) # pointer to easy object easy_p = pointer_from_objref(easy) # set debug callback debug_cb = @cfunction(debug_callback, Cint, (Ptr{Cvoid}, curl_infotype, Ptr{Cchar}, Csize_t, Ptr{Cvoid})) setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb) setopt(easy, CURLOPT_DEBUGDATA, easy_p) end function remove_debug_callback(easy::Easy) setopt(easy, CURLOPT_DEBUGFUNCTION, C_NULL) setopt(easy, CURLOPT_DEBUGDATA, C_NULL) end w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Downloads/src/Curl/Multi.jlภ������mutable struct Multi lock :: ReentrantLock handle :: Ptr{Cvoid} timer :: Union{Nothing,Timer} easies :: Vector{Easy} grace :: UInt64 function Multi(grace::Integer = typemax(UInt64)) multi = new(ReentrantLock(), C_NULL, nothing, Easy[], grace) finalizer(done!, multi) @lock MULTIS_LOCK push!(filter!(m -> m.value isa Multi, MULTIS), WeakRef(multi)) return multi end end function init!(multi::Multi) multi.handle != C_NULL && return multi.handle = curl_multi_init() add_callbacks(multi) set_defaults(multi) nothing end function done!(multi::Multi) stoptimer!(multi) handle = multi.handle handle == C_NULL && return multi.handle = C_NULL curl_multi_cleanup(handle) nothing end function stoptimer!(multi::Multi) t = multi.timer if t !== nothing multi.timer = nothing close(t) end nothing end # adding & removing easy handles function add_handle(multi::Multi, easy::Easy) connect_semaphore_acquire(easy) lock(multi.lock) do if isempty(multi.easies) preserve_handle(multi) end push!(multi.easies, easy) init!(multi) @check curl_multi_add_handle(multi.handle, easy.handle) end end const MULTIS_LOCK = Base.ReentrantLock() const MULTIS = WeakRef[] # Close any Multis and their timers at exit that haven't been finalized by then Base.atexit() do while true w = @lock MULTIS_LOCK (isempty(MULTIS) ? nothing : pop!(MULTIS)) w === nothing && break w = w.value w isa Multi && done!(w) end end function remove_handle(multi::Multi, easy::Easy) lock(multi.lock) do @check curl_multi_remove_handle(multi.handle, easy.handle) deleteat!(multi.easies, findlast(==(easy), multi.easies)::Int) isempty(multi.easies) || return stoptimer!(multi) if multi.grace <= 0 done!(multi) elseif 0 < multi.grace < typemax(multi.grace) multi.timer = Timer(multi.grace/1000) do timer lock(multi.lock) do multi.timer === timer || return multi.timer = nothing done!(multi) end end end unpreserve_handle(multi) end connect_semaphore_release(easy) end # multi-socket options function set_defaults(multi::Multi) # currently no defaults end # multi-socket handle state updates struct CURLMsg msg :: CURLMSG easy :: Ptr{Cvoid} code :: CURLcode end function check_multi_info(multi::Multi) while true p = curl_multi_info_read(multi.handle, Ref{Cint}()) p == C_NULL && return message = unsafe_load(convert(Ptr{CURLMsg}, p)) if message.msg == CURLMSG_DONE easy_handle = message.easy easy_p_ref = Ref{Ptr{Cvoid}}() @check curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, easy_p_ref) easy = unsafe_pointer_to_objref(easy_p_ref[])::Easy @assert easy_handle == easy.handle easy.code = message.code close(easy.progress) close(easy.output) easy.input = nothing notify(easy.ready) else @async @error("curl_multi_info_read: unknown message", message, maxlog=1_000) end end end # curl callbacks function do_multi(multi::Multi) @check curl_multi_socket_action(multi.handle, CURL_SOCKET_TIMEOUT, 0) check_multi_info(multi) end function timer_callback( multi_h :: Ptr{Cvoid}, timeout_ms :: Clong, multi_p :: Ptr{Cvoid}, )::Cint try multi = unsafe_pointer_to_objref(multi_p)::Multi @assert multi_h == multi.handle stoptimer!(multi) if timeout_ms >= 0 multi.timer = Timer(timeout_ms/1000) do timer lock(multi.lock) do multi.timer === timer || return multi.timer = nothing do_multi(multi) end end elseif timeout_ms != -1 @async @error("timer_callback: invalid timeout value", timeout_ms, maxlog=1_000) return -1 end return 0 catch err @async @error("timer_callback: unexpected error", err=err, maxlog=1_000) return -1 end end function socket_callback( easy_h :: Ptr{Cvoid}, sock :: curl_socket_t, action :: Cint, multi_p :: Ptr{Cvoid}, watcher_p :: Ptr{Cvoid}, )::Cint try if action โˆ‰ (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT, CURL_POLL_REMOVE) @async @error("socket_callback: unexpected action", action, maxlog=1_000) return -1 end multi = unsafe_pointer_to_objref(multi_p)::Multi if watcher_p != C_NULL old_watcher = unsafe_pointer_to_objref(watcher_p)::FDWatcher @check curl_multi_assign(multi.handle, sock, C_NULL) unpreserve_handle(old_watcher) end if action in (CURL_POLL_IN, CURL_POLL_OUT, CURL_POLL_INOUT) readable = action in (CURL_POLL_IN, CURL_POLL_INOUT) writable = action in (CURL_POLL_OUT, CURL_POLL_INOUT) watcher = FDWatcher(OS_HANDLE(sock), readable, writable) preserve_handle(watcher) watcher_p = pointer_from_objref(watcher) @check curl_multi_assign(multi.handle, sock, watcher_p) task = @async while watcher.readable || watcher.writable # isopen(watcher) events = try wait(watcher) catch err err isa EOFError && return err isa Base.IOError || rethrow() FileWatching.FDEvent() end flags = CURL_CSELECT_IN * isreadable(events) + CURL_CSELECT_OUT * iswritable(events) + CURL_CSELECT_ERR * (events.disconnect || events.timedout) lock(multi.lock) do watcher.readable || watcher.writable || return # !isopen @check curl_multi_socket_action(multi.handle, sock, flags) check_multi_info(multi) end end @isdefined(errormonitor) && errormonitor(task) end @isdefined(old_watcher) && close(old_watcher) return 0 catch err @async @error("socket_callback: unexpected error", err=err, maxlog=1_000) return -1 end end function add_callbacks(multi::Multi) multi_p = pointer_from_objref(multi) # set timer callback timer_cb = @cfunction(timer_callback, Cint, (Ptr{Cvoid}, Clong, Ptr{Cvoid})) setopt(multi, CURLMOPT_TIMERFUNCTION, timer_cb) setopt(multi, CURLMOPT_TIMERDATA, multi_p) # set socket callback socket_cb = @cfunction(socket_callback, Cint, (Ptr{Cvoid}, curl_socket_t, Cint, Ptr{Cvoid}, Ptr{Cvoid})) setopt(multi, CURLMOPT_SOCKETFUNCTION, socket_cb) setopt(multi, CURLMOPT_SOCKETDATA, multi_p) end j���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Pkg.jl{Ž������# This file is a part of Julia. License is MIT: https://julialang.org/license module Pkg if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@max_methods")) @eval Base.Experimental.@max_methods 1 end import Random import REPL import TOML using Dates export @pkg_str export PackageSpec export PackageMode, PKGMODE_MANIFEST, PKGMODE_PROJECT export UpgradeLevel, UPLEVEL_MAJOR, UPLEVEL_MINOR, UPLEVEL_PATCH export PreserveLevel, PRESERVE_TIERED_INSTALLED, PRESERVE_TIERED, PRESERVE_ALL_INSTALLED, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_NONE export Registry, RegistrySpec depots() = Base.DEPOT_PATH function depots1() d = depots() isempty(d) && Pkg.Types.pkgerror("no depots found in DEPOT_PATH") return d[1] end function pkg_server() server = get(ENV, "JULIA_PKG_SERVER", "https://pkg.julialang.org") isempty(server) && return nothing startswith(server, r"\w+://") || (server = "https://$server") return rstrip(server, '/') end logdir(depot = depots1()) = joinpath(depot, "logs") devdir(depot = depots1()) = get(ENV, "JULIA_PKG_DEVDIR", joinpath(depot, "dev")) envdir(depot = depots1()) = joinpath(depot, "environments") const UPDATED_REGISTRY_THIS_SESSION = Ref(false) const OFFLINE_MODE = Ref(false) const RESPECT_SYSIMAGE_VERSIONS = Ref(true) # For globally overriding in e.g. tests const DEFAULT_IO = Ref{Union{IO,Nothing}}(nothing) stderr_f() = something(DEFAULT_IO[], stderr) stdout_f() = something(DEFAULT_IO[], stdout) const PREV_ENV_PATH = Ref{String}("") can_fancyprint(io::IO) = (io isa Base.TTY) && (get(ENV, "CI", nothing) != "true") should_autoprecompile() = Base.JLOptions().use_compiled_modules == 1 && Base.get_bool_env("JULIA_PKG_PRECOMPILE_AUTO", true) include("utils.jl") include("MiniProgressBars.jl") include("GitTools.jl") include("PlatformEngines.jl") include("Versions.jl") include("Registry/Registry.jl") include("Resolve/Resolve.jl") include("Types.jl") include("BinaryPlatforms_compat.jl") include("Artifacts.jl") include("Operations.jl") include("API.jl") include("REPLMode/REPLMode.jl") import .REPLMode: @pkg_str import .Types: UPLEVEL_MAJOR, UPLEVEL_MINOR, UPLEVEL_PATCH, UPLEVEL_FIXED import .Types: PKGMODE_MANIFEST, PKGMODE_PROJECT import .Types: PRESERVE_TIERED_INSTALLED, PRESERVE_TIERED, PRESERVE_ALL_INSTALLED, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_NONE # Import artifacts API using .Artifacts, .PlatformEngines """ PackageMode An enum with the instances * `PKGMODE_MANIFEST` * `PKGMODE_PROJECT` Determines if operations should be made on a project or manifest level. Used as an argument to [`Pkg.rm`](@ref), [`Pkg.update`](@ref) and [`Pkg.status`](@ref). """ const PackageMode = Types.PackageMode """ UpgradeLevel An enum with the instances * `UPLEVEL_FIXED` * `UPLEVEL_PATCH` * `UPLEVEL_MINOR` * `UPLEVEL_MAJOR` Determines how much a package is allowed to be updated. Used as an argument to [`PackageSpec`](@ref) or as an argument to [`Pkg.update`](@ref). """ const UpgradeLevel = Types.UpgradeLevel const PreserveLevel = Types.PreserveLevel # Define new variables so tab comleting Pkg. works. """ Pkg.add(pkg::Union{String, Vector{String}}; preserve=PRESERVE_TIERED) Pkg.add(pkg::Union{PackageSpec, Vector{PackageSpec}}; preserve=PRESERVE_TIERED) Add a package to the current project. This package will be available by using the `import` and `using` keywords in the Julia REPL, and if the current project is a package, also inside that package. ## Resolution Tiers `Pkg` resolves the set of packages in your environment using a tiered algorithm. The `preserve` keyword argument allows you to key into a specific tier in the resolve algorithm. The following table describes the argument values for `preserve` (in order of strictness): | Value | Description | |:----------------------------|:-----------------------------------------------------------------------------------| | `PRESERVE_ALL_INSTALLED` | Like `PRESERVE_ALL` and only add those already installed | | `PRESERVE_ALL` | Preserve the state of all existing dependencies (including recursive dependencies) | | `PRESERVE_DIRECT` | Preserve the state of all existing direct dependencies | | `PRESERVE_SEMVER` | Preserve semver-compatible versions of direct dependencies | | `PRESERVE_NONE` | Do not attempt to preserve any version information | | `PRESERVE_TIERED_INSTALLED` | Like `PRESERVE_TIERED` except `PRESERVE_ALL_INSTALLED` is tried first | | `PRESERVE_TIERED` | Use the tier that will preserve the most version information while | | | allowing version resolution to succeed (this is the default) | !!! note To change the default strategy to `PRESERVE_TIERED_INSTALLED` set the env var `JULIA_PKG_PRESERVE_TIERED_INSTALLED` to true. After the installation of new packages the project will be precompiled. For more information see `pkg> ?precompile`. With the `PRESERVE_ALL_INSTALLED` strategy the newly added packages will likely already be precompiled, but if not this may be because either the combination of package versions resolved in this environment has not been resolved and precompiled before, or the precompile cache has been deleted by the LRU cache storage (see `JULIA_MAX_NUM_PRECOMPILE_FILES`). !!! compat "Julia 1.9" The `PRESERVE_TIERED_INSTALLED` and `PRESERVE_ALL_INSTALLED` strategies requires at least Julia 1.9. # Examples ```julia Pkg.add("Example") # Add a package from registry Pkg.add("Example"; preserve=Pkg.PRESERVE_ALL) # Add the `Example` package and strictly preserve existing dependencies Pkg.add(name="Example", version="0.3") # Specify version; latest release in the 0.3 series Pkg.add(name="Example", version="0.3.1") # Specify version; exact release Pkg.add(url="https://github.com/JuliaLang/Example.jl", rev="master") # From url to remote gitrepo Pkg.add(url="/remote/mycompany/juliapackages/OurPackage") # From path to local gitrepo Pkg.add(url="https://github.com/Company/MonoRepo", subdir="juliapkgs/Package.jl)") # With subdir ``` After the installation of new packages the project will be precompiled. See more at [Environment Precompilation](@ref). See also [`PackageSpec`](@ref), [`Pkg.develop`](@ref). """ const add = API.add """ Pkg.precompile(; strict::Bool=false, timing::Bool=false) Pkg.precompile(pkg; strict::Bool=false, timing::Bool=false) Pkg.precompile(pkgs; strict::Bool=false, timing::Bool=false) Precompile all or specific dependencies of the project in parallel. Set `timing=true` to show the duration of the precompilation of each dependency. !!! note Errors will only throw when precompiling the top-level dependencies, given that not all manifest dependencies may be loaded by the top-level dependencies on the given system. This can be overridden to make errors in all dependencies throw by setting the kwarg `strict` to `true` !!! note This method is called automatically after any Pkg action that changes the manifest. Any packages that have previously errored during precompilation won't be retried in auto mode until they have changed. To disable automatic precompilation set `ENV["JULIA_PKG_PRECOMPILE_AUTO"]=0`. To manually control the number of tasks used set `ENV["JULIA_NUM_PRECOMPILE_TASKS"]`. !!! compat "Julia 1.8" Specifying packages to precompile requires at least Julia 1.8. !!! compat "Julia 1.9" Timing mode requires at least Julia 1.9. # Examples ```julia Pkg.precompile() Pkg.precompile("Foo") Pkg.precompile(["Foo", "Bar"]) ``` """ const precompile = API.precompile """ Pkg.rm(pkg::Union{String, Vector{String}}; mode::PackageMode = PKGMODE_PROJECT) Pkg.rm(pkg::Union{PackageSpec, Vector{PackageSpec}}; mode::PackageMode = PKGMODE_PROJECT) Remove a package from the current project. If `mode` is equal to `PKGMODE_MANIFEST` also remove it from the manifest including all recursive dependencies of `pkg`. See also [`PackageSpec`](@ref), [`PackageMode`](@ref). """ const rm = API.rm """ Pkg.why(pkg::Union{String, Vector{String}}) Pkg.why(pkg::Union{PackageSpec, Vector{PackageSpec}}) Show the reason why this package is in the manifest. The output is all the different ways to reach the package through the dependency graph starting from the dependencies. !!! compat "Julia 1.9" This function requires at least Julia 1.9. """ const why = API.why """ Pkg.update(; level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode = PKGMODE_PROJECT, preserve::PreserveLevel) Pkg.update(pkg::Union{String, Vector{String}}) Pkg.update(pkg::Union{PackageSpec, Vector{PackageSpec}}) If no positional argument is given, update all packages in the manifest if `mode` is `PKGMODE_MANIFEST` and packages in both manifest and project if `mode` is `PKGMODE_PROJECT`. If no positional argument is given, `level` can be used to control by how much packages are allowed to be upgraded (major, minor, patch, fixed). If packages are given as positional arguments, the `preserve` argument can be used to control what other packages are allowed to update: - `PRESERVE_ALL` (default): Only allow `pkg` to update. - `PRESERVE_DIRECT`: Only allow `pkg` and indirect dependencies that are not a direct dependency in the project to update. - `PRESERVE_NONE`: Allow `pkg` and all its indirect dependencies to update. After any package updates the project will be precompiled. See more at [Environment Precompilation](@ref). See also [`PackageSpec`](@ref), [`PackageMode`](@ref), [`UpgradeLevel`](@ref). """ const update = API.up """ Pkg.test(; kwargs...) Pkg.test(pkg::Union{String, Vector{String}; kwargs...) Pkg.test(pkgs::Union{PackageSpec, Vector{PackageSpec}}; kwargs...) **Keyword arguments:** - `coverage::Bool=false`: enable or disable generation of coverage statistics. - `allow_reresolve::Bool=true`: allow Pkg to reresolve the package versions in the test environment - `julia_args::Union{Cmd, Vector{String}}`: options to be passed the test process. - `test_args::Union{Cmd, Vector{String}}`: test arguments (`ARGS`) available in the test process. !!! compat "Julia 1.9" `allow_reresolve` requires at least Julia 1.9. Run the tests for package `pkg`, or for the current project (which thus needs to be a package) if no positional argument is given to `Pkg.test`. A package is tested by running its `test/runtests.jl` file. The tests are run by generating a temporary environment with only the `pkg` package and its (recursive) dependencies in it. If a manifest file exists and the `allow_reresolve` keyword argument is set to `false`, the versions in the manifest file are used. Otherwise a feasible set of packages is resolved and installed. During the tests, test-specific dependencies are active, which are given in the project file as e.g. ``` [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"] ``` The tests are executed in a new process with `check-bounds=yes` and by default `startup-file=no`. If using the startup file (`~/.julia/config/startup.jl`) is desired, start julia with `--startup-file=yes`. Inlining of functions during testing can be disabled (for better coverage accuracy) by starting julia with `--inline=no`. The tests can be run as if different command line arguments were passed to julia by passing the arguments instead to the `julia_args` keyword argument, e.g. ``` Pkg.test("foo"; julia_args=["--inline"]) ``` To pass some command line arguments to be used in the tests themselves, pass the arguments to the `test_args` keyword argument. These could be used to control the code being tested, or to control the tests in some way. For example, the tests could have optional additional tests: ``` if "--extended" in ARGS @test some_function() end ``` which could be enabled by testing with ``` Pkg.test("foo"; test_args=["--extended"]) ``` """ const test = API.test """ Pkg.gc(; collect_delay::Period=Day(7), io::IO=stderr) Garbage-collect package and artifact installations by sweeping over all known `Manifest.toml` and `Artifacts.toml` files, noting those that have been deleted, and then finding artifacts and packages that are thereafter not used by any other projects, marking them as "orphaned". This method will only remove orphaned objects (package versions, artifacts, and scratch spaces) that have been continually un-used for a period of `collect_delay`; which defaults to seven days. """ const gc = API.gc """ Pkg.build(; verbose = false, io::IO=stderr) Pkg.build(pkg::Union{String, Vector{String}}; verbose = false, io::IO=stderr) Pkg.build(pkgs::Union{PackageSpec, Vector{PackageSpec}}; verbose = false, io::IO=stderr) Run the build script in `deps/build.jl` for `pkg` and all of its dependencies in depth-first recursive order. If no argument is given to `build`, the current project is built, which thus needs to be a package. This function is called automatically on any package that gets installed for the first time. `verbose = true` prints the build output to `stdout`/`stderr` instead of redirecting to the `build.log` file. """ const build = API.build """ Pkg.pin(pkg::Union{String, Vector{String}}; io::IO=stderr, all_pkgs::Bool=false) Pkg.pin(pkgs::Union{PackageSpec, Vector{PackageSpec}}; io::IO=stderr, all_pkgs::Bool=false) Pin a package to the current version (or the one given in the `PackageSpec`) or to a certain git revision. A pinned package is never automatically updated: if `pkg` is tracking a path, or a repository, those remain tracked but will not update. To get updates from the origin path or remote repository the package must first be freed. !!! compat "Julia 1.7" The `all_pkgs` kwarg was introduced in julia 1.7. # Examples ```julia Pkg.pin("Example") Pkg.pin(name="Example", version="0.3.1") Pkg.pin(all_pkgs = true) ``` """ const pin = API.pin """ Pkg.free(pkg::Union{String, Vector{String}}; io::IO=stderr, all_pkgs::Bool=false) Pkg.free(pkgs::Union{PackageSpec, Vector{PackageSpec}}; io::IO=stderr, all_pkgs::Bool=false) If `pkg` is pinned, remove the pin. If `pkg` is tracking a path, e.g. after [`Pkg.develop`](@ref), go back to tracking registered versions. To free all dependencies set `all_pkgs=true`. !!! compat "Julia 1.7" The `all_pkgs` kwarg was introduced in julia 1.7. # Examples ```julia Pkg.free("Package") Pkg.free(all_pkgs = true) ``` """ const free = API.free """ Pkg.develop(pkg::Union{String, Vector{String}}; io::IO=stderr, preserve=PRESERVE_TIERED, installed=false) Pkg.develop(pkgs::Union{PackageSpec, Vector{PackageSpec}}; io::IO=stderr, preserve=PRESERVE_TIERED, installed=false) Make a package available for development by tracking it by path. If `pkg` is given with only a name or by a URL, the package will be downloaded to the location specified by the environment variable `JULIA_PKG_DEVDIR`, with `joinpath(DEPOT_PATH[1],"dev")` being the default. If `pkg` is given as a local path, the package at that path will be tracked. The preserve strategies offered by `Pkg.add` are also available via the `preserve` kwarg. See [`Pkg.add`](@ref) for more information. # Examples ```julia # By name Pkg.develop("Example") # By url Pkg.develop(url="https://github.com/JuliaLang/Compat.jl") # By path Pkg.develop(path="MyJuliaPackages/Package.jl") ``` See also [`PackageSpec`](@ref), [`Pkg.add`](@ref). """ const develop = API.develop """ Pkg.generate(pkgname::String) Create a minimal project called `pkgname` in the current folder. For more featureful package creation, please see `PkgTemplates.jl`. """ const generate = API.generate """ Pkg.dependencies()::Dict{UUID, PackageInfo} This feature is considered experimental. Query the dependency graph of the active project. The result is a `Dict` that maps a package UUID to a `PackageInfo` struct representing the dependency (a package). # `PackageInfo` fields | Field | Description | |:------------------ |:----------------------------------------------------------------------------------| | `name` | The name of the package | | `version` | The version of the package (this is `Nothing` for stdlibs) | | `tree_hash` | A file hash of the package directory tree | | `is_direct_dep` | The package is a direct dependency | | `is_pinned` | Whether a package is pinned | | `is_tracking_path` | Whether a package is tracking a path | | `is_tracking_repo` | Whether a package is tracking a repository | | `is_tracking_registry`| Whether a package is being tracked by registry i.e. not by path nor by repository | | `git_revision` | The git revision when tracking by repository | | `git_source` | The git source when tracking by repository | | `source` | The directory containing the source code for that package | | `dependencies` | The dependencies of that package as a vector of UUIDs | """ const dependencies = API.dependencies """ Pkg.project()::ProjectInfo This feature is considered experimental. Request a `ProjectInfo` struct which contains information about the active project. # `ProjectInfo` fields | Field | Description | |:-------------|:--------------------------------------------------------------------------------------------| | name | The project's name | | uuid | The project's UUID | | version | The project's version | | ispackage | Whether the project is a package (has a name and uuid) | | dependencies | The project's direct dependencies as a `Dict` which maps dependency name to dependency UUID | | path | The location of the project file which defines the active project | """ const project = API.project """ Pkg.instantiate(; verbose = false, io::IO=stderr) If a `Manifest.toml` file exists in the active project, download all the packages declared in that manifest. Otherwise, resolve a set of feasible packages from the `Project.toml` files and install them. `verbose = true` prints the build output to `stdout`/`stderr` instead of redirecting to the `build.log` file. If no `Project.toml` exist in the current active project, create one with all the dependencies in the manifest and instantiate the resulting project. After packages have been installed the project will be precompiled. See more at [Environment Precompilation](@ref). """ const instantiate = API.instantiate """ Pkg.resolve(; io::IO=stderr) Update the current manifest with potential changes to the dependency graph from packages that are tracking a path. """ const resolve = API.resolve """ Pkg.status([pkgs...]; outdated::Bool=false, mode::PackageMode=PKGMODE_PROJECT, diff::Bool=false, compat::Bool=false, extensions::Bool=false, io::IO=stdout) Print out the status of the project/manifest. Packages marked with `โŒƒ` have new versions that can be installed, e.g. via [`Pkg.up`](@ref). Those marked with `โŒ…` have new versions available, but cannot be installed due to compatibility conflicts with other packages. To see why, set the keyword argument `outdated=true`. Setting `outdated=true` will only show packages that are not on the latest version, their maximum version and why they are not on the latest version (either due to other packages holding them back due to compatibility constraints, or due to compatibility in the project file). As an example, a status output like: ``` pkg> Pkg.status(; outdated=true) Status `Manifest.toml` โŒƒ [a8cc5b0e] Crayons v2.0.0 [<v3.0.0], (<v4.0.4) โŒ… [b8a86587] NearestNeighbors v0.4.8 (<v0.4.9) [compat] โŒ… [2ab3a3ac] LogExpFunctions v0.2.5 (<v0.3.0): SpecialFunctions ``` means that the latest version of Crayons is 4.0.4 but the latest version compatible with the `[compat]` section in the current project is 3.0.0. The latest version of NearestNeighbors is 0.4.9 but due to compat constrains in the project it is held back to 0.4.8. The latest version of LogExpFunctions is 0.3.0 but SpecialFunctions is holding it back to 0.2.5. If `mode` is `PKGMODE_PROJECT`, print out status only about the packages that are in the project (explicitly added). If `mode` is `PKGMODE_MANIFEST`, print status also about those in the manifest (recursive dependencies). If there are any packages listed as arguments, the output will be limited to those packages. Setting `ext=true` will show dependencies with extensions and what extension dependencies of those that are currently loaded. Setting `diff=true` will, if the environment is in a git repository, limit the output to the difference as compared to the last git commit. See [`Pkg.project`](@ref) and [`Pkg.dependencies`](@ref) to get the project/manifest status as a Julia object instead of printing it. !!! compat "Julia 1.8" The `โŒƒ` and `โŒ…` indicators were added in Julia 1.8. The `outdated` keyword argument requires at least Julia 1.8. """ const status = API.status """ Pkg.compat() Interactively edit the [compat] entries within the current Project. Pkg.compat(pkg::String, compat::String) Set the [compat] string for the given package within the current Project. See [Compatibility](@ref) for more information on the project [compat] section. """ const compat = API.compat """ Pkg.activate([s::String]; shared::Bool=false, io::IO=stderr) Pkg.activate(; temp::Bool=false, shared::Bool=false, io::IO=stderr) Activate the environment at `s`. The active environment is the environment that is modified by executing package commands. The logic for what path is activated is as follows: * If `shared` is `true`, the first existing environment named `s` from the depots in the depot stack will be activated. If no such environment exists, create and activate that environment in the first depot. * If `temp` is `true` this will create and activate a temporary environment which will be deleted when the julia process is exited. * If `s` is an existing path, then activate the environment at that path. * If `s` is a package in the current project and `s` is tracking a path, then activate the environment at the tracked path. * Otherwise, `s` is interpreted as a non-existing path, which is then activated. If no argument is given to `activate`, then use the first project found in `LOAD_PATH`. # Examples ``` Pkg.activate() Pkg.activate("local/path") Pkg.activate("MyDependency") Pkg.activate(; temp=true) ``` """ const activate = API.activate """ Pkg.offline(b::Bool=true) Enable (`b=true`) or disable (`b=false`) offline mode. In offline mode Pkg tries to do as much as possible without connecting to internet. For example, when adding a package Pkg only considers versions that are already downloaded in version resolution. To work in offline mode across Julia sessions you can set the environment variable `JULIA_PKG_OFFLINE` to `"true"`. """ offline(b::Bool=true) = (OFFLINE_MODE[] = b; nothing) """ Pkg.respect_sysimage_versions(b::Bool=true) Enable (`b=true`) or disable (`b=false`) respecting versions that are in the sysimage (enabled by default). If this option is enabled, Pkg will only install packages that have been put into the sysimage (e.g. via PackageCompiler) at the version of the package in the sysimage. Also, trying to add a package at a URL or `develop` a package that is in the sysimage will error. """ respect_sysimage_versions(b::Bool=true) = (RESPECT_SYSIMAGE_VERSIONS[] = b; nothing) """ PackageSpec(name::String, [uuid::UUID, version::VersionNumber]) PackageSpec(; name, url, path, subdir, rev, version, mode, level) A `PackageSpec` is a representation of a package with various metadata. This includes: * The `name` of the package. * The package's unique `uuid`. * A `version` (for example when adding a package). When upgrading, can also be an instance of the enum [`UpgradeLevel`](@ref). If the version is given as a `String` this means that unspecified versions are "free", for example `version="0.5"` allows any version `0.5.x` to be installed. If given as a `VersionNumber`, the exact version is used, for example `version=v"0.5.3"`. * A `url` and an optional git `rev`ision. `rev` can be a branch name or a git commit SHA1. * A local `path`. This is equivalent to using the `url` argument but can be more descriptive. * A `subdir` which can be used when adding a package that is not in the root of a repository. Most functions in Pkg take a `Vector` of `PackageSpec` and do the operation on all the packages in the vector. Many functions that take a `PackageSpec` or a `Vector{PackageSpec}` can be called with a more concise notation with `NamedTuple`s. For example, `Pkg.add` can be called either as the explicit or concise versions as: | Explicit | Concise | |:--------------------------------------------------------------------|:-----------------------------------------------| | `Pkg.add(PackageSpec(name="Package"))` | `Pkg.add(name = "Package")` | | `Pkg.add(PackageSpec(url="www.myhost.com/MyPkg")))` | `Pkg.add(name = "Package")` | |` Pkg.add([PackageSpec(name="Package"), PackageSpec(path="/MyPkg"])` | `Pkg.add([(;name="Package"), (;path="MyPkg")])`| Below is a comparison between the REPL mode and the functional API: | `REPL` | `API` | |:---------------------|:------------------------------------------------------| | `Package` | `PackageSpec("Package")` | | `Package@0.2` | `PackageSpec(name="Package", version="0.2")` | | - | `PackageSpec(name="Package", version=v"0.2.1")` | | `Package=a67d...` | `PackageSpec(name="Package", uuid="a67d...")` | | `Package#master` | `PackageSpec(name="Package", rev="master")` | | `local/path#feature` | `PackageSpec(path="local/path"; rev="feature")` | | `www.mypkg.com` | `PackageSpec(url="www.mypkg.com")` | | `--major Package` | `PackageSpec(name="Package", version=UPLEVEL_MAJOR)` | """ const PackageSpec = Types.PackageSpec """ setprotocol!(; domain::AbstractString = "github.com", protocol::Union{Nothing, AbstractString}=nothing ) Set the protocol used to access hosted packages when `add`ing a url or `develop`ing a package. Defaults to delegating the choice to the package developer (`protocol === nothing`). Other choices for `protocol` are `"https"` or `"git"`. # Examples ```julia-repl julia> Pkg.setprotocol!(domain = "github.com", protocol = "ssh") julia> Pkg.setprotocol!(domain = "gitlab.mycompany.com") ``` """ const setprotocol! = API.setprotocol! """ undo() Undoes the latest change to the active project. Only states in the current session are stored, up to a maximum of $(API.max_undo_limit) states. See also: [`redo`](@ref). """ const undo = API.undo """ redo() Redoes the changes from the latest [`undo`](@ref). """ const redo = API.redo """ RegistrySpec(name::String) RegistrySpec(; name, url, path) A `RegistrySpec` is a representation of a registry with various metadata, much like [`PackageSpec`](@ref). Most registry functions in Pkg take a `Vector` of `RegistrySpec` and do the operation on all the registries in the vector. # Examples Below is a comparison between the REPL mode and the functional API:: | `REPL` | `API` | |:---------------------|:--------------------------------------------------| | `MyRegistry` | `RegistrySpec("MyRegistry")` | | `MyRegistry=a67d...` | `RegistrySpec(name="MyRegistry", uuid="a67d...")` | | `local/path` | `RegistrySpec(path="local/path")` | | `www.myregistry.com` | `RegistrySpec(url="www.myregistry.com")` | """ const RegistrySpec = Registry.RegistrySpec """ upgrade_manifest() upgrade_manifest(manifest_path::String) Upgrades the format of the current or specified manifest file from v1.0 to v2.0 without re-resolving. """ const upgrade_manifest = API.upgrade_manifest """ is_manifest_current(ctx::Context = Context()) Returns whether the active manifest was resolved from the active project state. For instance, if the project had compat entries changed, but the manifest wasn't re-resolved, this would return false. If the manifest doesn't have the project hash recorded, `nothing` is returned. """ const is_manifest_current = API.is_manifest_current function __init__() Pkg.UPDATED_REGISTRY_THIS_SESSION[] = false if isdefined(Base, :active_repl) REPLMode.repl_init(Base.active_repl) else atreplinit() do repl if isinteractive() && repl isa REPL.LineEditREPL isdefined(repl, :interface) || (repl.interface = REPL.setup_interface(repl)) REPLMode.repl_init(repl) end end end push!(empty!(REPL.install_packages_hooks), REPLMode.try_prompt_pkg_add) Base.PKG_PRECOMPILE_HOOK[] = precompile # allows Base to use Pkg.precompile during loading OFFLINE_MODE[] = Base.get_bool_env("JULIA_PKG_OFFLINE", false) return nothing end ################ # Deprecations # ################ function installed() @warn "Pkg.installed() is deprecated" deps = dependencies() installs = Dict{String, VersionNumber}() for (uuid, dep) in deps dep.is_direct_dep || continue dep.version === nothing && continue installs[dep.name] = dep.version::VersionNumber end return installs end function dir(pkg::String, paths::AbstractString...) @warn "`Pkg.dir(pkgname, paths...)` is deprecated; instead, do `import $pkg; joinpath(dirname(pathof($pkg)), \"..\", paths...)`." maxlog=1 pkgid = Base.identify_package(pkg) pkgid === nothing && return nothing path = Base.locate_package(pkgid) path === nothing && return nothing return abspath(path, "..", "..", paths...) end ########### # AUTO GC # ########### const DEPOT_ORPHANAGE_TIMESTAMPS = Dict{String,Float64}() const _auto_gc_enabled = Ref{Bool}(true) function _auto_gc(ctx::Types.Context; collect_delay::Period = Day(7)) if !_auto_gc_enabled[] return end # If we don't know the last time this depot was GC'ed (because this is the # first time we've looked this session), or it looks like we might want to # collect; let's go ahead and hit the filesystem to find the mtime of the # `orphaned.toml` file, which should tell us how long since the last time # we GC'ed. orphanage_path = joinpath(logdir(depots1()), "orphaned.toml") delay_secs = Second(collect_delay).value curr_time = time() if curr_time - get(DEPOT_ORPHANAGE_TIMESTAMPS, depots1(), 0.0) >= delay_secs DEPOT_ORPHANAGE_TIMESTAMPS[depots1()] = mtime(orphanage_path) end if curr_time - DEPOT_ORPHANAGE_TIMESTAMPS[depots1()] > delay_secs printpkgstyle(ctx.io, :Info, "We haven't cleaned this depot up for a bit, running Pkg.gc()...", color = Base.info_color()) try Pkg.gc(ctx; collect_delay) DEPOT_ORPHANAGE_TIMESTAMPS[depots1()] = curr_time catch ex @error("GC failed", exception=ex) end end end ################## # Precompilation # ################## function _auto_precompile(ctx::Types.Context, pkgs::Vector{PackageSpec}=PackageSpec[]; warn_loaded = true, already_instantiated = false) if should_autoprecompile() Pkg.precompile(ctx, pkgs; internal_call=true, warn_loaded = warn_loaded, already_instantiated = already_instantiated) end end using LibGit2: LibGit2 using Tar: Tar function _run_precompilation_script_setup() tmp = mktempdir() cd(tmp) empty!(DEPOT_PATH) pushfirst!(DEPOT_PATH, tmp) touch("Project.toml") Pkg.activate(".") Pkg.generate("TestPkg") uuid = TOML.parsefile(joinpath("TestPkg", "Project.toml"))["uuid"] mv("TestPkg", "TestPkg.jl") tree_hash = cd("TestPkg.jl") do sig = LibGit2.Signature("TEST", "TEST@TEST.COM", round(time()), 0) repo = LibGit2.init(".") LibGit2.add!(repo, "") commit = LibGit2.commit(repo, "initial commit"; author=sig, committer=sig) th = LibGit2.peel(LibGit2.GitTree, LibGit2.GitObject(repo, commit)) |> LibGit2.GitHash |> string close(repo) th end # Prevent cloning the General registry by adding a fake one mkpath("registries/Registry/T/TestPkg") write("registries/Registry/Registry.toml", """ name = "Registry" uuid = "37c07fec-e54c-4851-934c-2e3885e4053e" repo = "https://github.com/JuliaRegistries/Registry.git" [packages] $uuid = { name = "TestPkg", path = "T/TestPkg" } """) write("registries/Registry/T/TestPkg/Compat.toml", """ ["0"] julia = "1" """) write("registries/Registry/T/TestPkg/Deps.toml", """ ["0"] Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" """) write("registries/Registry/T/TestPkg/Versions.toml", """ ["0.1.0"] git-tree-sha1 = "$tree_hash" """) write("registries/Registry/T/TestPkg/Package.toml", """ name = "TestPkg" uuid = "$uuid" repo = "$(escape_string(tmp))/TestPkg.jl" """) Tar.create("registries/Registry", "registries/Registry.tar") cmd = `$(Pkg.PlatformEngines.exe7z()) a "registries/Registry.tar.gz" -tgzip "registries/Registry.tar"` run(pipeline(cmd, stdout = stdout_f(), stderr = stderr_f())) write("registries/Registry.toml", """ git-tree-sha1 = "11b5fad51c4f98cfe0c145ceab0b8fb63fed6f81" uuid = "37c07fec-e54c-4851-934c-2e3885e4053e" path = "Registry.tar.gz" """) Base.rm("registries/Registry"; recursive=true) return tmp end function _run_precompilation_script_artifact() # Create simple artifact, bind it, then use it: foo_hash = Pkg.Artifacts.create_artifact(dir -> touch(joinpath(dir, "foo"))) Artifacts.bind_artifact!("./Artifacts.toml", "foo", foo_hash) # Also create multiple platform-specific ones because that's a codepath we need precompiled Artifacts.bind_artifact!("./Artifacts.toml", "foo_plat", foo_hash; platform=Base.BinaryPlatforms.HostPlatform()) # Because @artifact_str doesn't work at REPL-level, we JIT out a file that we can include() write("load_artifact.jl", """ Pkg.Artifacts.artifact"foo" Pkg.Artifacts.artifact"foo_plat" """) foo_path = include("load_artifact.jl") end const CTRL_C = '\x03' const precompile_script = """ import Pkg _pwd = pwd() Pkg.UPDATED_REGISTRY_THIS_SESSION[] = true tmp = Pkg._run_precompilation_script_setup() $CTRL_C Pkg.add("TestPkg") Pkg.develop(Pkg.PackageSpec(path="TestPkg.jl")) Pkg.add(Pkg.PackageSpec(path="TestPkg.jl/")) Pkg.REPLMode.try_prompt_pkg_add(Symbol[:notapackage]) Pkg.update(; update_registry=false) Pkg.precompile() ] add Te\t\t$CTRL_C ] st $CTRL_C Pkg._run_precompilation_script_artifact() rm(tmp; recursive=true) cd(_pwd) """ end # module l���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/utils.jla ������ function printpkgstyle(io::IO, cmd::Symbol, text::String, ignore_indent::Bool=false; color=:green) indent = textwidth(string(:Precompiling)) # "Precompiling" is the longest operation ignore_indent && (indent = 0) printstyled(io, lpad(string(cmd), indent), color=color, bold=true) println(io, " ", text) end function linewrap(str::String; io = stdout_f(), padding = 0, width = Base.displaysize(io)[2]) text_chunks = split(str, ' ') lines = String[""] for chunk in text_chunks new_line_attempt = string(last(lines), chunk, " ") if length(strip(new_line_attempt)) > width - padding lines[end] = strip(last(lines)) push!(lines, string(chunk, " ")) else lines[end] = new_line_attempt end end return lines end const URL_regex = r"((file|git|ssh|http(s)?)|(git@[\w\-\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)?(/)?"x isurl(r::String) = occursin(URL_regex, r) stdlib_dir() = normpath(joinpath(Sys.BINDIR::String, "..", "share", "julia", "stdlib", "v$(VERSION.major).$(VERSION.minor)")) stdlib_path(stdlib::String) = joinpath(stdlib_dir(), stdlib) function pathrepr(path::String) # print stdlib paths as @stdlib/Name if startswith(path, stdlib_dir()) path = "@stdlib/" * basename(path) end return "`" * Base.contractuser(path) * "`" end function set_readonly(path) for (root, dirs, files) in walkdir(path) for file in files filepath = joinpath(root, file) # `chmod` on a link would change the permissions of the target. If # the link points to a file within the same root, it will be # chmod'ed anyway, but we don't want to make directories read-only. # It's better not to mess with the other cases (links to files # outside of the root, links to non-file/non-directories, etc...) islink(filepath) && continue fmode = filemode(filepath) @static if Sys.iswindows() if Sys.isexecutable(filepath) fmode |= 0o111 end end try chmod(filepath, fmode & (typemax(fmode) โŠป 0o222)) catch end end end return nothing end set_readonly(::Nothing) = nothing # try to call realpath on as much as possible function safe_realpath(path) isempty(path) && return path if ispath(path) try return realpath(path) catch return path end end a, b = splitdir(path) return joinpath(safe_realpath(a), b) end # Windows sometimes throw on `isdir`... function isdir_nothrow(path::String) try isdir(path) catch e false end end function isfile_nothrow(path::String) try isfile(path) catch e false end end function casesensitive_isdir(dir::String) dir = abspath(dir) lastdir = splitpath(dir)[end] isdir_nothrow(dir) && lastdir in readdir(joinpath(dir, "..")) end ## ordering of UUIDs ## if VERSION < v"1.2.0-DEV.269" # Defined in Base as of #30947 Base.isless(a::UUID, b::UUID) = a.value < b.value end w���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/MiniProgressBars.jl๊ ������module MiniProgressBars export MiniProgressBar, start_progress, end_progress, show_progress, print_progress_bottom using Printf Base.@kwdef mutable struct MiniProgressBar max::Int = 1.0 header::String = "" color::Symbol = :nothing width::Int = 40 current::Int = 0.0 prev::Int = 0.0 has_shown::Bool = false time_shown::Float64 = 0.0 percentage::Bool = true always_reprint::Bool = false indent::Int = 4 end const PROGRESS_BAR_TIME_GRANULARITY = Ref(1 / 30.0) # 30 fps const PROGRESS_BAR_PERCENTAGE_GRANULARITY = Ref(0.1) function start_progress(io::IO, _::MiniProgressBar) ansi_disablecursor = "\e[?25l" print(io, ansi_disablecursor) end function show_progress(io::IO, p::MiniProgressBar; termwidth=nothing, carriagereturn=true) if p.max == 0 perc = 0.0 prev_perc = 0.0 else perc = p.current / p.max * 100 prev_perc = p.prev / p.max * 100 end # Bail early if we are not updating the progress bar, # Saves printing to the terminal if !p.always_reprint && p.has_shown && !((perc - prev_perc) > PROGRESS_BAR_PERCENTAGE_GRANULARITY[]) return end t = time() if p.has_shown && (t - p.time_shown) < PROGRESS_BAR_TIME_GRANULARITY[] return end p.time_shown = t p.prev = p.current p.has_shown = true progress_text = if p.percentage @sprintf "%2.1f %%" perc else string(p.current, "/", p.max) end termwidth = @something termwidth displaysize(io)[2] max_progress_width = max(0, min(termwidth - textwidth(p.header) - textwidth(progress_text) - 10 , p.width)) n_filled = ceil(Int, max_progress_width * perc / 100) n_left = max_progress_width - n_filled to_print = sprint(; context=io) do io print(io, " "^p.indent) printstyled(io, p.header, color=p.color, bold=true) print(io, " [") print(io, "="^n_filled, ">") print(io, " "^n_left, "] ", ) print(io, progress_text) carriagereturn && print(io, "\r") end # Print everything in one call print(io, to_print) end function end_progress(io, p::MiniProgressBar) ansi_enablecursor = "\e[?25h" ansi_clearline = "\e[2K" print(io, ansi_enablecursor * ansi_clearline) end # Useful when writing a progress bar in the bottom # makes the bottom progress bar not flicker # prog = MiniProgressBar(...) # prog.end = n # for progress in 1:n # print_progree_bottom(io) # println("stuff") # prog.current = progress # showproress(io, prog) # end # function print_progress_bottom(io::IO) ansi_clearline = "\e[2K" ansi_movecol1 = "\e[1G" ansi_moveup(n::Int) = string("\e[", n, "A") print(io, "\e[S" * ansi_moveup(1) * ansi_clearline * ansi_movecol1) end end o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/GitTools.jl๛.������# This file is a part of Julia. License is MIT: https://julialang.org/license module GitTools using ..Pkg using ..MiniProgressBars import ..can_fancyprint, ..printpkgstyle, ..stdout_f using SHA import Base: SHA1 import LibGit2 using Printf use_cli_git() = Base.get_bool_env("JULIA_PKG_USE_CLI_GIT", false) function transfer_progress(progress::Ptr{LibGit2.TransferProgress}, p::Any) progress = unsafe_load(progress) @assert haskey(p, :transfer_progress) bar = p[:transfer_progress] @assert typeof(bar) == MiniProgressBar if progress.total_deltas != 0 bar.header = "Resolving Deltas:" bar.max = progress.total_deltas bar.current = progress.indexed_deltas else bar.max = progress.total_objects bar.current = progress.received_objects end show_progress(stdout_f(), bar) return Cint(0) end const GIT_REGEX = r"^(?:(?<proto>git|ssh|https)://)?(?:[\w\.\+\-:]+@)?(?<hostname>.+?)(?(<proto>)/|:)(?<path>.+?)(?:\.git)?$" const GIT_PROTOCOLS = Dict{String, Union{Nothing, String}}() const GIT_USERS = Dict{String, Union{Nothing, String}}() @deprecate setprotocol!(proto::Union{Nothing, AbstractString}) setprotocol!(protocol = proto) false function setprotocol!(; domain::AbstractString="github.com", protocol::Union{Nothing, AbstractString}=nothing, user::Union{Nothing, AbstractString}=(protocol == "ssh" ? "git" : nothing) ) domain = lowercase(domain) GIT_PROTOCOLS[domain] = protocol GIT_USERS[domain] = user end function normalize_url(url::AbstractString) # LibGit2 is fussy about trailing slash. Make sure there is none. url = rstrip(url, '/') m = match(GIT_REGEX, url) m === nothing && return url host = m[:hostname] path = "$(m[:path]).git" proto = get(GIT_PROTOCOLS, lowercase(host), nothing) if proto === nothing url else user = get(GIT_USERS, lowercase(host), nothing) user = user === nothing ? "" : "$user@" "$proto://$user$host/$path" end end function ensure_clone(io::IO, target_path, url; kwargs...) if ispath(target_path) return LibGit2.GitRepo(target_path) else return GitTools.clone(io, url, target_path; kwargs...) end end function checkout_tree_to_path(repo::LibGit2.GitRepo, tree::LibGit2.GitObject, path::String) GC.@preserve path begin opts = LibGit2.CheckoutOptions( checkout_strategy = LibGit2.Consts.CHECKOUT_FORCE, target_directory = Base.unsafe_convert(Cstring, path) ) LibGit2.checkout_tree(repo, tree, options=opts) end end function clone(io::IO, url, source_path; header=nothing, credentials=nothing, kwargs...) url = String(url)::String source_path = String(source_path)::String @assert !isdir(source_path) || isempty(readdir(source_path)) url = normalize_url(url) printpkgstyle(io, :Cloning, header === nothing ? "git-repo `$url`" : header) bar = MiniProgressBar(header = "Fetching:", color = Base.info_color()) fancyprint = can_fancyprint(io) callbacks = if fancyprint LibGit2.Callbacks( :transfer_progress => ( @cfunction(transfer_progress, Cint, (Ptr{LibGit2.TransferProgress}, Any)), bar, ) ) else LibGit2.Callbacks() end fancyprint && start_progress(io, bar) if credentials === nothing credentials = LibGit2.CachedCredentials() end try if use_cli_git() cmd = `git clone --quiet $url $source_path` try run(pipeline(cmd; stdout=devnull)) catch err Pkg.Types.pkgerror("The command $(cmd) failed, error: $err") end return LibGit2.GitRepo(source_path) else mkpath(source_path) return LibGit2.clone(url, source_path; callbacks=callbacks, credentials=credentials, kwargs...) end catch err rm(source_path; force=true, recursive=true) err isa LibGit2.GitError || err isa InterruptException || rethrow() if err isa InterruptException Pkg.Types.pkgerror("git clone of `$url` interrupted") elseif (err.class == LibGit2.Error.Net && err.code == LibGit2.Error.EINVALIDSPEC) || (err.class == LibGit2.Error.Repository && err.code == LibGit2.Error.ENOTFOUND) Pkg.Types.pkgerror("git repository not found at `$(url)`") else Pkg.Types.pkgerror("failed to clone from $(url), error: $err") end finally Base.shred!(credentials) fancyprint && end_progress(io, bar) end end function fetch(io::IO, repo::LibGit2.GitRepo, remoteurl=nothing; header=nothing, credentials=nothing, refspecs=[""], kwargs...) if remoteurl === nothing remoteurl = LibGit2.with(LibGit2.get(LibGit2.GitRemote, repo, "origin")) do remote LibGit2.url(remote) end end fancyprint = can_fancyprint(io) remoteurl = normalize_url(remoteurl) printpkgstyle(io, :Updating, header === nothing ? "git-repo `$remoteurl`" : header) bar = MiniProgressBar(header = "Fetching:", color = Base.info_color()) fancyprint = can_fancyprint(io) callbacks = if fancyprint LibGit2.Callbacks( :transfer_progress => ( @cfunction(transfer_progress, Cint, (Ptr{LibGit2.TransferProgress}, Any)), bar, ) ) else LibGit2.Callbacks() end fancyprint && start_progress(io, bar) if credentials === nothing credentials = LibGit2.CachedCredentials() end try if use_cli_git() let remoteurl=remoteurl cd(LibGit2.path(repo)) do cmd = `git fetch -q $remoteurl $(only(refspecs))` try run(pipeline(cmd; stdout=devnull)) catch err Pkg.Types.pkgerror("The command $(cmd) failed, error: $err") end end end else return LibGit2.fetch(repo; remoteurl=remoteurl, callbacks=callbacks, refspecs=refspecs, kwargs...) end catch err err isa LibGit2.GitError || rethrow() if (err.class == LibGit2.Error.Repository && err.code == LibGit2.Error.ERROR) Pkg.Types.pkgerror("Git repository not found at '$(remoteurl)'") else Pkg.Types.pkgerror("failed to fetch from $(remoteurl), error: $err") end finally Base.shred!(credentials) fancyprint && end_progress(io, bar) end end # This code gratefully adapted from https://github.com/simonbyrne/GitX.jl @enum GitMode mode_dir=0o040000 mode_normal=0o100644 mode_executable=0o100755 mode_symlink=0o120000 mode_submodule=0o160000 Base.string(mode::GitMode) = string(UInt32(mode); base=8) Base.print(io::IO, mode::GitMode) = print(io, string(mode)) function gitmode(path::AbstractString) # Windows doesn't deal with executable permissions in quite the same way, # `stat()` gives a different answer than we actually want, so we use # `isexecutable()` which uses `uv_fs_access()` internally. On other # platforms however, we just want to check via `stat()`. function isexec(p) @static if Sys.iswindows() return Sys.isexecutable(p) end return !iszero(filemode(p) & 0o100) end if islink(path) return mode_symlink elseif isdir(path) return mode_dir elseif isexec(path) return mode_executable else return mode_normal end end """ blob_hash(HashType::Type, path::AbstractString) Calculate the git blob hash of a given path. """ function blob_hash(::Type{HashType}, path::AbstractString) where HashType ctx = HashType() if islink(path) datalen = length(readlink(path)) else datalen = filesize(path) end # First, the header SHA.update!(ctx, Vector{UInt8}("blob $(datalen)\0")) # Next, read data in in chunks of 4KB buff = Vector{UInt8}(undef, 4*1024) try if islink(path) update!(ctx, Vector{UInt8}(readlink(path))) else open(path, "r") do io while !eof(io) num_read = readbytes!(io, buff) update!(ctx, buff, num_read) end end end catch e if isa(e, InterruptException) rethrow(e) end @warn("Unable to open $(path) for hashing; git-tree-sha1 likely suspect") end # Finish it off and return the digest! return SHA.digest!(ctx) end blob_hash(path::AbstractString) = blob_hash(SHA1_CTX, path) """ contains_files(root::AbstractString) Helper function to determine whether a directory contains files; e.g. it is a direct parent of a file or it contains some other directory that itself is a direct parent of a file. This is used to exclude directories from tree hashing. """ function contains_files(path::AbstractString) st = lstat(path) ispath(st) || throw(ArgumentError("non-existent path: $(repr(path))")) isdir(st) || return true for p in readdir(path) contains_files(joinpath(path, p)) && return true end return false end """ tree_hash(HashType::Type, root::AbstractString) Calculate the git tree hash of a given path. """ function tree_hash(::Type{HashType}, root::AbstractString; debug_out::Union{IO,Nothing} = nothing, indent::Int=0) where HashType entries = Tuple{String, Vector{UInt8}, GitMode}[] for f in sort(readdir(root; join=true); by = f -> gitmode(f) == mode_dir ? f*"/" : f) # Skip `.git` directories if basename(f) == ".git" continue end filepath = abspath(f) mode = gitmode(filepath) if mode == mode_dir # If this directory contains no files, then skip it contains_files(filepath) || continue # Otherwise, hash it up! child_stream = nothing if debug_out !== nothing child_stream = IOBuffer() end hash = tree_hash(HashType, filepath; debug_out=child_stream, indent=indent+1) if debug_out !== nothing indent_str = "| "^indent println(debug_out, "$(indent_str)+ [D] $(basename(filepath)) - $(bytes2hex(hash))") print(debug_out, String(take!(child_stream))) println(debug_out, indent_str) end else hash = blob_hash(HashType, filepath) if debug_out !== nothing indent_str = "| "^indent mode_str = mode == mode_normal ? "F" : "X" println(debug_out, "$(indent_str)[$(mode_str)] $(basename(filepath)) - $(bytes2hex(hash))") end end push!(entries, (basename(filepath), hash, mode)) end content_size = 0 for (n, h, m) in entries content_size += ndigits(UInt32(m); base=8) + 1 + sizeof(n) + 1 + sizeof(h) end # Return the hash of these entries ctx = HashType() SHA.update!(ctx, Vector{UInt8}("tree $(content_size)\0")) for (name, hash, mode) in entries SHA.update!(ctx, Vector{UInt8}("$(mode) $(name)\0")) SHA.update!(ctx, hash) end return SHA.digest!(ctx) end tree_hash(root::AbstractString; debug_out::Union{IO,Nothing} = nothing) = tree_hash(SHA.SHA1_CTX, root; debug_out) function check_valid_HEAD(repo) try LibGit2.head(repo) catch err Pkg.Types.pkgerror("invalid git HEAD ($(err.msg))") end end function git_file_stream(repo::LibGit2.GitRepo, spec::String; fakeit::Bool=false)::IO blob = try LibGit2.GitBlob(repo, spec) catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() fakeit && return devnull end iob = IOBuffer(LibGit2.content(blob)) close(blob) return iob end end # module v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/PlatformEngines.jl6^������# This file is a part of Julia. License is MIT: https://julialang.org/license # Content in this file is extracted from BinaryProvider.jl, see LICENSE.method module PlatformEngines using SHA, Downloads, Tar import ...Pkg: Pkg, TOML, pkg_server, depots1, can_fancyprint, stderr_f using ..MiniProgressBars using Base.BinaryPlatforms, p7zip_jll export verify, unpack, package, download_verify_unpack const EXE7Z_LOCK = ReentrantLock() const EXE7Z = Ref{String}() function exe7z() # If the JLL is available, use the wrapper function defined in there if p7zip_jll.is_available() return p7zip_jll.p7zip() end lock(EXE7Z_LOCK) do if !isassigned(EXE7Z) EXE7Z[] = find7z() end return Cmd([EXE7Z[]]) end end function find7z() name = "7z" Sys.iswindows() && (name = "$name.exe") for dir in (joinpath("..", "libexec"), ".") path = normpath(Sys.BINDIR::String, dir, name) isfile(path) && return path end path = Sys.which(name) path !== nothing && return path error("7z binary not found") end is_secure_url(url::AbstractString) = occursin(r"^(https://|\w+://(127\.0\.0\.1|localhost)(:\d+)?($|/))"i, url) function get_server_dir( url :: AbstractString, server :: Union{AbstractString, Nothing} = pkg_server(), ) server === nothing && return url == server || startswith(url, "$server/") || return m = match(r"^\w+://([^\\/]+)(?:$|/)", server) if m === nothing @warn "malformed Pkg server value" server return end isempty(Base.DEPOT_PATH) && return invalid_filename_chars = [':', '/', '<', '>', '"', '/', '\\', '|', '?', '*'] dir = join(replace(c -> c in invalid_filename_chars ? '_' : c, collect(String(m[1])))) return joinpath(depots1(), "servers", dir) end const AUTH_ERROR_HANDLERS = Pair{Union{String, Regex},Any}[] function handle_auth_error(url, err; verbose::Bool = false) handled, should_retry = false, false for (scheme, handler) in AUTH_ERROR_HANDLERS occursin(scheme, url) || continue handled, should_retry = handler(url, pkg_server(), err) handled && break end handled && should_retry && return get_auth_header(url; verbose = verbose) return nothing end """ register_auth_error_handler(urlscheme::Union{AbstractString, Regex}, f) Registers `f` as the topmost handler for failures in package server authentication. A handler is only invoked if `occursin(urlscheme, url)` is true (where `url` is the URL Pkg is currently trying to download.) `f` must be a function that takes three input arguments `(url, pkgserver, err)`, where `url` is the URL currently being downloaded, `pkgserver = Pkg.pkg_server()` the current package server, and `err` is one of `no-auth-file`, `insecure-connection`, `malformed-file`, `no-access-token`, `no-refresh-key` or `insecure-refresh-url`. The handler `f` needs to return a tuple of `Bool`s `(handled, should_retry)`. If `handled` is `false`, the next handler in the stack will be called, otherwise handling terminates; `get_auth_header` is called again if `should_retry` is `true`. `register_auth_error_handler` returns a zero-arg function that can be called to deregister the handler. """ function register_auth_error_handler(urlscheme::Union{AbstractString, Regex}, @nospecialize(f)) unique!(pushfirst!(AUTH_ERROR_HANDLERS, urlscheme => f)) return () -> deregister_auth_error_handler(urlscheme, f) end """ deregister_auth_error_handler(urlscheme::Union{AbstractString, Regex}, f) Removes `f` from the stack of authentication error handlers. """ function deregister_auth_error_handler(urlscheme::Union{String, Regex}, @nospecialize(f)) filter!(handler -> !(handler.first == urlscheme && handler.second === f), AUTH_ERROR_HANDLERS) return nothing end function get_auth_header(url::AbstractString; verbose::Bool = false) server_dir = get_server_dir(url) server_dir === nothing && return auth_file = joinpath(server_dir, "auth.toml") isfile(auth_file) || return handle_auth_error(url, "no-auth-file"; verbose=verbose) # TODO: check for insecure auth file permissions if !is_secure_url(url) @warn "refusing to send auth info over insecure connection" url=url return handle_auth_error(url, "insecure-connection"; verbose=verbose) end # parse the auth file auth_info = try TOML.parsefile(auth_file) catch err @error "malformed auth file" file=auth_file err=err return handle_auth_error(url, "malformed-file"; verbose=verbose) end # check for an auth token if !haskey(auth_info, "access_token") @warn "auth file without access_token field" file=auth_file return handle_auth_error(url, "no-access-token"; verbose=verbose) end auth_token = auth_info["access_token"]::String auth_header = "Authorization" => "Bearer $auth_token" # handle token expiration and refresh expires_at = Inf if haskey(auth_info, "expires_at") expires_at = min(expires_at, Float64(auth_info["expires_at"])::Float64) end if haskey(auth_info, "expires_in") expires_at = min(expires_at, mtime(auth_file) + Float64(auth_info["expires_in"])::Float64) end # if token is good until ten minutes from now, use it time_now = time() if expires_at โ‰ฅ time_now + 10*60 # ten minutes return auth_header end if !haskey(auth_info, "refresh_url") || !haskey(auth_info, "refresh_token") if expires_at โ‰คย time_now @warn "expired auth without refresh keys" file=auth_file end # try it anyway since we can't refresh return something(handle_auth_error(url, "no-refresh-key"; verbose=verbose), auth_header) end refresh_url = auth_info["refresh_url"]::String if !is_secure_url(refresh_url) @warn "ignoring insecure auth refresh URL" url=refresh_url return something(handle_auth_error(url, "insecure-refresh-url"; verbose=verbose), auth_header) end verbose && @info "Refreshing expired auth token..." file=auth_file tmp = tempname() refresh_token = auth_info["refresh_token"]::String refresh_auth = "Authorization" => "Bearer $refresh_token" try download(refresh_url, tmp, auth_header=refresh_auth, verbose=verbose) catch err @warn "token refresh failure" file=auth_file url=refresh_url err=err rm(tmp, force=true) return handle_auth_error(url, "token-refresh-failed"; verbose=verbose) end auth_info = try TOML.parsefile(tmp) catch err @warn "discarding malformed auth file" url=refresh_url err=err rm(tmp, force=true) return something(handle_auth_error(url, "malformed-file"; verbose=verbose), auth_header) end if !haskey(auth_info, "access_token") if haskey(auth_info, "refresh_token") auth_info["refresh_token"] = "*"^64 end @warn "discarding auth file without access token" auth=auth_info rm(tmp, force=true) return something(handle_auth_error(url, "no-access-token"; verbose=verbose), auth_header) end if haskey(auth_info, "expires_in") expires_in = auth_info["expires_in"] if expires_in isa Number expires_at = floor(time_now + Float64(expires_in)::Float64) # overwrite expires_at (avoids clock skew issues) auth_info["expires_at"] = expires_at end end let auth_info = auth_info open(tmp, write=true) do io TOML.print(io, auth_info, sorted=true) end end mv(tmp, auth_file, force=true) access_token = auth_info["access_token"]::String return "Authorization" => "Bearer $access_token" end # based on information in this post: # https://github.community/t5/GitHub-Actions/Have-the-CI-environment-variable-set-by-default/m-p/32358/highlight/true#M1097 const CI_VARIABLES = [ "APPVEYOR", "CI", "CI_SERVER", "CIRCLECI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "GITLAB_CI", "JULIA_CI", "JULIA_PKGEVAL", "JULIA_REGISTRYCI_AUTOMERGE", "TF_BUILD", "TRAVIS", ] function get_metadata_headers(url::AbstractString) headers = Pair{String,String}[] server = pkg_server() server_dir = get_server_dir(url, server) server_dir === nothing && return headers push!(headers, "Julia-Pkg-Protocol" => "1.0") push!(headers, "Julia-Pkg-Server" => server) push!(headers, "Julia-Version" => string(VERSION)) system = triplet(HostPlatform()) push!(headers, "Julia-System" => system) ci_info = String[] for var in CI_VARIABLES val = get(ENV, var, nothing) state = val === nothing ? "n" : lowercase(val) in ("true", "t", "1", "yes", "y") ? "t" : lowercase(val) in ("false", "f", "0", "no", "n") ? "f" : "o" push!(ci_info, "$var=$state") end push!(headers, "Julia-CI-Variables" => join(ci_info, ';')) push!(headers, "Julia-Interactive" => string(isinteractive())) for (key, val) in ENV m = match(r"^JULIA_PKG_SERVER_([A-Z0-9_]+)$"i, key) m === nothing && continue val = strip(val) isempty(val) && continue words = split(m.captures[1], '_', keepempty=false) isempty(words) && continue hdr = "Julia-" * join(map(titlecase, words), '-') any(hdr == k for (k, v) in headers) && continue push!(headers, hdr => val) end return headers end function download( url::AbstractString, dest::AbstractString; verbose::Bool = false, headers::Vector{Pair{String,String}} = Pair{String,String}[], auth_header::Union{Pair{String,String}, Nothing} = nothing, io::IO=stderr_f() ) if auth_header === nothing auth_header = get_auth_header(url, verbose=verbose) end if auth_header !== nothing push!(headers, auth_header) end for header in get_metadata_headers(url) push!(headers, header) end do_fancy = verbose && can_fancyprint(io) progress = if do_fancy bar = MiniProgressBar(header="Downloading", color=Base.info_color()) start_progress(io, bar) let bar=bar (total, now) -> begin bar.max = total bar.current = now show_progress(io, bar) end end else (total, now) -> nothing end try Downloads.download(url, dest; headers, progress) finally do_fancy && end_progress(io, bar) end end """ download_verify( url::AbstractString, hash::Union{AbstractString, Nothing}, dest::AbstractString; verbose::Bool = false, force::Bool = false, quiet_download::Bool = false, ) Download file located at `url`, verify it matches the given `hash`, and throw an error if anything goes wrong. If `dest` already exists, just verify it. If `force` is set to `true`, overwrite the given file if it exists but does not match the given `hash`. This method returns `true` if the file was downloaded successfully, `false` if an existing file was removed due to the use of `force`, and throws an error if `force` is not set and the already-existent file fails verification, or if `force` is set, verification fails, and then verification fails again after redownloading the file. If `quiet_download` is set to `false`, this method will print to stdout when downloading a new file. If it is set to `true` (default, and `verbose` is set to `false`) the downloading process will be completely silent. If `verbose` is set to `true`, messages about integrity verification will be printed in addition to messages regarding downloading. """ function download_verify( url::AbstractString, hash::Union{AbstractString, Nothing}, dest::AbstractString; verbose::Bool = false, force::Bool = false, quiet_download::Bool = false, ) # Whether the file existed in the first place file_existed = false if isfile(dest) file_existed = true if verbose @info("Destination file $(dest) already exists, verifying...") end # verify download, if it passes, return happy. If it fails, (and # `force` is `true`, re-download!) if hash !== nothing && verify(dest, hash; verbose=verbose) return true elseif !force error("Verification failed, not overwriting $(dest)") end end # Make sure the containing folder exists mkpath(dirname(dest)) # Download the file, optionally continuing f = retry(delays = fill(1.0, 3)) do try download(url, dest; verbose=verbose || !quiet_download) catch err @debug "download and verify failed" url dest err rethrow() end end f() if hash !== nothing && !verify(dest, hash; verbose=verbose) # If the file already existed, it's possible the initially downloaded chunk # was bad. If verification fails after downloading, auto-delete the file # and start over from scratch. if file_existed if verbose @info("Continued download didn't work, restarting from scratch") end Base.rm(dest; force=true) # Download and verify from scratch download(url, dest; verbose=verbose || !quiet_download) if hash !== nothing && !verify(dest, hash; verbose=verbose) error("Verification failed. Download does not match expected hash") end else # If it didn't verify properly and we didn't resume, something is # very wrong and we must complain mightily. error("Verification failed. Download does not match expected hash") end end # If the file previously existed, this means we removed it (due to `force`) # and redownloaded, so return `false`. If it didn't exist, then this means # that we successfully downloaded it, so return `true`. return !file_existed end # TODO: can probably delete this, only affects tests function copy_symlinks() var = get(ENV, "BINARYPROVIDER_COPYDEREF", "") lowercase(var) in ("true", "t", "yes", "y", "1") ? true : lowercase(var) in ("false", "f", "no", "n", "0") ? false : nothing end function unpack( tarball_path::AbstractString, dest::AbstractString; verbose::Bool = false, ) Tar.extract(`$(exe7z()) x $tarball_path -so`, dest, copy_symlinks = copy_symlinks()) end """ package(src_dir::AbstractString, tarball_path::AbstractString) Compress `src_dir` into a tarball located at `tarball_path`. """ function package(src_dir::AbstractString, tarball_path::AbstractString; io=stderr_f()) rm(tarball_path, force=true) cmd = `$(exe7z()) a -si -tgzip -mx9 $tarball_path` open(pipeline(cmd, stdout=devnull, stderr=io), write=true) do io Tar.create(src_dir, io) end end """ download_verify_unpack( url::AbstractString, hash::Union{AbstractString, Nothing}, dest::AbstractString; tarball_path = nothing, ignore_existence::Bool = false, force::Bool = false, verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr, ) Helper method to download tarball located at `url`, verify it matches the given `hash`, then unpack it into folder `dest`. In general, the method `install()` should be used to download and install tarballs into a `Prefix`; this method should only be used if the extra functionality of `install()` is undesired. If `tarball_path` is specified, the given `url` will be downloaded to `tarball_path`, and it will not be removed after downloading and verification is complete. If it is not specified, the tarball will be downloaded to a temporary location, and removed after verification is complete. If `force` is specified, a verification failure will cause `tarball_path` to be deleted (if it exists), the `dest` folder to be removed (if it exists) and the tarball to be redownloaded and reverified. If the verification check is failed a second time, an exception is raised. If `force` is not specified, a verification failure will result in an immediate raised exception. If `ignore_existence` is set, the tarball is unpacked even if the destination directory already exists. Returns `true` if a tarball was actually unpacked, `false` if nothing was changed in the destination prefix. """ function download_verify_unpack( url::AbstractString, hash::Union{AbstractString, Nothing}, dest::AbstractString; tarball_path = nothing, ignore_existence::Bool = false, force::Bool = false, verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr_f(), ) # First, determine whether we should keep this tarball around remove_tarball = false if tarball_path === nothing remove_tarball = true function url_ext(url) url = basename(url) # Chop off urlparams qidx = findfirst(isequal('?'), url) if qidx !== nothing url = url[1:qidx] end # Try to detect extension dot_idx = findlast(isequal('.'), url) if dot_idx === nothing return nothing end return url[dot_idx+1:end] end # If extension of url contains a recognized extension, use it, otherwise use ".gz" ext = url_ext(url) if !(ext in ["tar", "gz", "tgz", "bz2", "xz"]) ext = "gz" end # Work around windows limitations regarding tempname() tarball_path = "$(tempname())-download.$(ext)" tries = 0 while isfile(tarball_path) && tries < 100 tarball_path = "$(tempname())-download.$(ext)" tries += 1 end if tries >= 100 error("Unable to generate unused tempname! Clean up your temporary folder $(dirname(tempname())) and try again.") end end # Download the tarball; if it already existed and we needed to remove it # then we should remove the unpacked path as well should_delete = !download_verify(url, hash, tarball_path; force=force, verbose=verbose, quiet_download=quiet_download) if should_delete if verbose @info("Removing dest directory $(dest) as source tarball changed") end Base.rm(dest; recursive=true, force=true) end # If the destination path already exists, don't bother to unpack if !ignore_existence && isdir(dest) if verbose @info("Destination directory $(dest) already exists, returning") end # Signify that we didn't do any unpacking return false end try if verbose @info("Unpacking $(tarball_path) into $(dest)...") end open(`$(exe7z()) x $tarball_path -so`) do io Tar.extract(io, dest, copy_symlinks = copy_symlinks()) end finally if remove_tarball Base.rm(tarball_path) # Remove cached tarball hash, if it exists. Base.rm(string(tarball_path, ".sha256"); force=true) end end # Signify that we did some unpacking! return true end """ verify(path::AbstractString, hash::AbstractString; verbose::Bool = false, report_cache_status::Bool = false) Given a file `path` and a `hash`, calculate the SHA256 of the file and compare it to `hash`. This method caches verification results in a `"\$(path).sha256"` file to accelerate reverification of files that have been previously verified. If no `".sha256"` file exists, a full verification will be done and the file will be created, with the calculated hash being stored within the `".sha256"` file. If a `".sha256"` file does exist, its contents are checked to ensure that the hash contained within matches the given `hash` parameter, and its modification time shows that the file located at `path` has not been modified since the last verification. If `report_cache_status` is set to `true`, then the return value will be a `Symbol` giving a granular status report on the state of the hash cache, in addition to the `true`/`false` signifying whether verification completed successfully. """ function verify(path::AbstractString, hash::AbstractString; verbose::Bool = false, report_cache_status::Bool = false, hash_path::AbstractString="$(path).sha256") # Check hash string format if !occursin(r"^[0-9a-f]{64}$"i, hash) msg = "Hash value must be 64 hexadecimal characters (256 bits), " if !isascii(hash) msg *= "given hash value is non-ASCII" elseif occursin(r"^[0-9a-f]*$"i, hash) msg *= "given hash value has the wrong length ($(length(hash)))" else msg *= "given hash value contains non-hexadecimal characters" end msg *= ": $(repr(hash))" error(msg) end hash = lowercase(hash) # Check to see if the hash cache is consistent status = :hash_consistent # First, it must exist if isfile(hash_path) # Next, it must contain the same hash as what we're verifying against if read(hash_path, String) == hash # Next, it must be no older than the actual path if stat(hash_path).mtime >= stat(path).mtime # If all of that is true, then we're good! if verbose @info("Hash cache is consistent, returning true") end status = :hash_cache_consistent # If we're reporting our status, then report it! if report_cache_status return true, status else return true end else if verbose @info("File has been modified, hash cache invalidated") end status = :file_modified end else if verbose @info("Verification hash mismatch, hash cache invalidated") end status = :hash_cache_mismatch end else if verbose @info("No hash cache found") end status = :hash_cache_missing end calc_hash = open(path) do file bytes2hex(sha256(file)) end @assert occursin(r"^[0-9a-f]{64}$", calc_hash) if verbose @info("Calculated hash $calc_hash for file $path") end if calc_hash != hash msg = "Hash Mismatch!\n" msg *= " Expected sha256: $hash\n" msg *= " Calculated sha256: $calc_hash" @error(msg) if report_cache_status return false, :hash_mismatch else return false end end # Try to save a hash cache if everything worked out fine try open(hash_path, "w") do file write(file, hash) end catch e if isa(e, InterruptException) rethrow(e) end if verbose @warn("Unable to create hash cache file $(hash_path)") end end if report_cache_status return true, status else return true end end # Verify the git-tree-sha1 hash of a compressed archive. function verify_archive_tree_hash(tar_gz::AbstractString, expected_hash::Base.SHA1) # This can fail because unlike sha256 verification of the downloaded # tarball, tree hash verification requires that the file can i) be # decompressed and ii) is a proper archive. calc_hash = try Base.SHA1(open(Tar.tree_hash, `$(exe7z()) x $tar_gz -so`)) catch err @warn "unable to decompress and read archive" exception=err return false end if calc_hash != expected_hash @warn "tarball content does not match expected git-tree-sha1" return false end return true end end # module PlatformEngines v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/p7zip_jll/src/p7zip_jll.jl# ������# This file is a part of Julia. License is MIT: https://julialang.org/license ## dummy stub for https://github.com/JuliaBinaryWrappers/p7zip_jll.jl baremodule p7zip_jll using Base Base.Experimental.@compiler_options compile=min optimize=0 infer=false const PATH_list = String[] const LIBPATH_list = String[] export p7zip # These get calculated in __init__() const PATH = Ref("") const LIBPATH = Ref("") artifact_dir::String = "" p7zip_path::String = "" if Sys.iswindows() const p7zip_exe = "7z.exe" else const p7zip_exe = "7z" end if Sys.iswindows() const LIBPATH_env = "PATH" const LIBPATH_default = "" const pathsep = ';' elseif Sys.isapple() const LIBPATH_env = "DYLD_FALLBACK_LIBRARY_PATH" const LIBPATH_default = "~/lib:/usr/local/lib:/lib:/usr/lib" const pathsep = ':' else const LIBPATH_env = "LD_LIBRARY_PATH" const LIBPATH_default = "" const pathsep = ':' end function adjust_ENV!(env::Dict{keytype(Base.EnvDict),valtype(Base.EnvDict)}, PATH::String, LIBPATH::String, adjust_PATH::Bool, adjust_LIBPATH::Bool) if adjust_LIBPATH LIBPATH_base = get(env, LIBPATH_env, expanduser(LIBPATH_default)) if !isempty(LIBPATH_base) env[LIBPATH_env] = string(LIBPATH, pathsep, LIBPATH_base) else env[LIBPATH_env] = LIBPATH end end if adjust_PATH && (LIBPATH_env != "PATH" || !adjust_LIBPATH) if adjust_PATH if !isempty(get(env, "PATH", "")) env["PATH"] = string(PATH, pathsep, env["PATH"]) else env["PATH"] = PATH end end end return env end function p7zip(f::Function; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH) withenv(env...) do return f(p7zip_path) end end function p7zip(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH) return Cmd(Cmd([p7zip_path]); env) end function init_p7zip_path() # Prefer our own bundled p7zip, but if we don't have one, pick it up off of the PATH # If this is an in-tree build, `7z` will live in `bindir`. Otherwise, it'll be in `private_libexecdir` for bundled_p7zip_path in (joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, p7zip_exe), joinpath(Sys.BINDIR, p7zip_exe)) if isfile(bundled_p7zip_path) global p7zip_path = abspath(bundled_p7zip_path) return end end global p7zip_path = something(Sys.which(p7zip_exe), p7zip_exe) end function __init__() global artifact_dir = dirname(Sys.BINDIR) init_p7zip_path() PATH[] = dirname(p7zip_path) push!(PATH_list, PATH[]) append!(LIBPATH_list, [joinpath(Sys.BINDIR, Base.LIBDIR, "julia"), joinpath(Sys.BINDIR, Base.LIBDIR)]) LIBPATH[] = join(LIBPATH_list, pathsep) end # JLLWrappers API compatibility shims. Note that not all of these will really make sense. # For instance, `find_artifact_dir()` won't actually be the artifact directory, because # there isn't one. It instead returns the overall Julia prefix. is_available() = true find_artifact_dir() = artifact_dir dev_jll() = error("stdlib JLLs cannot be dev'ed") best_wrapper = nothing end # module p7zip_jll o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Versions.jlใ4������# This file is a part of Julia. License is MIT: https://julialang.org/license module Versions export VersionBound, VersionRange, VersionSpec, semver_spec, isjoinable ################ # VersionBound # ################ struct VersionBound t::NTuple{3,UInt32} n::Int function VersionBound(tin::NTuple{n,Integer}) where n n <= 3 || throw(ArgumentError("VersionBound: you can only specify major, minor and patch versions")) n == 0 && return new((0, 0, 0), n) n == 1 && return new((tin[1], 0, 0), n) n == 2 && return new((tin[1], tin[2], 0), n) n == 3 && return new((tin[1], tin[2], tin[3]), n) error("invalid $n") end end VersionBound(t::Integer...) = VersionBound(t) VersionBound(v::VersionNumber) = VersionBound(v.major, v.minor, v.patch) Base.getindex(b::VersionBound, i::Int) = b.t[i] function โ‰ฒ(v::VersionNumber, b::VersionBound) b.n == 0 && return true b.n == 1 && return v.major <= b[1] b.n == 2 && return (v.major, v.minor) <= (b[1], b[2]) return (v.major, v.minor, v.patch) <= (b[1], b[2], b[3]) end function โ‰ฒ(b::VersionBound, v::VersionNumber) b.n == 0 && return true b.n == 1 && return v.major >= b[1] b.n == 2 && return (v.major, v.minor) >= (b[1], b[2]) return (v.major, v.minor, v.patch) >= (b[1], b[2], b[3]) end function isless_ll(a::VersionBound, b::VersionBound) m, n = a.n, b.n for i = 1:min(m, n) a[i] < b[i] && return true a[i] > b[i] && return false end return m < n end stricterlower(a::VersionBound, b::VersionBound) = isless_ll(a, b) ? b : a # Comparison between two upper bounds function isless_uu(a::VersionBound, b::VersionBound) m, n = a.n, b.n for i = 1:min(m, n) a[i] < b[i] && return true a[i] > b[i] && return false end return m > n end stricterupper(a::VersionBound, b::VersionBound) = isless_uu(a, b) ? a : b # `isjoinable` compares an upper bound of a range with the lower bound of the next range # to determine if they can be joined, as in [1.5-2.8, 2.5-3] -> [1.5-3]. Used by `union!`. # The equal-length-bounds case is special since e.g. `1.5` can be joined with `1.6`, # `2.3.4` can be joined with `2.3.5` etc. function isjoinable(up::VersionBound, lo::VersionBound) up.n == 0 && lo.n == 0 && return true if up.n == lo.n n = up.n for i = 1:(n - 1) up[i] > lo[i] && return true up[i] < lo[i] && return false end up[n] < lo[n] - 1 && return false return true else l = min(up.n, lo.n) for i = 1:l up[i] > lo[i] && return true up[i] < lo[i] && return false end end return true end Base.hash(r::VersionBound, h::UInt) = hash(r.t, hash(r.n, h)) # Hot code function VersionBound(s::AbstractString) s = strip(s) s == "*" && return VersionBound() first(s) == 'v' && (s = SubString(s, 2)) l = lastindex(s) p = findnext('.', s, 1) b = p === nothing ? l : (p-1) i = parse(Int64, SubString(s, 1, b)) p === nothing && return VersionBound(i) a = p+1 p = findnext('.', s, a) b = p === nothing ? l : (p-1) j = parse(Int64, SubString(s, a, b)) p === nothing && return VersionBound(i, j) a = p+1 p = findnext('.', s, a) b = p === nothing ? l : (p-1) k = parse(Int64, SubString(s, a, b)) p === nothing && return VersionBound(i, j, k) error("invalid VersionBound string $(repr(s))") end ################ # VersionRange # ################ struct VersionRange lower::VersionBound upper::VersionBound # NOTE: ranges are allowed to be empty; they are ignored by VersionSpec anyway function VersionRange(lo::VersionBound, hi::VersionBound) # lo.t == hi.t implies that digits past min(lo.n, hi.n) are zero # lo.n < hi.n example: 1.2-1.2.0 => 1.2.0 # lo.n > hi.n example: 1.2.0-1.2 => 1.2 lo.t == hi.t && (lo = hi) return new(lo, hi) end end VersionRange(b::VersionBound=VersionBound()) = VersionRange(b, b) VersionRange(t::Integer...) = VersionRange(VersionBound(t...)) VersionRange(v::VersionNumber) = VersionRange(VersionBound(v)) VersionRange(lo::VersionNumber, hi::VersionNumber) = VersionRange(VersionBound(lo), VersionBound(hi)) # The vast majority of VersionRanges are in practice equal to "1" const VersionRange_1 = VersionRange(VersionBound("1"), VersionBound("1")) function VersionRange(s::AbstractString) s == "1" && return VersionRange_1 p = split(s, "-") if length(p) != 1 && length(p) != 2 throw(ArgumentError("invalid version range: $(repr(s))")) end lower = VersionBound(p[1]) upper = length(p) == 1 ? lower : VersionBound(p[2]) return VersionRange(lower, upper) end function Base.isempty(r::VersionRange) for i = 1:min(r.lower.n, r.upper.n) r.lower[i] > r.upper[i] && return true r.lower[i] < r.upper[i] && return false end return false end function Base.print(io::IO, r::VersionRange) m, n = r.lower.n, r.upper.n if (m, n) == (0, 0) print(io, '*') elseif m == 0 print(io, "0-") join(io, r.upper.t, '.') elseif n == 0 join(io, r.lower.t, '.') print(io, "-*") else join(io, r.lower.t[1:m], '.') if r.lower != r.upper print(io, '-') join(io, r.upper.t[1:n], '.') end end end Base.show(io::IO, r::VersionRange) = print(io, "VersionRange(\"", r, "\")") Base.in(v::VersionNumber, r::VersionRange) = r.lower โ‰ฒ v โ‰ฒ r.upper Base.intersect(a::VersionRange, b::VersionRange) = VersionRange(stricterlower(a.lower, b.lower), stricterupper(a.upper, b.upper)) function Base.union!(ranges::Vector{<:VersionRange}) l = length(ranges) l == 0 && return ranges sort!(ranges, lt=(a, b) -> (isless_ll(a.lower, b.lower) || (a.lower == b.lower && isless_uu(a.upper, b.upper)))) k0 = 1 ks = findfirst(!isempty, ranges) ks === nothing && return empty!(ranges) lo, up, k0 = ranges[ks].lower, ranges[ks].upper, 1 for k = (ks + 1):l isempty(ranges[k]) && continue lo1, up1 = ranges[k].lower, ranges[k].upper if isjoinable(up, lo1) isless_uu(up, up1) && (up = up1) continue end vr = VersionRange(lo, up) @assert !isempty(vr) ranges[k0] = vr k0 += 1 lo, up = lo1, up1 end vr = VersionRange(lo, up) if !isempty(vr) ranges[k0] = vr k0 += 1 end resize!(ranges, k0 - 1) return ranges end ############### # VersionSpec # ############### struct VersionSpec ranges::Vector{VersionRange} VersionSpec(r::Vector{<:VersionRange}) = new(union!(r)) VersionSpec(vs::VersionSpec) = vs end VersionSpec(r::VersionRange) = VersionSpec(VersionRange[r]) VersionSpec(v::VersionNumber) = VersionSpec(VersionRange(v)) const _all_versionsspec = VersionSpec(VersionRange()) VersionSpec() = _all_versionsspec VersionSpec(s::AbstractString) = VersionSpec(VersionRange(s)) VersionSpec(v::AbstractVector) = VersionSpec(map(VersionRange, v)) # Hot code function Base.in(v::VersionNumber, s::VersionSpec) for r in s.ranges v in r && return true end return false end Base.copy(vs::VersionSpec) = VersionSpec(vs) const empty_versionspec = VersionSpec(VersionRange[]) const _empty_symbol = "โˆ…" Base.isempty(s::VersionSpec) = all(isempty, s.ranges) @assert isempty(empty_versionspec) # Hot code, measure performance before changing function Base.intersect(A::VersionSpec, B::VersionSpec) (isempty(A) || isempty(B)) && return copy(empty_versionspec) ranges = Vector{VersionRange}(undef, length(A.ranges) * length(B.ranges)) i = 1 @inbounds for a in A.ranges, b in B.ranges ranges[i] = intersect(a, b) i += 1 end VersionSpec(ranges) end Base.intersect(a::VersionNumber, B::VersionSpec) = a in B ? VersionSpec(a) : empty_versionspec Base.intersect(A::VersionSpec, b::VersionNumber) = intersect(b, A) function Base.union(A::VersionSpec, B::VersionSpec) A == B && return A Ar = copy(A.ranges) append!(Ar, B.ranges) union!(Ar) return VersionSpec(Ar) end Base.:(==)(A::VersionSpec, B::VersionSpec) = A.ranges == B.ranges Base.hash(s::VersionSpec, h::UInt) = hash(s.ranges, h + (0x2fd2ca6efa023f44 % UInt)) function Base.print(io::IO, s::VersionSpec) isempty(s) && return print(io, _empty_symbol) length(s.ranges) == 1 && return print(io, s.ranges[1]) print(io, '[') for i = 1:length(s.ranges) 1 < i && print(io, ", ") print(io, s.ranges[i]) end print(io, ']') end Base.show(io::IO, s::VersionSpec) = print(io, "VersionSpec(\"", s, "\")") ################### # Semver notation # ################### function semver_spec(s::String; throw = true) ranges = VersionRange[] for ver in strip.(split(strip(s), ',')) range = nothing found_match = false for (ver_reg, f) in ver_regs if occursin(ver_reg, ver) range = f(match(ver_reg, ver)) found_match = true break end end if !found_match if throw error("invalid version specifier: \"$s\"") else return nothing end end push!(ranges, range) end return VersionSpec(ranges) end function semver_interval(m::RegexMatch) @assert length(m.captures) == 4 n_significant = count(x -> x !== nothing, m.captures) - 1 typ, _major, _minor, _patch = m.captures major = parse(Int, _major) minor = (n_significant < 2) ? 0 : parse(Int, _minor) patch = (n_significant < 3) ? 0 : parse(Int, _patch) if n_significant == 3 && major == 0 && minor == 0 && patch == 0 error("invalid version: \"0.0.0\"") end # Default type is :caret vertyp = (typ == "" || typ == "^") ? :caret : :tilde v0 = VersionBound((major, minor, patch)) if vertyp === :caret if major != 0 return VersionRange(v0, VersionBound((v0[1],))) elseif minor != 0 return VersionRange(v0, VersionBound((v0[1], v0[2]))) else if n_significant == 1 return VersionRange(v0, VersionBound((0,))) elseif n_significant == 2 return VersionRange(v0, VersionBound((0, 0,))) else return VersionRange(v0, VersionBound((0, 0, v0[3]))) end end else if n_significant == 3 || n_significant == 2 return VersionRange(v0, VersionBound((v0[1], v0[2],))) else return VersionRange(v0, VersionBound((v0[1],))) end end end const _inf = VersionBound("*") function inequality_interval(m::RegexMatch) @assert length(m.captures) == 4 typ, _major, _minor, _patch = m.captures n_significant = count(x -> x !== nothing, m.captures) - 1 major = parse(Int, _major) minor = (n_significant < 2) ? 0 : parse(Int, _minor) patch = (n_significant < 3) ? 0 : parse(Int, _patch) if n_significant == 3 && major == 0 && minor == 0 && patch == 0 error("invalid version: 0.0.0") end v = VersionBound(major, minor, patch) if occursin(r"^<\s*$", typ) nil = VersionBound(0, 0, 0) if v[3] == 0 if v[2] == 0 v1 = VersionBound(v[1]-1) else v1 = VersionBound(v[1], v[2]-1) end else v1 = VersionBound(v[1], v[2], v[3]-1) end return VersionRange(nil, v1) elseif occursin(r"^=\s*$", typ) return VersionRange(v) elseif occursin(r"^>=\s*$", typ) || occursin(r"^โ‰ฅ\s*$", typ) return VersionRange(v, _inf) else error("invalid prefix $typ") end end function hyphen_interval(m::RegexMatch) @assert length(m.captures) == 6 _lower_major, _lower_minor, _lower_patch, _upper_major, _upper_minor, _upper_patch = m.captures if isnothing(_lower_minor) lower_bound = VersionBound(parse(Int, _lower_major)) elseif isnothing(_lower_patch) lower_bound = VersionBound(parse(Int, _lower_major), parse(Int, _lower_minor)) else lower_bound = VersionBound(parse(Int, _lower_major), parse(Int, _lower_minor), parse(Int, _lower_patch)) end if isnothing(_upper_minor) upper_bound = VersionBound(parse(Int, _upper_major)) elseif isnothing(_upper_patch) upper_bound = VersionBound(parse(Int, _upper_major), parse(Int, _upper_minor)) else upper_bound = VersionBound(parse(Int, _upper_major), parse(Int, _upper_minor), parse(Int, _upper_patch)) end return VersionRange(lower_bound, upper_bound) end const version = "v?([0-9]+?)(?:\\.([0-9]+?))?(?:\\.([0-9]+?))?" const ver_regs = Pair{Regex,Any}[ Regex("^([~^]?)?$version\$") => semver_interval, # 0.5 ^0.4 ~0.3.2 Regex("^((?:โ‰ฅ\\s*)|(?:>=\\s*)|(?:=\\s*)|(?:<\\s*)|(?:=\\s*))v?$version\$") => inequality_interval,# < 0.2 >= 0.5,2 Regex("^[\\s]*$version[\\s]*?\\s-\\s[\\s]*?$version[\\s]*\$") => hyphen_interval, # 0.7 - 1.3 ] end x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Registry/Registry.jlc������module Registry import ..Pkg using ..Pkg: depots1, printpkgstyle, stderr_f, isdir_nothrow, pathrepr, pkg_server, GitTools using ..Pkg.PlatformEngines: download_verify_unpack, download, download_verify, exe7z, verify_archive_tree_hash using UUIDs, LibGit2, TOML, Dates import FileWatching include("registry_instance.jl") mutable struct RegistrySpec name::Union{String,Nothing} uuid::Union{UUID,Nothing} url::Union{String,Nothing} # the path field can be a local source when adding a registry # otherwise it is the path where the registry is installed path::Union{String,Nothing} linked::Union{Bool,Nothing} end RegistrySpec(name::String) = RegistrySpec(name = name) RegistrySpec(;name::Union{String,Nothing}=nothing, uuid::Union{String,UUID,Nothing}=nothing, url::Union{String,Nothing}=nothing, path::Union{String,Nothing}=nothing, linked::Union{Bool,Nothing}=nothing) = RegistrySpec(name, isa(uuid, String) ? UUID(uuid) : uuid, url, path, linked) """ Pkg.Registry.add(registry::RegistrySpec) Add new package registries. The no-argument `Pkg.Registry.add()` will install the default registries. # Examples ```julia Pkg.Registry.add("General") Pkg.Registry.add(RegistrySpec(uuid = "23338594-aafe-5451-b93e-139f81909106")) Pkg.Registry.add(RegistrySpec(url = "https://github.com/JuliaRegistries/General.git")) ``` """ add(reg::Union{String,RegistrySpec}; kwargs...) = add([reg]; kwargs...) add(regs::Vector{String}; kwargs...) = add(RegistrySpec[RegistrySpec(name = name) for name in regs]; kwargs...) add(; kwargs...) = add(RegistrySpec[]; kwargs...) function add(regs::Vector{RegistrySpec}; io::IO=stderr_f(), depot=depots1()) if isempty(regs) download_default_registries(io, only_if_empty = false; depot) else download_registries(io, regs, depot) end end const DEFAULT_REGISTRIES = RegistrySpec[RegistrySpec(name = "General", uuid = UUID("23338594-aafe-5451-b93e-139f81909106"), url = "https://github.com/JuliaRegistries/General.git")] function pkg_server_registry_info() registry_info = Dict{UUID, Base.SHA1}() server = pkg_server() server === nothing && return nothing tmp_path = tempname() download_ok = false try f = retry(delays = fill(1.0, 3)) do download("$server/registries", tmp_path, verbose=false) end f() download_ok = true catch err @warn "could not download $server/registries" exception=err end download_ok || return nothing open(tmp_path) do io for line in eachline(io) if (m = match(r"^/registry/([^/]+)/([^/]+)$", line)) !== nothing uuid = UUID(m.captures[1]::SubString{String}) hash = Base.SHA1(m.captures[2]::SubString{String}) registry_info[uuid] = hash end end end Base.rm(tmp_path, force=true) return server, registry_info end function pkg_server_registry_urls() server_registry_info = pkg_server_registry_info() registry_urls = Dict{UUID, String}() server_registry_info === nothing && return registry_urls server, registry_info = server_registry_info for (uuid, hash) in registry_info registry_urls[uuid] = "$server/registry/$uuid/$hash" end return registry_urls end pkg_server_url_hash(url::String) = Base.SHA1(split(url, '/')[end]) function download_default_registries(io::IO; only_if_empty::Bool = true, depot=depots1()) installed_registries = reachable_registries() # Only clone if there are no installed registries, unless called # with false keyword argument. if isempty(installed_registries) || !only_if_empty printpkgstyle(io, :Installing, "known registries into $(pathrepr(depot))") registries = copy(DEFAULT_REGISTRIES) for uuid in keys(pkg_server_registry_urls()) if !(uuid in (reg.uuid for reg in registries)) push!(registries, RegistrySpec(uuid = uuid)) end end filter!(reg -> !(reg.uuid in installed_registries), registries) download_registries(io, registries, depot) return true end return false end # Hacky way to make e.g. `registry add General` work. function populate_known_registries_with_urls!(registries::Vector{RegistrySpec}) known_registries = DEFAULT_REGISTRIES # TODO: Some way to add stuff here? for reg in registries, known in known_registries if reg.uuid !== nothing if reg.uuid === known.uuid reg.url = known.url reg.path = known.path reg.linked = known.linked end elseif reg.name !== nothing if reg.name == known.name named_regs = filter(r -> r.name == reg.name, known_registries) if !all(r -> r.uuid == first(named_regs).uuid, named_regs) Pkg.Types.pkgerror("multiple registries with name `$(reg.name)`, please specify with uuid.") end reg.uuid = known.uuid reg.url = known.url reg.path = known.path reg.linked = known.linked end end end end function registry_use_pkg_server() get(ENV, "JULIA_PKG_SERVER", nothing) !== "" end registry_read_from_tarball() = registry_use_pkg_server() && !Base.get_bool_env("JULIA_PKG_UNPACK_REGISTRY", false) function check_registry_state(reg) reg_currently_uses_pkg_server = reg.tree_info !== nothing reg_should_use_pkg_server = registry_use_pkg_server() if reg_currently_uses_pkg_server && !reg_should_use_pkg_server msg = string( "Your registry may be outdated. We recommend that you run the ", "following command: ", "using Pkg; Pkg.Registry.rm(\"$(reg.name)\"); Pkg.Registry.add(\"$(reg.name)\")", ) @warn(msg) end return nothing end function download_registries(io::IO, regs::Vector{RegistrySpec}, depot::String=depots1()) populate_known_registries_with_urls!(regs) regdir = joinpath(depot, "registries") isdir(regdir) || mkpath(regdir) # only allow one julia process to download and install registries at a time FileWatching.mkpidlock(joinpath(regdir, ".pid"), stale_age = 10) do registry_urls = pkg_server_registry_urls() for reg in regs if reg.path !== nothing && reg.url !== nothing Pkg.Types.pkgerror(""" ambiguous registry specification; both `url` and `path` are set: url=\"$(reg.url)\" path=\"$(reg.path)\" """ ) end url = get(registry_urls, reg.uuid, nothing) if url !== nothing && registry_read_from_tarball() tmp = tempname() try download_verify(url, nothing, tmp) catch err Pkg.Types.pkgerror("could not download $url \nException: $(sprint(showerror, err))") end _hash = pkg_server_url_hash(url) if !verify_archive_tree_hash(tmp, _hash) Pkg.Types.pkgerror("unable to verify download from $url") end if reg.name === nothing # Need to look up the registry name here reg_unc = uncompress_registry(tmp) reg.name = TOML.parse(reg_unc["Registry.toml"])["name"]::String end mv(tmp, joinpath(regdir, reg.name * ".tar.gz"); force=true) reg_info = Dict("uuid" => string(reg.uuid), "git-tree-sha1" => string(_hash), "path" => reg.name * ".tar.gz") open(joinpath(regdir, reg.name * ".toml"), "w") do io TOML.print(io, reg_info) end else mktempdir() do tmp if reg.path !== nothing && reg.linked == true # symlink to local source registry = Registry.RegistryInstance(reg.path) regpath = joinpath(regdir, registry.name) printpkgstyle(io, :Symlinking, "registry from `$(Base.contractuser(reg.path))`") isdir(dirname(regpath)) || mkpath(dirname(regpath)) symlink(reg.path, regpath) isfile(joinpath(regpath, "Registry.toml")) || Pkg.Types.pkgerror("no `Registry.toml` file in linked registry.") registry = Registry.RegistryInstance(regpath) printpkgstyle(io, :Symlinked, "registry `$(Base.contractuser(registry.name))` to `$(Base.contractuser(regpath))`") return elseif reg.url !== nothing && reg.linked == true pkgerror(""" A symlinked registry was requested but `path` was not set and `url` was set to `$url`. Set only `path` and `linked = true` to use registry symlinking. """) elseif url !== nothing && registry_use_pkg_server() # download from Pkg server try download_verify_unpack(url, nothing, tmp, ignore_existence = true, io = io) catch err Pkg.Types.pkgerror("could not download $url \nException: $(sprint(showerror, err))") end tree_info_file = joinpath(tmp, ".tree_info.toml") hash = pkg_server_url_hash(url) write(tree_info_file, "git-tree-sha1 = " * repr(string(hash))) elseif reg.path !== nothing # copy from local source printpkgstyle(io, :Copying, "registry from `$(Base.contractuser(reg.path))`") isfile(joinpath(reg.path, "Registry.toml")) || Pkg.Types.pkgerror("no `Registry.toml` file in source directory.") registry = Registry.RegistryInstance(reg.path) regpath = joinpath(regdir, registry.name) cp(reg.path, regpath; force=true) # has to be cp given we're copying printpkgstyle(io, :Copied, "registry `$(Base.contractuser(registry.name))` to `$(Base.contractuser(regpath))`") return elseif reg.url !== nothing # clone from url # retry to help spurious connection issues, particularly on CI repo = retry(GitTools.clone, delays = fill(1.0, 5), check=(s,e)->isa(e, LibGit2.GitError))(io, reg.url, tmp; header = "registry from $(repr(reg.url))") LibGit2.close(repo) else Pkg.Types.pkgerror("no path or url specified for registry") end # verify that the clone looks like a registry if !isfile(joinpath(tmp, "Registry.toml")) Pkg.Types.pkgerror("no `Registry.toml` file in cloned registry.") end registry = Registry.RegistryInstance(tmp) regpath = joinpath(regdir, registry.name) # copy to `depot` ispath(dirname(regpath)) || mkpath(dirname(regpath)) if isfile(joinpath(regpath, "Registry.toml")) existing_registry = Registry.RegistryInstance(regpath) if registry.uuid == existing_registry.uuid println(io, "Registry `$(registry.name)` already exists in `$(Base.contractuser(regpath))`.") else throw(Pkg.Types.PkgError("registry `$(registry.name)=\"$(registry.uuid)\"` conflicts with " * "existing registry `$(existing_registry.name)=\"$(existing_registry.uuid)\"`. " * "To install it you can clone it manually into e.g. " * "`$(Base.contractuser(joinpath(regdir, registry.name*"-2")))`.")) end elseif (url !== nothing && registry_use_pkg_server()) || reg.linked !== true # if the dir doesn't exist, or exists but doesn't contain a Registry.toml mv(tmp, regpath, force=true) printpkgstyle(io, :Added, "registry `$(registry.name)` to `$(Base.contractuser(regpath))`") end end end end end # mkpidlock return nothing end """ Pkg.Registry.rm(registry::String) Pkg.Registry.rm(registry::RegistrySpec) Remove registries. # Examples ```julia Pkg.Registry.rm("General") Pkg.Registry.rm(RegistrySpec(uuid = "23338594-aafe-5451-b93e-139f81909106")) ``` """ rm(reg::Union{String,RegistrySpec}; kwargs...) = rm([reg]; kwargs...) rm(regs::Vector{String}; kwargs...) = rm([RegistrySpec(name = name) for name in regs]; kwargs...) function rm(regs::Vector{RegistrySpec}; io::IO=stderr_f()) for registry in find_installed_registries(io, regs; depots=first(Base.DEPOT_PATH)) printpkgstyle(io, :Removing, "registry `$(registry.name)` from $(Base.contractuser(registry.path))") if isfile(registry.path) d = TOML.parsefile(registry.path) if haskey(d, "path") Base.rm(joinpath(dirname(registry.path), d["path"]); force=true) end end Base.rm(registry.path; force=true, recursive=true) end return nothing end # Search for the input registries among installed ones function find_installed_registries(io::IO, needles::Union{Vector{Registry.RegistryInstance}, Vector{RegistrySpec}}; depots=Base.DEPOT_PATH) haystack = reachable_registries(; depots) output = Registry.RegistryInstance[] for needle in needles if needle.name === nothing && needle.uuid === nothing Pkg.Types.pkgerror("no name or uuid specified for registry.") end found = false for candidate in haystack if needle.uuid !== nothing if needle.uuid == candidate.uuid push!(output, candidate) found = true end elseif needle.name !== nothing if needle.name == candidate.name named_regs = filter(r -> r.name == needle.name, haystack) if !all(r -> r.uuid == first(named_regs).uuid, named_regs) Pkg.Types.pkgerror("multiple registries with name `$(needle.name)`, please specify with uuid.") end push!(output, candidate) found = true end end end if !found println(io, "registry `$(needle.name === nothing ? needle.uuid : needle.uuid === nothing ? needle.name : "$(needle.name)=$(needle.uuid)")` not found.") end end return output end function get_registry_update_log() pkg_scratch_space = joinpath(DEPOT_PATH[1], "scratchspaces", "44cfe95a-1eb2-52ea-b672-e2afdf69b78f") pkg_reg_updated_file = joinpath(pkg_scratch_space, "registry_updates.toml") updated_registry_d = isfile(pkg_reg_updated_file) ? TOML.parsefile(pkg_reg_updated_file) : Dict{String, Any}() return updated_registry_d end function save_registry_update_log(d::Dict) pkg_scratch_space = joinpath(DEPOT_PATH[1], "scratchspaces", "44cfe95a-1eb2-52ea-b672-e2afdf69b78f") mkpath(pkg_scratch_space) pkg_reg_updated_file = joinpath(pkg_scratch_space, "registry_updates.toml") open(pkg_reg_updated_file, "w") do io TOML.print(io, d) end end """ Pkg.Registry.update() Pkg.Registry.update(registry::RegistrySpec) Pkg.Registry.update(registry::Vector{RegistrySpec}) Update registries. If no registries are given, update all available registries. # Examples ```julia Pkg.Registry.update() Pkg.Registry.update("General") Pkg.Registry.update(RegistrySpec(uuid = "23338594-aafe-5451-b93e-139f81909106")) ``` """ update(reg::Union{String,RegistrySpec}; kwargs...) = update([reg]; kwargs...) update(regs::Vector{String}; kwargs...) = update([RegistrySpec(name = name) for name in regs]; kwargs...) function update(regs::Vector{RegistrySpec} = RegistrySpec[]; io::IO=stderr_f(), force::Bool=true, depots = [depots1()], update_cooldown = Second(1)) registry_update_log = get_registry_update_log() for depot in depots depot_regs = isempty(regs) ? reachable_registries(; depots=depot) : regs regdir = joinpath(depot, "registries") isdir(regdir) || mkpath(regdir) # only allow one julia process to update registries in this depot at a time FileWatching.mkpidlock(joinpath(regdir, ".pid"), stale_age = 10) do errors = Tuple{String, String}[] registry_urls = pkg_server_registry_urls() for reg in unique(r -> r.uuid, find_installed_registries(io, depot_regs; depots=[depot]); seen=Set{UUID}()) prev_update = get(registry_update_log, string(reg.uuid), nothing)::Union{Nothing, DateTime} if prev_update !== nothing diff = now() - prev_update if diff < update_cooldown @debug "Skipping updating registry $(reg.name) since it is on cooldown: $(Dates.canonicalize(Millisecond(update_cooldown) - diff)) left" continue end end let reg=reg, errors=errors regpath = pathrepr(reg.path) let regpath=regpath if reg.tree_info !== nothing printpkgstyle(io, :Updating, "registry at " * regpath) old_hash = reg.tree_info url = get(registry_urls, reg.uuid, nothing) if url !== nothing check_registry_state(reg) end if url !== nothing && (new_hash = pkg_server_url_hash(url)) != old_hash # TODO: update faster by using a diff, if available # TODO: DRY with the code in `download_default_registries` let new_hash = new_hash, url = url if registry_read_from_tarball() tmp = tempname() try download_verify(url, nothing, tmp) catch err push!(errors, (reg.path, "failed to download from $(url). Exception: $(sprint(showerror, err))")) @goto done_tarball_read end hash = pkg_server_url_hash(url) if !verify_archive_tree_hash(tmp, hash) push!(errors, (reg.path, "failed to verify download from $(url)")) @goto done_tarball_read end # If we have an uncompressed Pkg server registry, remove it and get the compressed version if isdir(reg.path) Base.rm(reg.path; recursive=true, force=true) end registry_path = dirname(reg.path) mv(tmp, joinpath(registry_path, reg.name * ".tar.gz"); force=true) reg_info = Dict("uuid" => string(reg.uuid), "git-tree-sha1" => string(hash), "path" => reg.name * ".tar.gz") open(joinpath(registry_path, reg.name * ".toml"), "w") do io TOML.print(io, reg_info) end registry_update_log[string(reg.uuid)] = now() @label done_tarball_read else mktempdir() do tmp try download_verify_unpack(url, nothing, tmp, ignore_existence = true, io=io) registry_update_log[string(reg.uuid)] = now() catch err push!(errors, (reg.path, "failed to download and unpack from $(url). Exception: $(sprint(showerror, err))")) @goto done_tarball_unpack end tree_info_file = joinpath(tmp, ".tree_info.toml") write(tree_info_file, "git-tree-sha1 = " * repr(string(new_hash))) mv(tmp, reg.path, force=true) @label done_tarball_unpack end end end end elseif isdir(joinpath(reg.path, ".git")) printpkgstyle(io, :Updating, "registry at " * regpath) LibGit2.with(LibGit2.GitRepo(reg.path)) do repo if LibGit2.isdirty(repo) push!(errors, (regpath, "registry dirty")) @goto done_git end if !LibGit2.isattached(repo) push!(errors, (regpath, "registry detached")) @goto done_git end if !("origin" in LibGit2.remotes(repo)) push!(errors, (regpath, "origin not in the list of remotes")) @goto done_git end branch = LibGit2.headname(repo) try GitTools.fetch(io, repo; refspecs=["+refs/heads/$branch:refs/remotes/origin/$branch"]) catch e e isa Pkg.Types.PkgError || rethrow() push!(errors, (reg.path, "failed to fetch from repo: $(e.msg)")) @goto done_git end attempts = 0 @label merge ff_succeeded = try LibGit2.merge!(repo; branch="refs/remotes/origin/$branch", fastforward=true) catch e attempts += 1 if e isa LibGit2.GitError && e.code == LibGit2.Error.ELOCKED && attempts <= 3 @warn "Registry update attempt failed because repository is locked. Resetting and retrying." e LibGit2.reset!(repo, LibGit2.head_oid(repo), LibGit2.Consts.RESET_HARD) sleep(1) @goto merge elseif e isa LibGit2.GitError && e.code == LibGit2.Error.ENOTFOUND push!(errors, (reg.path, "branch origin/$branch not found")) @goto done_git else rethrow() end end if !ff_succeeded try LibGit2.rebase!(repo, "origin/$branch") catch e e isa LibGit2.GitError || rethrow() push!(errors, (reg.path, "registry failed to rebase on origin/$branch")) @goto done_git end end registry_update_log[string(reg.uuid)] = now() @label done_git end end end end end if !isempty(errors) warn_str = "Some registries failed to update:" for (reg, err) in errors warn_str *= "\n โ€” $reg โ€” $err" end @error warn_str end end # mkpidlock end save_registry_update_log(registry_update_log) return end """ Pkg.Registry.status() Display information about available registries. # Examples ```julia Pkg.Registry.status() ``` """ function status(io::IO=stderr_f()) regs = reachable_registries() regs = unique(r -> r.uuid, regs; seen=Set{Union{UUID,Nothing}}()) printpkgstyle(io, Symbol("Registry Status"), "") if isempty(regs) println(io, " (no registries found)") else for reg in regs printstyled(io, " [$(string(reg.uuid)[1:8])]"; color = :light_black) print(io, " $(reg.name)") reg.repo === nothing || print(io, " ($(reg.repo))") println(io) end end end end # module ���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Registry/registry_instance.jlhA������using Base: UUID, SHA1 using TOML using Tar using ..Versions: VersionSpec, VersionRange # The content of a registry is assumed to be constant during the # lifetime of a `Registry`. Create a new `Registry` if you want to have # a new view on the current registry. function to_tar_path_format(file::AbstractString) @static if Sys.iswindows() file = replace(file, "\\" => "/") end return file end # See loading.jl const TOML_CACHE = Base.TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}()) const TOML_LOCK = ReentrantLock() _parsefile(toml_file::AbstractString) = Base.parsed_toml(toml_file, TOML_CACHE, TOML_LOCK) function parsefile(in_memory_registry::Union{Dict, Nothing}, folder::AbstractString, file::AbstractString) if in_memory_registry === nothing return _parsefile(joinpath(folder, file)) else content = in_memory_registry[to_tar_path_format(file)] return TOML.Internals.parse(TOML.Parser(content; filepath=file)) end end custom_isfile(in_memory_registry::Union{Dict, Nothing}, folder::AbstractString, file::AbstractString) = in_memory_registry === nothing ? isfile(joinpath(folder, file)) : haskey(in_memory_registry, to_tar_path_format(file)) # Info about each version of a package mutable struct VersionInfo const git_tree_sha1::Base.SHA1 const yanked::Bool uncompressed_compat::Dict{UUID, VersionSpec} # lazily initialized weak_uncompressed_compat::Dict{UUID, VersionSpec} # lazily initialized VersionInfo(git_tree_sha1::Base.SHA1, yanked::Bool) = new(git_tree_sha1, yanked) end # This is the information that exists in e.g. General/A/ACME struct PkgInfo # Package.toml: repo::Union{String, Nothing} subdir::Union{String, Nothing} # Versions.toml: version_info::Dict{VersionNumber, VersionInfo} # Compat.toml compat::Dict{VersionRange, Dict{String, VersionSpec}} # Deps.toml deps::Dict{VersionRange, Dict{String, UUID}} # WeakCompat.toml weak_compat::Dict{VersionRange, Dict{String, VersionSpec}} # WeakDeps.toml weak_deps::Dict{VersionRange, Dict{String, UUID}} end isyanked(pkg::PkgInfo, v::VersionNumber) = pkg.version_info[v].yanked treehash(pkg::PkgInfo, v::VersionNumber) = pkg.version_info[v].git_tree_sha1 function uncompress(compressed::Dict{VersionRange, Dict{String, T}}, vsorted::Vector{VersionNumber}) where {T} @assert issorted(vsorted) uncompressed = Dict{VersionNumber, Dict{String, T}}() for v in vsorted uncompressed[v] = Dict{String, T}() end for (vs, data) in compressed first = length(vsorted) + 1 # We find the first and last version that are in the range # and since the versions are sorted, all versions in between are sorted for i in eachindex(vsorted) v = vsorted[i] v in vs && (first = i; break) end last = 0 for i in reverse(eachindex(vsorted)) v = vsorted[i] v in vs && (last = i; break) end for i in first:last v = vsorted[i] uv = uncompressed[v] for (key, value) in data if haskey(uv, key) error("Overlapping ranges for $(key) for version $v in registry.") else uv[key] = value end end end end return uncompressed end const JULIA_UUID = UUID("1222c4b2-2114-5bfd-aeef-88e4692bbb3e") function initialize_uncompressed!(pkg::PkgInfo, versions = keys(pkg.version_info)) # Only valid to call this with existing versions of the package # Remove all versions we have already uncompressed versions = filter!(v -> !isdefined(pkg.version_info[v], :uncompressed_compat), collect(versions)) sort!(versions) uncompressed_compat = uncompress(pkg.compat, versions) uncompressed_deps = uncompress(pkg.deps, versions) for v in versions vinfo = pkg.version_info[v] compat = Dict{UUID, VersionSpec}() uncompressed_deps_v = uncompressed_deps[v] # Everything depends on Julia uncompressed_deps_v["julia"] = JULIA_UUID uncompressed_compat_v = uncompressed_compat[v] for (pkg, uuid) in uncompressed_deps_v vspec = get(uncompressed_compat_v, pkg, nothing) compat[uuid] = vspec === nothing ? VersionSpec() : vspec end @assert !isdefined(vinfo, :uncompressed_compat) vinfo.uncompressed_compat = compat end return pkg end function initialize_weak_uncompressed!(pkg::PkgInfo, versions = keys(pkg.version_info)) # Only valid to call this with existing versions of the package # Remove all versions we have already uncompressed versions = filter!(v -> !isdefined(pkg.version_info[v], :weak_uncompressed_compat), collect(versions)) sort!(versions) weak_uncompressed_compat = uncompress(pkg.weak_compat, versions) weak_uncompressed_deps = uncompress(pkg.weak_deps, versions) for v in versions vinfo = pkg.version_info[v] weak_compat = Dict{UUID, VersionSpec}() weak_uncompressed_deps_v = weak_uncompressed_deps[v] weak_uncompressed_compat_v = weak_uncompressed_compat[v] for (pkg, uuid) in weak_uncompressed_deps_v vspec = get(weak_uncompressed_compat_v, pkg, nothing) weak_compat[uuid] = vspec === nothing ? VersionSpec() : vspec end @assert !isdefined(vinfo, :weak_uncompressed_compat) vinfo.weak_uncompressed_compat = weak_compat end return pkg end function compat_info(pkg::PkgInfo) initialize_uncompressed!(pkg) return Dict(v => info.uncompressed_compat for (v, info) in pkg.version_info) end function weak_compat_info(pkg::PkgInfo) if isempty(pkg.weak_deps) return nothing end initialize_weak_uncompressed!(pkg) return Dict(v => info.weak_uncompressed_compat for (v, info) in pkg.version_info) end mutable struct PkgEntry # Registry.toml: const path::String const registry_path::String const name::String const uuid::UUID const in_memory_registry::Union{Dict{String, String}, Nothing} # Version.toml / (Compat.toml / Deps.toml): info::PkgInfo # lazily initialized PkgEntry(path, registry_path, name, uuid, in_memory_registry) = new(path, registry_path, name, uuid, in_memory_registry, #= undef =#) end registry_info(pkg::PkgEntry) = init_package_info!(pkg) function init_package_info!(pkg::PkgEntry) # Already uncompressed the info for this package, return early isdefined(pkg, :info) && return pkg.info path = pkg.registry_path d_p = parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Package.toml")) name = d_p["name"]::String name != pkg.name && error("inconsistent name in Registry.toml ($(name)) and Package.toml ($(pkg.name)) for pkg at $(path)") repo = get(d_p, "repo", nothing)::Union{Nothing, String} subdir = get(d_p, "subdir", nothing)::Union{Nothing, String} # Versions.toml d_v = custom_isfile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Versions.toml")) ? parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Versions.toml")) : Dict{String, Any}() version_info = Dict{VersionNumber, VersionInfo}(VersionNumber(k) => VersionInfo(SHA1(v["git-tree-sha1"]::String), get(v, "yanked", false)::Bool) for (k, v) in d_v) # Compat.toml compat_data_toml = custom_isfile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Compat.toml")) ? parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Compat.toml")) : Dict{String, Any}() compat = Dict{VersionRange, Dict{String, VersionSpec}}() for (v, data) in compat_data_toml data = data::Dict{String, Any} vr = VersionRange(v) d = Dict{String, VersionSpec}(dep => VersionSpec(vr_dep) for (dep, vr_dep::Union{String, Vector{String}}) in data) compat[vr] = d end # Deps.toml deps_data_toml = custom_isfile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Deps.toml")) ? parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "Deps.toml")) : Dict{String, Any}() deps = Dict{VersionRange, Dict{String, UUID}}() for (v, data) in deps_data_toml data = data::Dict{String, Any} vr = VersionRange(v) d = Dict{String, UUID}(dep => UUID(uuid) for (dep, uuid::String) in data) deps[vr] = d end # All packages depend on julia deps[VersionRange()] = Dict("julia" => JULIA_UUID) # WeakCompat.toml weak_compat_data_toml = custom_isfile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "WeakCompat.toml")) ? parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "WeakCompat.toml")) : Dict{String, Any}() weak_compat = Dict{VersionRange, Dict{String, VersionSpec}}() for (v, data) in weak_compat_data_toml data = data::Dict{String, Any} vr = VersionRange(v) d = Dict{String, VersionSpec}(dep => VersionSpec(vr_dep) for (dep, vr_dep::Union{String, Vector{String}}) in data) weak_compat[vr] = d end # WeakDeps.toml weak_deps_data_toml = custom_isfile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "WeakDeps.toml")) ? parsefile(pkg.in_memory_registry, pkg.registry_path, joinpath(pkg.path, "WeakDeps.toml")) : Dict{String, Any}() weak_deps = Dict{VersionRange, Dict{String, UUID}}() for (v, data) in weak_deps_data_toml data = data::Dict{String, Any} vr = VersionRange(v) d = Dict{String, UUID}(dep => UUID(uuid) for (dep, uuid::String) in data) weak_deps[vr] = d end @assert !isdefined(pkg, :info) pkg.info = PkgInfo(repo, subdir, version_info, compat, deps, weak_compat, weak_deps) return pkg.info end function uncompress_registry(tar_gz::AbstractString) if !isfile(tar_gz) error("$(repr(tar_gz)): No such file") end data = Dict{String, String}() buf = Vector{UInt8}(undef, Tar.DEFAULT_BUFFER_SIZE) io = IOBuffer() open(`$(exe7z()) x $tar_gz -so`) do tar Tar.read_tarball(x->true, tar; buf=buf) do hdr, _ if hdr.type == :file Tar.read_data(tar, io; size=hdr.size, buf=buf) data[hdr.path] = String(take!(io)) end end end return data end struct RegistryInstance path::String name::String uuid::UUID repo::Union{String, Nothing} description::Union{String, Nothing} pkgs::Dict{UUID, PkgEntry} tree_info::Union{Base.SHA1, Nothing} in_memory_registry::Union{Nothing, Dict{String, String}} # various caches name_to_uuids::Dict{String, Vector{UUID}} end const REGISTRY_CACHE = Dict{String, Tuple{Base.SHA1, Bool, RegistryInstance}}() function get_cached_registry(path, tree_info::Base.SHA1, compressed::Bool) if !ispath(path) delete!(REGISTRY_CACHE, path) return nothing end v = get(REGISTRY_CACHE, path, nothing) if v !== nothing cached_tree_info, cached_compressed, reg = v if cached_tree_info == tree_info && cached_compressed == compressed return reg end end # Prevent hogging up memory indefinitely length(REGISTRY_CACHE) > 20 && empty!(REGISTRY_CACHE) return nothing end function RegistryInstance(path::AbstractString) compressed_file = nothing if isfile(path) @assert splitext(path)[2] == ".toml" d_reg_info = parsefile(nothing, dirname(path), basename(path)) compressed_file = d_reg_info["path"]::String tree_info = Base.SHA1(d_reg_info["git-tree-sha1"]::String) else tree_info_file = joinpath(path, ".tree_info.toml") tree_info = if isfile(tree_info_file) Base.SHA1(parsefile(nothing, path, ".tree_info.toml")["git-tree-sha1"]::String) else nothing end end # Reuse an existing cached registry if it exists for this content if tree_info !== nothing reg = get_cached_registry(path, tree_info, compressed_file !== nothing) if reg isa RegistryInstance return reg end end in_memory_registry = if compressed_file !== nothing uncompress_registry(joinpath(dirname(path), compressed_file)) else nothing end d = parsefile(in_memory_registry, path, "Registry.toml") pkgs = Dict{UUID, PkgEntry}() for (uuid, info) in d["packages"]::Dict{String, Any} uuid = UUID(uuid::String) info::Dict{String, Any} name = info["name"]::String pkgpath = info["path"]::String pkg = PkgEntry(pkgpath, path, name, uuid, in_memory_registry) pkgs[uuid] = pkg end reg = RegistryInstance( path, d["name"]::String, UUID(d["uuid"]::String), get(d, "repo", nothing)::Union{String, Nothing}, get(d, "description", nothing)::Union{String, Nothing}, pkgs, tree_info, in_memory_registry, Dict{String, UUID}(), ) if tree_info !== nothing REGISTRY_CACHE[path] = (tree_info, compressed_file !== nothing, reg) end return reg end function Base.show(io::IO, ::MIME"text/plain", r::RegistryInstance) println(io, "Registry: $(repr(r.name)) at $(repr(r.path)):") println(io, " uuid: ", r.uuid) println(io, " repo: ", r.repo) if r.tree_info !== nothing println(io, " git-tree-sha1: ", r.tree_info) end println(io, " packages: ", length(r.pkgs)) end function uuids_from_name(r::RegistryInstance, name::String) create_name_uuid_mapping!(r) return get(Vector{UUID}, r.name_to_uuids, name) end function create_name_uuid_mapping!(r::RegistryInstance) isempty(r.name_to_uuids) || return for (uuid, pkg) in r.pkgs uuids = get!(Vector{UUID}, r.name_to_uuids, pkg.name) push!(uuids, pkg.uuid) end return end function verify_compressed_registry_toml(path::String) d = TOML.tryparsefile(path) if d isa TOML.ParserError @warn "Failed to parse registry TOML file at $(repr(path))" exception=d return false end for key in ("git-tree-sha1", "uuid", "path") if !haskey(d, key) @warn "Expected key $(repr(key)) to exist in registry TOML file at $(repr(path))" return false end end compressed_file = joinpath(dirname(path), d["path"]::String) if !isfile(compressed_file) @warn "Expected the compressed registry for $(repr(path)) to exist at $(repr(compressed_file))" return false end return true end function reachable_registries(; depots::Union{String, Vector{String}}=Base.DEPOT_PATH) # collect registries if depots isa String depots = [depots] end registries = RegistryInstance[] for d in depots isdir(d) || continue reg_dir = joinpath(d, "registries") isdir(reg_dir) || continue reg_paths = readdir(reg_dir; join=true) candidate_registries = String[] # All folders could be registries append!(candidate_registries, filter(isdir, reg_paths)) if registry_read_from_tarball() compressed_registries = filter(endswith(".toml"), reg_paths) # if we are reading compressed registries, ignore compressed registries # with the same name compressed_registry_names = Set([splitext(basename(file))[1] for file in compressed_registries]) filter!(x -> !(basename(x) in compressed_registry_names), candidate_registries) for compressed_registry in compressed_registries if verify_compressed_registry_toml(compressed_registry) push!(candidate_registries, compressed_registry) end end end for candidate in candidate_registries # candidate can be either a folder or a TOML file if isfile(joinpath(candidate, "Registry.toml")) || isfile(candidate) push!(registries, RegistryInstance(candidate)) end end end return registries end # Dict interface function Base.haskey(r::RegistryInstance, uuid::UUID) return haskey(r.pkgs, uuid) end function Base.keys(r::RegistryInstance) return keys(r.pkgs) end Base.getindex(r::RegistryInstance, uuid::UUID) = r.pkgs[uuid] Base.get(r::RegistryInstance, uuid::UUID, default) = get(r.pkgs, uuid, default) Base.iterate(r::RegistryInstance) = iterate(r.pkgs) Base.iterate(r::RegistryInstance, state) = iterate(r.pkgs, state) v���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Resolve/Resolve.jlYP������# This file is a part of Julia. License is MIT: https://julialang.org/license module Resolve using ..Versions import ..stdout_f, ..stderr_f using Printf using Random using UUIDs export resolve, sanity_check, Graph, pkgID #################### # Requires / Fixed # #################### const Requires = Dict{UUID,VersionSpec} struct Fixed version::VersionNumber requires::Requires weak::Set{UUID} end Fixed(v::VersionNumber, requires::Requires = Requires()) = Fixed(v, requires, Set{UUID}()) Base.:(==)(a::Fixed, b::Fixed) = a.version == b.version && a.requires == b.requires && a.weak == b.weak Base.hash(f::Fixed, h::UInt) = hash((f.version, f.requires, f.weak), h + (0x68628b809fd417ca % UInt)) Base.show(io::IO, f::Fixed) = isempty(f.requires) ? print(io, "Fixed(", repr(f.version), ")") : isempty(f.weak) ? print(io, "Fixed(", repr(f.version), ",", f.requires, ")") : print(io, "Fixed(", repr(f.version), ",", f.requires, ", weak=", f.weak, ")") struct ResolverError <: Exception msg::AbstractString ex::Union{Exception,Nothing} end ResolverError(msg::AbstractString) = ResolverError(msg, nothing) function Base.showerror(io::IO, pkgerr::ResolverError) print(io, pkgerr.msg) if pkgerr.ex !== nothing pkgex = pkgerr.ex if isa(pkgex, CompositeException) for cex in pkgex print(io, "\n=> ") showerror(io, cex) end else print(io, "\n") showerror(io, pkgex) end end end const uuid_julia = UUID("1222c4b2-2114-5bfd-aeef-88e4692bbb3e") include("versionweights.jl") include("fieldvalues.jl") include("graphtype.jl") include("maxsum.jl") "Resolve package dependencies." function resolve(graph::Graph) sol = _resolve(graph::Graph, nothing, nothing) # return the solution as a Dict mapping UUID => VersionNumber return compute_output_dict(sol, graph) end function _resolve(graph::Graph, lower_bound::Union{Vector{Int},Nothing}, previous_sol::Union{Vector{Int},Nothing}) np = graph.np spp = graph.spp gconstr = graph.gconstr if lower_bound โ‰ข nothing for p0 = 1:np v0 = lower_bound[p0] @assert v0 โ‰  spp[p0] gconstr[p0][1:(v0-1)] .= false end end # attempt trivial solution first greedy_ok, sol = greedysolver(graph) greedy_ok && @goto solved log_event_global!(graph, "greedy solver failed") # trivial solution failed, use maxsum solver maxsum_ok, sol, staged = maxsum(graph) maxsum_ok && @goto solved log_event_global!(graph, "maxsum solver failed") @assert previous_sol โ‰ก nothing # the problem is unsat, force-trigger a failure # in order to produce a log - this will contain # information about the best that the solver could # achieve trigger_failure!(graph, sol, staged) @label solved # verify solution (debug code) and enforce its optimality @assert verify_solution(sol, graph) if greedy_ok log_event_global!(graph, "the solver found an optimal configuration") return sol else enforce_optimality!(sol, graph) if lower_bound โ‰ข nothing @assert all(sol .โ‰ฅ lower_bound) @assert all((sol .โ‰ฅ previous_sol) .| (previous_sol .== spp)) end if sol โ‰  previous_sol log_event_global!(graph, "the solver found a feasible configuration and will try to improve it") new_lower_bound = copy(sol) uninst_mask = sol .== spp new_lower_bound[uninst_mask] .= 1 if lower_bound โ‰ข nothing lb_mask = uninst_mask .& (lower_bound .โ‰  spp) new_lower_bound[lb_mask] = lower_bound[lb_mask] @assert all(new_lower_bound .โ‰ฅ lower_bound) end return _resolve(graph, new_lower_bound, sol) else log_event_global!(graph, "the solver found a feasible configuration and can't improve it") return sol end end end """ Scan the graph for (explicit or implicit) contradictions. Returns a list of problematic (package,version) combinations. """ function sanity_check(graph::Graph, sources::Set{UUID} = Set{UUID}(), verbose::Bool = true) req_inds = graph.req_inds fix_inds = graph.fix_inds id(p) = pkgID(p, graph) isempty(req_inds) || @warn("sanity check called on a graph with non-empty requirements") if !any(is_julia(graph, fp0) for fp0 in fix_inds) @warn("sanity check called on a graph without julia requirement, adding it") add_fixed!(graph, Dict(uuid_julia=>Fixed(VERSION))) end if length(fix_inds) โ‰  1 @warn("sanity check called on a graph with extra fixed requirements (besides julia)") end isources = isempty(sources) ? Set{Int}(1:graph.np) : Set{Int}(graph.data.pdict[p] for p in sources) simplify_graph!(graph, isources) np = graph.np spp = graph.spp gadj = graph.gadj data = graph.data pkgs = data.pkgs pdict = data.pdict pvers = data.pvers eq_classes = data.eq_classes problematic = Tuple{String,VersionNumber}[] np == 0 && return problematic vers = [(pkgs[p0],pvers[p0][v0]) for p0 = 1:np for v0 = 1:(spp[p0]-1)] sort!(vers, by=pv->(-length(gadj[pdict[pv[1]]]))) nv = length(vers) svdict = Dict{Tuple{UUID,VersionNumber},Int}(vers[i] => i for i = 1:nv) checked = falses(nv) last_str_len = 0 for (i,(p,vn)) in enumerate(vers) if verbose frac_compl = i / nv print("\r", " "^last_str_len, "\r") progr_msg = @sprintf("%.3i/%.3i (%i%%) โ€” problematic so far: %i", i, nv, round(Int, 100 * frac_compl), length(problematic)) print(progr_msg) last_str_len = length(progr_msg) end length(gadj[pdict[p]]) == 0 && break checked[i] && continue push_snapshot!(graph) # enforce package version # TODO: use add_reqs! instead... p0 = graph.data.pdict[p] v0 = graph.data.vdict[p0][vn] fill!(graph.gconstr[p0], false) graph.gconstr[p0][v0] = true push!(graph.req_inds, p0) ok = false try simplify_graph_soft!(graph, Set{Int}([p0]), log_events = false) catch err isa(err, ResolverError) || rethrow() @goto done end ok, sol = greedysolver(graph) ok && @goto done ok, sol = maxsum(graph) @label done if !ok for vneq in eq_classes[p][vn] push!(problematic, (id(p), vneq)) end else @assert verify_solution(sol, graph) sol_dict = compute_output_dict(sol, graph) for (sp,svn) in sol_dict j = svdict[sp,svn] checked[j] = true end end # state reset empty!(graph.req_inds) pop_snapshot!(graph) end if verbose print("\r", " "^last_str_len, "\r") println("found $(length(problematic)) problematic versions") end return sort!(problematic) end """ Translate the solver output (a Vector{Int} of package states) into a Dict which associates a VersionNumber to each installed package UUID. """ function compute_output_dict(sol::Vector{Int}, graph::Graph) np = graph.np spp = graph.spp fix_inds = graph.fix_inds pkgs = graph.data.pkgs pvers = graph.data.pvers pruned = graph.data.pruned want = Dict{UUID,VersionNumber}() for p0 = 1:np p0 โˆˆ fix_inds && continue p = pkgs[p0] s0 = sol[p0] s0 == spp[p0] && continue vn = pvers[p0][s0] want[p] = vn end for (p,vn) in pruned @assert !haskey(want, p) want[p] = vn end return want end """ Preliminary solver attempt: tries to maximize each version; bails out as soon as some non-trivial requirement is detected. """ function greedysolver(graph::Graph) spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk np = graph.np push_snapshot!(graph) gconstr = graph.gconstr # initialize solution: all uninstalled sol = [spp[p0] for p0 = 1:np] # packages which are not allowed to be uninstalled # (NOTE: this is potentially a superset of graph.req_inds, # since it may include implicit requirements) req_inds = Set{Int}(p0 for p0 = 1:np if !gconstr[p0][end]) # set up required packages to their highest allowed versions for rp0 in req_inds # look for the highest version which satisfies the requirements rv0 = findlast(gconstr[rp0]) @assert rv0 โ‰ข nothing && rv0 โ‰  spp[rp0] sol[rp0] = rv0 fill!(gconstr[rp0], false) gconstr[rp0][rv0] = true end # propagate the requirements try simplify_graph_soft!(graph, req_inds, log_events = false) catch err err isa ResolverError || rethrow() pop_snapshot!(graph) return (false, Int[]) end # we start from required packages and explore the graph # following dependencies staged = req_inds seen = copy(staged) while !isempty(staged) staged_next = Set{Int}() for p0 in staged s0 = sol[p0] @assert s0 < spp[p0] # scan dependencies for (j1,p1) in enumerate(gadj[p0]) msk = gmsk[p0][j1] # look for the highest version which satisfies the requirements v1 = findlast(msk[:,s0] .& gconstr[p1]) v1 == spp[p1] && continue # p1 is not required by p0's current version # if we found a version, and the package was uninstalled # or the same version was already selected, we're ok; # otherwise we can't be sure what the optimal configuration is # and we bail out old_v1 = sol[p1] if v1 โ‰ก nothing || (old_v1 โ‰  v1 && old_v1 โ‰  spp[p1]) pop_snapshot!(graph) return (false, Int[]) elseif old_v1 == spp[p1] sol[p1] = v1 push!(staged_next, p1) end end end union!(seen, staged_next) staged = staged_next end pop_snapshot!(graph) for p0 = 1:np log_event_greedysolved!(graph, p0, sol[p0]) end return true, sol end """ Verifies that the solver solution fulfills all hard constraints (requirements and dependencies). This is intended as debug code. """ function verify_solution(sol::Vector{Int}, graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr @assert length(sol) == np @assert all(sol .> 0) # verify constraints and dependencies for p0 = 1:np s0 = sol[p0] gconstr[p0][s0] || (@warn("gconstr[$p0][$s0] fail"); return false) for (j1,p1) in enumerate(gadj[p0]) msk = gmsk[p0][j1] s1 = sol[p1] msk[s1,s0] || (@warn("gmsk[$p0][$p1][$s1,$s0] fail"); return false) end end return true end """ Uninstall unreachable packages: start from the required ones and keep only the packages reachable from them along the graph. """ function _uninstall_unreachable!(sol::Vector{Int}, why::Vector{Union{Symbol,Int}}, graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr uninst = trues(np) staged = Set{Int}(p0 for p0 = 1:np if !gconstr[p0][end]) seen = copy(staged) โˆช Set{Int}(p0 for p0 = 1:np if sol[p0] == spp[p0]) # we'll skip uninstalled packages while !isempty(staged) staged_next = Set{Int}() for p0 in staged s0 = sol[p0] @assert s0 โ‰  spp[p0] uninst[p0] = false for (j1,p1) in enumerate(gadj[p0]) p1 โˆˆ seen && continue # we've already seen the package, or it is uninstalled gmsk[p0][j1][end,s0] && continue # the package is not required by p0 at version s0 push!(staged_next, p1) end end union!(seen, staged_next) staged = staged_next end for p0 in findall(uninst) sol[p0] = spp[p0] why[p0] = :uninst end end """ Push the given solution to a local optimum if needed: keeps increasing the states of the given solution as long as no constraints are violated. It might also install additional packages, if needed to bump the ones already installed. It also removes unnecessary parts of the solution which are unconnected to the required packages. """ function enforce_optimality!(sol::Vector{Int}, graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr pkgs = graph.data.pkgs # keep a track for the log why = Union{Symbol,Int}[0 for p0 = 1:np] # Strategy: # There's a cycle in which first the unnecessary (unconnected) packages are removed, # then we make a pass over the whole packages trying to bump each of them. # We repeat the above two steps until no further action is allowed. # When attempting to bump a package, we may attempt to bump or install other packages # if needed. Except if the bump would uninstall a package, in which cases we don't # touch anything else: we do it only if it has no consequence at all. This strategy # favors installing packages as needed. # During the bumping pass, we keep an upper and lower bound for each package, which # progressively shrink. These are used when adjusting for the effect of an attempted bump. # The way it's written should ensure that no package is ever downgraded (unless it was # originally unneeded, and then got removed, and later reinstalled to a lower version as # a consequence of a bump of some other package). # move_up is used to keep track of which packages can move up # (they start installed and can be bumped) and which down (they start uninstalled and # can be installed) move_up = BitVector(undef, length(sol)) # lower and upper bounds on the valid range of each package upperbound = similar(spp) lowerbound = similar(spp) # backup space for restoring the state if an attempted bump fails bk_sol = similar(sol) bk_lowerbound = similar(lowerbound) bk_upperbound = similar(upperbound) # auxiliary sets to perform breadth-first search on the graph staged = Set{Int}() staged_next = Set{Int}() old_sol = similar(sol) # to detect if we made any changes allsols = Set{Vector{Int}}() # used to make 100% sure we avoid infinite loops while true copy!(old_sol, sol) push!(allsols, copy(sol)) # step 1: uninstall unneeded packages _uninstall_unreachable!(sol, why, graph) # setp 2: try to bump each installed package in turn move_up .= sol .โ‰  spp copy!(upperbound, spp) let move_up = move_up lowerbound .= [move_up[p0] ? sol[p0] : 1 for p0 = 1:np] end for p0 = 1:np s0 = sol[p0] s0 == spp[p0] && (why[p0] = :uninst; continue) # the package is not installed move_up[p0] || continue # the package is only installed as a result of a previous bump, skip it @assert upperbound[p0] == spp[p0] # pick the next version that doesn't violate a constraint (if any) bump_range = collect(s0+1:spp[p0]) bump = let gconstr = gconstr findfirst(v0->gconstr[p0][v0], bump_range) end # no such version was found, skip this package bump โ‰ก nothing && (why[p0] = :constr; continue) # assume that we will succeed in bumping the version (otherwise we # roll-back at the end) new_s0 = bump_range[bump] try_uninstall = new_s0 == spp[p0] # are we trying to uninstall a package? copy!(bk_sol, sol) copy!(bk_lowerbound, lowerbound) copy!(bk_upperbound, upperbound) sol[p0] = new_s0 # if we're trying to uninstall, the bump is "soft": we don't update the # lower bound so that the package can be reinstalled later in the pass # if needed by another package try_uninstall || (lowerbound[p0] = new_s0) # note that we're in the move_up case empty!(staged) empty!(staged_next) push!(staged, p0) while !isempty(staged) for f0 in staged for (j1,f1) in enumerate(gadj[f0]) s1 = sol[f1] msk = gmsk[f0][j1] if f1 == p0 || try_uninstall # when uninstalling or looking at p0, no further changes are allowed bump_range = [s1] else lb1 = lowerbound[f1] ub1 = upperbound[f1] @assert lb1 โ‰ค s1 โ‰ค ub1 if move_up[f1] s1 > lb1 && @assert s1 == spp[f1] # the arrangement of the range gives precedence to improving the # current situation, but allows reinstalling a package if needed bump_range = vcat(s1:ub1, s1-1:-1:lb1) else bump_range = collect(ub1:-1:lb1) end end bump = let gconstr = gconstr findfirst(v1->(gconstr[f1][v1] && msk[v1, sol[f0]]), bump_range) end if bump โ‰ก nothing why[p0] = f1 # TODO: improve this? (ideally we might want the path from p0 to f1) @goto abort end new_s1 = bump_range[bump] sol[f1] = new_s1 new_s1 == s1 && continue push!(staged_next, f1) if move_up[f1] lowerbound[f1] = new_s1 else upperbound[f1] = new_s1 end end end staged, staged_next = staged_next, staged empty!(staged_next) end # if we're here the bump was successful, there's nothing more to do continue ## abort the bumping: restore the solution @label abort copy!(sol, bk_sol) copy!(lowerbound, bk_lowerbound) copy!(upperbound, bk_upperbound) end sol โ‰  old_sol || break # It might be possible in principle to contrive a situation in which # the solutions oscillate sol โˆˆ allsols && break end @assert verify_solution(sol, graph) for p0 = 1:np log_event_maxsumsolved!(graph, p0, sol[p0], why[p0]) end end function apply_maxsum_trace!(graph::Graph, sol::Vector{Int}) gconstr = graph.gconstr for (p0,s0) in enumerate(sol) s0 == 0 && continue gconstr0 = gconstr[p0] old_constr = copy(gconstr0) @assert old_constr[s0] fill!(gconstr0, false) gconstr0[s0] = true gconstr0 โ‰  old_constr && log_event_maxsumtrace!(graph, p0, s0) end end function trigger_failure!(graph::Graph, sol::Vector{Int}, staged::Tuple{Int,Int}) apply_maxsum_trace!(graph, sol) simplify_graph_soft!(graph, Set(findall(sol .> 0)), log_events = true) # this may throw an error... np = graph.np gconstr = graph.gconstr p0, v0 = staged @assert gconstr[p0][v0] fill!(gconstr[p0], false) gconstr[p0][v0] = true log_event_maxsumtrace!(graph, p0, v0) simplify_graph!(graph) # this may throw an error... outdict = resolve(graph) # ...otherwise, this MUST throw an error open(io->showlog(io, graph, view=:chronological), "logchrono.errresolve.txt", "w") error("this is not supposed to happen... $(Dict(pkgID(p, graph) => vn for (p,vn) in outdict))") end end # module }���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Resolve/versionweights.jlะ������# This file is a part of Julia. License is MIT: https://julialang.org/license # The numeric type used to determine how the different # versions of a package should be weighed struct VersionWeight major::Int64 minor::Int64 patch::Int64 end VersionWeight(major::Integer, minor::Integer) = VersionWeight(major, minor, 0) VersionWeight(major::Integer) = VersionWeight(major, 0) VersionWeight() = VersionWeight(0) VersionWeight(vn::VersionNumber) = VersionWeight(vn.major, vn.minor, vn.patch) Base.zero(::Type{VersionWeight}) = VersionWeight() Base.typemin(::Type{VersionWeight}) = (x=typemin(Int64); VersionWeight(x, x, x)) Base.:(-)(a::VersionWeight, b::VersionWeight) = VersionWeight(a.major-b.major, a.minor-b.minor, a.patch-b.patch) Base.:(+)(a::VersionWeight, b::VersionWeight) = VersionWeight(a.major+b.major, a.minor+b.minor, a.patch+b.patch) Base.:(-)(a::VersionWeight) = VersionWeight(-a.major, -a.minor, -a.patch) function Base.isless(a::VersionWeight, b::VersionWeight) (a.major, a.minor, a.patch) < (b.major, b.minor, b.patch) end Base.abs(a::VersionWeight) = VersionWeight(abs(a.major), abs(a.minor), abs(a.patch)) # This isn't nice, but it's for debugging only anyway function Base.show(io::IO, a::VersionWeight) print(io, "(", a.major) a == VersionWeight(a.major) && @goto done print(io, ".", a.minor) a == VersionWeight(a.major, a.minor) && @goto done print(io, ".", a.patch) @label done print(io, ")") end z���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Resolve/fieldvalues.jlู ������# This file is a part of Julia. License is MIT: https://julialang.org/license # FieldValue is a hierarchical numeric type with 5 levels. # When summing two FieldValues, the levels are summed independently. # When comparing them, lower levels take precedence. # The levels are used as such: # l0 : for hard constraints (dependencies and requirements) # l1 : for favoring higher versions of the explicitly required # packages # l2 : for favoring higher versions of all other packages # l3 : for favoring uninstallation of non-needed packages # struct FieldValue l0::Int64 l1::VersionWeight l2::VersionWeight l3::Int64 FieldValue(l0::Integer = 0, l1::VersionWeight = zero(VersionWeight), l2::VersionWeight = zero(VersionWeight), l3::Integer = 0) = new(l0, l1, l2, l3) end # This isn't nice, but it's for debugging only anyway function Base.show(io::IO, a::FieldValue) print(io, a.l0) a == FieldValue(a.l0) && return print(io, ".", a.l1) a == FieldValue(a.l0, a.l1) && return print(io, ".", a.l2) a == FieldValue(a.l0, a.l1, a.l2) && return print(io, ".", a.l3) return end const Field = Vector{FieldValue} Base.zero(::Type{FieldValue}) = FieldValue() Base.typemin(::Type{FieldValue}) = (x=typemin(Int64); y=typemin(VersionWeight); FieldValue(x, y, y, x)) Base.:-(a::FieldValue, b::FieldValue) = FieldValue(a.l0-b.l0, a.l1-b.l1, a.l2-b.l2, a.l3-b.l3) Base.:+(a::FieldValue, b::FieldValue) = FieldValue(a.l0+b.l0, a.l1+b.l1, a.l2+b.l2, a.l3+b.l3) function Base.isless(a::FieldValue, b::FieldValue) a.l0 < b.l0 && return true a.l0 > b.l0 && return false return (a.l1, a.l2, a.l3) < (b.l1, b.l2, b.l3) end Base.abs(a::FieldValue) = FieldValue(abs(a.l0), abs(a.l1), abs(a.l2), abs(a.l3)) # if the maximum field has l0 < 0, it means that # some hard constraint is being violated validmax(a::FieldValue) = a.l0 >= 0 # like usual argmax, but favors the highest indices # in case of a tie function Base.argmax(f::Field) m = typemin(FieldValue) mi = 0 for j = length(f):-1:1 if f[j] > m m = f[j] mi = j end end @assert mi != 0 return mi end # secondmax returns the (normalized) value of the second maximum in a # field. It's used to determine the most polarized field. function secondmax(f::Field, msk::BitVector = trues(length(f))) m = typemin(FieldValue) m2 = typemin(FieldValue) for i = 1:length(f) msk[i] || continue a = f[i] if a > m m2 = m m = a elseif a > m2 m2 = a end end return m2 - m end # Support broadcasting like a scalar by default Base.Broadcast.broadcastable(a::FieldValue) = Ref(a) x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Resolve/graphtype.jlฃษ������# This file is a part of Julia. License is MIT: https://julialang.org/license # The ResolveLog is used to keep track of events that take place during the # resolution process. We use one ResolveLogEntry per package, and record all events # associated with that package. An event consists of two items: another # entry representing another package's influence (or `nothing`) and # a message for the log. # # Specialized functions called `log_event_[...]!` are used to store the # various events. The events are also recorded in order in a shared # ResolveJournal, which is used to provide a plain chronological view. # # The `showlog` functions are used for display, and called to create messages # in case of resolution errors. const UUID0 = UUID(UInt128(0)) const ResolveJournal = Vector{Tuple{UUID,String}} mutable struct ResolveLogEntry journal::ResolveJournal # shared with all other entries pkg::UUID header::String events::Vector{Tuple{Any,String}} # here Any should ideally be Union{ResolveLogEntry,Nothing} ResolveLogEntry(journal::ResolveJournal, pkg::UUID, header::String = "") = new(journal, pkg, header, []) end function Base.push!(entry::ResolveLogEntry, reason::Tuple{Union{ResolveLogEntry,Nothing},String}, to_journal::Bool = true) push!(entry.events, reason) to_journal && entry.pkg โ‰  uuid_julia && push!(entry.journal, (entry.pkg, reason[2])) return entry end mutable struct ResolveLog # init: used to keep track of all package entries which were created during # initialization, since the `pool` can be pruned during the resolution # process. init::ResolveLogEntry # globals: records global events not associated to any particular package globals::ResolveLogEntry # pool: records entries associated to each package pool::Dict{UUID,ResolveLogEntry} # journal: record all messages in order (shared between all entries) journal::ResolveJournal # exact: keeps track of whether the resolve process is still exact, or # heuristics have been employed exact::Bool # verbose: print global events messages on screen verbose::Bool # UUID to names uuid_to_name::Dict{UUID,String} function ResolveLog(uuid_to_name::Dict{UUID,String}, verbose::Bool = false) journal = ResolveJournal() init = ResolveLogEntry(journal, UUID0, "") globals = ResolveLogEntry(journal, UUID0, "Global events:") return new(init, globals, Dict{UUID,ResolveLogEntry}(), journal, true, verbose, uuid_to_name) end end # Installation state: either a version, or uninstalled const InstState = Union{VersionNumber,Nothing} # GraphData is basically a part of Graph that collects data structures useful # for interfacing the internal abstract representation of Graph with the # input/output (e.g. converts between package UUIDs and node numbers, etc.) mutable struct GraphData # packages list pkgs::Vector{UUID} # number of packages np::Int # states per package: one per version + uninstalled spp::Vector{Int} # package dict: associates an index to each package id pdict::Dict{UUID,Int} # package versions: for each package, keep the list of the # possible version numbers; this defines a # mapping from version numbers of a package # to indices pvers::Vector{Vector{VersionNumber}} # versions dict: associates a version index to each package # version; such that # pvers[p0][vdict[p0][vn]] = vn vdict::Vector{Dict{VersionNumber,Int}} # UUID to names uuid_to_name::Dict{UUID,String} # pruned packages: during graph simplification, packages that # only have one allowed version are pruned. # This keeps track of them, so that they may # be returned in the solution (unless they # were explicitly fixed) pruned::Dict{UUID,VersionNumber} # equivalence classes: for each package and each of its possible # states, keep track of other equivalent states eq_classes::Dict{UUID,Dict{InstState,Set{InstState}}} # resolve log: keep track of the resolution process rlog::ResolveLog function GraphData( compat::Dict{UUID,Dict{VersionNumber,Dict{UUID,VersionSpec}}}, uuid_to_name::Dict{UUID,String}, verbose::Bool = false ) # generate pkgs pkgs = sort!(collect(keys(compat))) np = length(pkgs) # generate pdict pdict = Dict{UUID,Int}(pkgs[p0] => p0 for p0 = 1:np) # generate spp and pvers pvers = [sort!(collect(keys(compat[pkgs[p0]]))) for p0 = 1:np] spp = length.(pvers) .+ 1 # generate vdict vdict = [Dict{VersionNumber,Int}(vn => i for (i,vn) in enumerate(pvers[p0])) for p0 = 1:np] # nothing is pruned yet, of course pruned = Dict{UUID,VersionNumber}() # equivalence classes (at the beginning each state represents just itself) eq_vn(v0, p0) = (v0 == spp[p0] ? nothing : pvers[p0][v0]) # Hot code, measure performance before changing eq_classes = Dict{UUID,Dict{InstState,Set{InstState}}}() for p0 = 1:np d = Dict{InstState, Set{InstState}}() for v0 = 1:spp[p0] let p0 = p0 # Due to https://github.com/JuliaLang/julia/issues/15276 d[eq_vn(v0,p0)] = Set([eq_vn(v0,p0)]) end end eq_classes[pkgs[p0]] = d end # the resolution log is actually initialized below rlog = ResolveLog(uuid_to_name, verbose) data = new(pkgs, np, spp, pdict, pvers, vdict, uuid_to_name, pruned, eq_classes, rlog) init_log!(data) return data end function Base.copy(data::GraphData) pkgs = copy(data.pkgs) np = data.np spp = copy(data.spp) pdict = copy(data.pdict) pvers = [copy(data.pvers[p0]) for p0 = 1:np] vdict = [copy(data.vdict[p0]) for p0 = 1:np] pruned = copy(data.pruned) eq_classes = Dict(p => copy(eq) for (p,eq) in data.eq_classes) rlog = deepcopy(data.rlog) uuid_to_name = rlog.uuid_to_name return new(pkgs, np, spp, pdict, pvers, vdict, uuid_to_name, pruned, eq_classes, rlog) end end mutable struct Graph # data: # stores all the structures required to map between # parsed items (names, UUIDS, version numbers...) and # the numeric representation used in the main Graph data # structure. data::GraphData # adjacency matrix: # for each package, has the list of neighbors # indices gadj::Vector{Vector{Int}} # compatibility mask: # for each package p0 has a list of bool masks. # Each entry in the list gmsk[p0] is relative to the # package p1 as read from gadj[p0]. # Each mask has dimension spp1 ร— spp0, where # spp0 is the number of states of p0, and # spp1 is the number of states of p1. gmsk::Vector{Vector{BitMatrix}} # constraints: # a mask of allowed states for each package (e.g. to express # requirements) gconstr::Vector{BitVector} # adjacency dict: # allows one to retrieve the indices in gadj, so that # gadj[p0][adjdict[p1][p0]] = p1 # ("At which index does package p1 appear in gadj[p0]?") adjdict::Vector{Dict{Int,Int}} # indices of the packages that were *explicitly* required # used to favor their versions at resolution req_inds::Set{Int} # indices of the packages that were *explicitly* fixed # used to avoid returning them in the solution fix_inds::Set{Int} ignored::BitVector # stack of constraints/ignored packages: # allows to keep a sort of "versioning" of the constraints # such that the solver can implement tentative solutions solve_stack::Vector{Tuple{Vector{BitVector},BitVector}} # states per package: same as in GraphData spp::Vector{Int} # number of packages (all Vectors above have this length) np::Int # some workspace vectors newmsg::Vector{FieldValue} diff::Vector{FieldValue} cavfld::Vector{FieldValue} function Graph( compat::Dict{UUID,Dict{VersionNumber,Dict{UUID,VersionSpec}}}, compat_weak::Dict{UUID,Dict{VersionNumber,Set{UUID}}}, uuid_to_name::Dict{UUID,String}, reqs::Requires, fixed::Dict{UUID,Fixed}, verbose::Bool = false, julia_version::Union{VersionNumber,Nothing} = VERSION ) # Tell the resolver about julia itself uuid_to_name[uuid_julia] = "julia" if julia_version !== nothing fixed[uuid_julia] = Fixed(julia_version) compat[uuid_julia] = Dict(julia_version => Dict{VersionNumber,Dict{UUID,VersionSpec}}()) else compat[uuid_julia] = Dict{VersionNumber,Dict{UUID,VersionSpec}}() end data = GraphData(compat, uuid_to_name, verbose) pkgs, np, spp, pdict, pvers, vdict, rlog = data.pkgs, data.np, data.spp, data.pdict, data.pvers, data.vdict, data.rlog extended_deps = let spp = spp # Due to https://github.com/JuliaLang/julia/issues/15276 [Vector{Dict{Int,BitVector}}(undef, spp[p0]-1) for p0 = 1:np] end for p0 = 1:np, v0 = 1:(spp[p0]-1) vn = pvers[p0][v0] req = Dict{Int,VersionSpec}() uuid0 = pkgs[p0] vnmap = get(Dict{String,VersionSpec}, compat[uuid0], vn) for (uuid1, vs) in vnmap p1 = pdict[uuid1] p1 == p0 && error("Package $(pkgID(pkgs[p0], uuid_to_name)) version $vn has a dependency with itself") # check conflicts instead of intersecting? # (intersecting is used by fixed packages though...) req_p1 = get!(VersionSpec, req, p1) req[p1] = req_p1 โˆฉ vs end # Translate the requirements into bit masks # Hot code, measure performance before changing req_msk = Dict{Int,BitVector}() maybe_weak = haskey(compat_weak, uuid0) && haskey(compat_weak[uuid0], vn) for (p1, vs) in req pv = pvers[p1] req_msk_p1 = BitVector(undef, spp[p1]) @inbounds for i in 1:spp[p1] - 1 req_msk_p1[i] = pv[i] โˆˆ vs end weak = maybe_weak && (pkgs[p1] โˆˆ compat_weak[uuid0][vn]) req_msk_p1[end] = weak req_msk[p1] = req_msk_p1 end extended_deps[p0][v0] = req_msk end gadj = [Int[] for p0 = 1:np] gmsk = [BitMatrix[] for p0 = 1:np] gconstr = let spp = spp # Due to https://github.com/JuliaLang/julia/issues/15276 [trues(spp[p0]) for p0 = 1:np] end adjdict = [Dict{Int,Int}() for p0 = 1:np] for p0 = 1:np, v0 = 1:(spp[p0]-1), (p1,rmsk1) in extended_deps[p0][v0] @assert p0 โ‰  p1 j0 = get(adjdict[p1], p0, length(gadj[p0]) + 1) j1 = get(adjdict[p0], p1, length(gadj[p1]) + 1) @assert (j0 > length(gadj[p0]) && j1 > length(gadj[p1])) || (j0 โ‰ค length(gadj[p0]) && j1 โ‰ค length(gadj[p1])) if j0 > length(gadj[p0]) push!(gadj[p0], p1) push!(gadj[p1], p0) j0 = length(gadj[p0]) j1 = length(gadj[p1]) adjdict[p1][p0] = j0 adjdict[p0][p1] = j1 bm = trues(spp[p1], spp[p0]) bmt = trues(spp[p0], spp[p1]) push!(gmsk[p0], bm) push!(gmsk[p1], bmt) else bm = gmsk[p0][j0] bmt = gmsk[p1][j1] end for v1 = 1:spp[p1] rmsk1[v1] && continue bm[v1, v0] = false bmt[v0, v1] = false end end req_inds = Set{Int}() fix_inds = Set{Int}() ignored = falses(np) solve_stack = Tuple{Vector{BitVector},BitVector}[] graph = new(data, gadj, gmsk, gconstr, adjdict, req_inds, fix_inds, ignored, solve_stack, spp, np, FieldValue[], FieldValue[], FieldValue[]) _add_fixed!(graph, fixed) _add_reqs!(graph, reqs, :explicit_requirement) @assert check_consistency(graph) check_constraints(graph) return graph end function Base.copy(graph::Graph) data = copy(graph.data) np = graph.np spp = data.spp gadj = [copy(graph.gadj[p0]) for p0 = 1:np] gmsk = [[copy(graph.gmsk[p0][j0]) for j0 = 1:length(gadj[p0])] for p0 = 1:np] gconstr = [copy(graph.gconstr[p0]) for p0 = 1:np] adjdict = [copy(graph.adjdict[p0]) for p0 = 1:np] req_inds = copy(graph.req_inds) fix_inds = copy(graph.fix_inds) ignored = copy(graph.ignored) solve_stack = [([copy(gc0) for gc0 in sav_gconstr],copy(sav_ignored)) for (sav_gconstr,sav_ignored) in graph.solve_stack] return new(data, gadj, gmsk, gconstr, adjdict, req_inds, fix_inds, ignored, solve_stack, spp, np) end end """ Add explicit requirements to the graph. """ function add_reqs!(graph::Graph, reqs::Requires) _add_reqs!(graph, reqs, :explicit_requirement) check_constraints(graph) # TODO: add reqs to graph data? return graph end function _add_reqs!(graph::Graph, reqs::Requires, reason; weak_reqs::Set{UUID} = Set{UUID}()) gconstr = graph.gconstr spp = graph.spp req_inds = graph.req_inds pdict = graph.data.pdict pvers = graph.data.pvers for (rp,rvs) in reqs haskey(pdict, rp) || error("unknown required package $(pkgID(rp, graph))") rp0 = pdict[rp] new_constr = trues(spp[rp0]) for rv0 = 1:(spp[rp0]-1) rvn = pvers[rp0][rv0] rvn โˆˆ rvs || (new_constr[rv0] = false) end weak = rp โˆˆ weak_reqs new_constr[end] = weak old_constr = copy(gconstr[rp0]) gconstr[rp0] .&= new_constr reason โ‰ก :explicit_requirement && push!(req_inds, rp0) old_constr โ‰  gconstr[rp0] && log_event_req!(graph, rp, rvs, reason) end return graph end "Add fixed packages to the graph, and their requirements." function add_fixed!(graph::Graph, fixed::Dict{UUID,Fixed}) _add_fixed!(graph, fixed) check_constraints(graph) # TODO: add fixed to graph data? return graph end function _add_fixed!(graph::Graph, fixed::Dict{UUID,Fixed}) gconstr = graph.gconstr spp = graph.spp fix_inds = graph.fix_inds pdict = graph.data.pdict vdict = graph.data.vdict for (fp,fx) in fixed haskey(pdict, fp) || error("unknown fixed package $(pkgID(fp, graph))") fp0 = pdict[fp] fv0 = vdict[fp0][fx.version] new_constr = falses(spp[fp0]) new_constr[fv0] = true gconstr[fp0] .&= new_constr push!(fix_inds, fp0) bkitem = log_event_fixed!(graph, fp, fx) _add_reqs!(graph, fx.requires, (fp, bkitem); weak_reqs=fx.weak) end return graph end pkgID(p::UUID, rlog::ResolveLog) = pkgID(p, rlog.uuid_to_name) pkgID(p::UUID, data::GraphData) = pkgID(p, data.uuid_to_name) pkgID(p0::Int, data::GraphData) = pkgID(data.pkgs[p0], data) pkgID(p, graph::Graph) = pkgID(p, graph.data) ## user-friendly representation of package IDs ## function pkgID(p::UUID, uuid_to_name::Dict{UUID,String}) name = get(uuid_to_name, p, "(unknown)") uuid_short = string(p)[1:8] return "$name [$uuid_short]" end function check_consistency(graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr adjdict = graph.adjdict req_inds = graph.req_inds fix_inds = graph.fix_inds ignored = graph.ignored solve_stack = graph.solve_stack data = graph.data pkgs = data.pkgs pdict = data.pdict pvers = data.pvers vdict = data.vdict pruned = data.pruned eq_classes = data.eq_classes rlog = data.rlog # TODO: check ignored and solve_stack @assert np โ‰ฅ 0 for x in Any[spp, gadj, gmsk, gconstr, adjdict, ignored, rlog.pool, pkgs, pdict, pvers, vdict] @assert length(x)::Int == np end for p0 = 1:np @assert pdict[pkgs[p0]] == p0 spp0 = spp[p0] @assert spp0 โ‰ฅ 1 pvers0 = pvers[p0] vdict0 = vdict[p0] @assert length(pvers0) == spp0 - 1 for v0 = 1:(spp0-1) @assert vdict0[pvers0[v0]] == v0 end for (vn,v0) in vdict0 @assert 1 โ‰ค v0 โ‰ค spp0-1 @assert pvers0[v0] == vn end gconstr0 = gconstr[p0] @assert length(gconstr0) == spp0 gadj0 = gadj[p0] gmsk0 = gmsk[p0] adjdict0 = adjdict[p0] @assert length(gmsk0) == length(gadj0) @assert length(adjdict0) == length(gadj0) for (j0,p1) in enumerate(gadj0) @assert p1 โ‰  p0 @assert adjdict[p1][p0] == j0 spp1 = spp[p1] @assert size(gmsk0[j0]) == (spp1,spp0) j1 = adjdict0[p1] gmsk1 = gmsk[p1] # This assert is a bit too expensive # @assert gmsk1[j1] == permutedims(gmsk0[j0]) end end for (p,p0) in pdict @assert 1 โ‰ค p0 โ‰ค np @assert pkgs[p0] == p @assert !haskey(pruned, p) end for p0 in req_inds @assert 1 โ‰ค p0 โ‰ค np @assert !gconstr[p0][end] end for p0 in fix_inds @assert 1 โ‰ค p0 โ‰ค np @assert !gconstr[p0][end] @assert count(gconstr[p0]) โ‰ค 1 # note: the 0 case should be handled by check_constraints end for (p,eq_cl) in eq_classes, (rvn,rvs) in eq_cl @assert rvn โˆˆ rvs end for (sav_gconstr,sav_ignored) in solve_stack @assert length(sav_ignored) == np @assert length(sav_gconstr) == np for p0 = 1:np @assert length(sav_gconstr[p0]) == spp[p0] end end return true end function push_snapshot!(graph::Graph) np = graph.np gconstr = graph.gconstr ignored = graph.ignored solve_stack = graph.solve_stack sav_gconstr = [copy(gc0) for gc0 in gconstr] sav_ignored = copy(ignored) push!(solve_stack, (gconstr, ignored)) graph.gconstr = sav_gconstr graph.ignored = sav_ignored return graph end function pop_snapshot!(graph::Graph) np = graph.np solve_stack = graph.solve_stack @assert length(solve_stack) > 0 sav_gconstr, sav_ignored = pop!(graph.solve_stack) graph.gconstr = sav_gconstr graph.ignored = sav_ignored return graph end function wipe_snapshots!(graph::Graph) np = graph.np solve_stack = graph.solve_stack if length(solve_stack) > 0 graph.gconstr, graph.ignored = first(solve_stack) empty!(solve_stack) end return graph end # system colors excluding whites/greys/blacks and error-red const CONFLICT_COLORS = [1:6; 10:14]; pkgID_color(pkgID) = CONFLICT_COLORS[mod1(hash(pkgID), end)] logstr(pkgID) = logstr(pkgID, pkgID) function logstr(pkgID, args...) # workout the string with the color codes, check stderr to decide if color is enabled return sprint(args; context=stderr::IO) do io, iargs printstyled(io, iargs...; color=pkgID_color(pkgID)) end end """ range_compressed_versionspec(pool, subset=pool) - `pool`: all versions that exist (a superset of `subset`) - `subset`: the subset of those versions that we want to include in the spec. Finds a minimal collection of ranges as a `VersionSpec`, that permits everything in the `subset`, but does not permit anything else from the `pool`. """ function range_compressed_versionspec(pool, subset=pool) length(subset) == 1 && return VersionSpec(only(subset)) # PREM-OPT: we keep re-sorting these, probably not required. sort!(pool) sort!(subset) @assert subset โІ pool contiguous_subsets = VersionRange[] range_start = first(subset) pool_ii = findfirst(isequal(range_start), pool) + 1 # skip-forward til we have started for s in @view subset[2:end] if s != pool[pool_ii] range_end = pool[pool_ii-1] # previous element was last in this range push!(contiguous_subsets, VersionRange(range_start, range_end)) range_start = s # start a new range while (s != pool[pool_ii]) # advance til time to start pool_ii += 1 end end pool_ii += 1 end push!(contiguous_subsets, VersionRange(range_start, last(subset))) return VersionSpec(contiguous_subsets) end function init_log!(data::GraphData) np = data.np pkgs = data.pkgs pvers = data.pvers rlog = data.rlog for p0 = 1:np p = pkgs[p0] id = pkgID(p0, data) versions = pvers[p0] if isempty(versions) msg = "$(logstr(id)) has no known versions!" # This shouldn't happen? else vspec = range_compressed_versionspec(versions) vers = logstr(id, vspec) uuid = data.pkgs[p0] name = data.uuid_to_name[uuid] pkgid = Base.PkgId(uuid, name) msg = "possible versions are: $vers or uninstalled" if Base.in_sysimage(pkgid) msg *= " (package in sysimage!)" end end first_entry = get!(rlog.pool, p) do ResolveLogEntry(rlog.journal, p, "$(logstr(id)) log:") end if p โ‰  uuid_julia push!(first_entry, (nothing, msg)) push!(rlog.init, (first_entry, ""), false) end end return data end function log_event_fixed!(graph::Graph, fp::UUID, fx::Fixed) rlog = graph.data.rlog id = pkgID(fp, rlog) msg = "$(logstr(id)) is fixed to version $(logstr(id, fx.version))" entry = rlog.pool[fp] push!(entry, (nothing, msg)) return entry end function _vs_string(p0::Int, vmask::BitVector, id::String, pvers::Vector{Vector{VersionNumber}}) if any(vmask[1:(end-1)]) vspec = range_compressed_versionspec(pvers[p0], pvers[p0][vmask[1:(end-1)]]) vns = logstr(id, vspec) vmask[end] && (vns *= " or uninstalled") else @assert vmask[end] vns = "uninstalled" end return vns end function log_event_req!(graph::Graph, rp::UUID, rvs::VersionSpec, reason) rlog = graph.data.rlog gconstr = graph.gconstr pdict = graph.data.pdict pvers = graph.data.pvers id = pkgID(rp, rlog) msg = "restricted to versions $(logstr(id, rvs)) by " if reason isa Symbol @assert reason === :explicit_requirement other_entry = nothing msg *= "an explicit requirement" else other_p, other_entry = reason::Tuple{UUID,ResolveLogEntry} if other_p == uuid_julia msg *= "julia compatibility requirements" other_entry = nothing # don't propagate the log else msg *= logstr(pkgID(other_p, rlog)) end end rp0 = pdict[rp] @assert !gconstr[rp0][end] || reason โ‰ข :explicit_requirement if any(gconstr[rp0]) msg *= ", leaving only versions: $(_vs_string(rp0, gconstr[rp0], id, pvers))" else msg *= " โ€” no versions left" end entry = rlog.pool[rp] push!(entry, (other_entry, msg)) return entry end function log_event_global!(graph::Graph, msg::String) rlog = graph.data.rlog rlog.verbose && @info(msg) push!(rlog.globals, (nothing, msg)) end function log_event_implicit_req!(graph::Graph, p1::Int, vmask::BitVector, p0::Int) rlog = graph.data.rlog gconstr = graph.gconstr pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p1] id = pkgID(p, rlog) other_p, other_entry = pkgs[p0], rlog.pool[pkgs[p0]] other_id = pkgID(other_p, rlog) if any(vmask) if all(vmask[1:(end-1)]) # Check if all versions are allowed (except uninstalled) msg = "" other_entry = nothing # Don't propagate the log if all versions allowed else msg = "restricted by " if other_p == uuid_julia msg *= "julia compatibility requirements " other_entry = nothing # don't propagate the log else msg *= "compatibility requirements with $(logstr(other_id)) " end msg *= "to versions: $(_vs_string(p1, vmask, id, pvers))" if vmask โ‰  gconstr[p1] if any(gconstr[p1]) msg *= ", leaving only versions: $(_vs_string(p1, gconstr[p1], id, pvers))" else msg *= " โ€” no versions left" end end end else msg = "found to have no compatible versions left with " if other_p == uuid_julia msg *= "julia" other_entry = nothing # don't propagate the log else msg *= logstr(other_id) end end entry = rlog.pool[p] push!(entry, (other_entry, msg)) return entry end function log_event_pruned!(graph::Graph, p0::Int, s0::Int) rlog = graph.data.rlog spp = graph.spp pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p0] id = pkgID(p, rlog) if s0 == spp[p0] msg = "determined to be unneeded during graph pruning" else ver = logstr(id, pvers[p0][s0]) msg = "fixed during graph pruning to its only remaining available version, $ver" end entry = rlog.pool[p] push!(entry, (nothing, msg)) return entry end function log_event_greedysolved!(graph::Graph, p0::Int, s0::Int) rlog = graph.data.rlog spp = graph.spp pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p0] id = pkgID(p, rlog) if s0 == spp[p0] msg = "determined to be unneeded by the solver" else ver = logstr(id, pvers[p0][s0]) if s0 == spp[p0] - 1 msg = "set by the solver to its maximum version: $ver" else msg = "set by the solver to the maximum version compatible with the constraints: $ver" end end entry = rlog.pool[p] push!(entry, (nothing, msg)) return entry end function log_event_maxsumsolved!(graph::Graph, p0::Int, s0::Int, why::Symbol) rlog = graph.data.rlog spp = graph.spp pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p0] id = pkgID(p, rlog) if s0 == spp[p0] @assert why === :uninst msg = "determined to be unneeded by the solver" else @assert why === :constr ver = logstr(id, pvers[p0][s0]) if s0 == spp[p0] - 1 msg = "set by the solver to its maximum version: $ver" else xver = logstr(id, pvers[p0][s0+1]) msg = "set by the solver to version: $ver (version $xver would violate its constraints)" end end entry = rlog.pool[p] push!(entry, (nothing, msg)) return entry end function log_event_maxsumsolved!(graph::Graph, p0::Int, s0::Int, p1::Int) rlog = graph.data.rlog spp = graph.spp pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p0] id = pkgID(p, rlog) other_id = pkgID(pkgs[p1], rlog) @assert s0 โ‰  spp[p0] ver = logstr(id, pvers[p0][s0]) if s0 == spp[p0] - 1 msg = "set by the solver to its maximum version: $ver (installation is required by $other_id)" else xver = logstr(id, pvers[p0][s0+1]) msg = "set by the solver version: $ver (version $xver would violate a dependency relation with $other_id)" end other_entry = rlog.pool[pkgs[p1]] entry = rlog.pool[p] push!(entry, (other_entry, msg)) return entry end function log_event_eq_classes!(graph::Graph, p0::Int) rlog = graph.data.rlog spp = graph.spp gconstr = graph.gconstr pkgs = graph.data.pkgs pvers = graph.data.pvers p = pkgs[p0] id = pkgID(p, rlog) msg = "versions reduced by equivalence to: " if any(gconstr[p0][1:(end-1)]) vspec = range_compressed_versionspec(pvers[p0], pvers[p0][gconstr[p0][1:(end-1)]]) msg *= logstr(id, vspec) gconstr[p0][end] && (msg *= " or uninstalled") elseif gconstr[p0][end] msg *= "uninstalled" else msg *= "no version" end entry = rlog.pool[p] push!(entry, (nothing, msg)) return entry end function log_event_maxsumtrace!(graph::Graph, p0::Int, s0::Int) rlog = graph.data.rlog rlog.exact = false p = graph.data.pkgs[p0] id = pkgID(p, rlog) if s0 < graph.spp[p0] ver = logstr(id, graph.data.pvers[p0][s0]) msg = "fixed by the MaxSum heuristic to version $ver" else msg = "determined to be unneeded by the MaxSum heuristic" end entry = rlog.pool[p] push!(entry, (nothing, msg)) return entry end "Get the resolution log, detached" get_resolve_log(graph::Graph) = deepcopy(graph.data.rlog) const _logindent = " " showlog(graph::Graph, args...; kw...) = showlog(stdout_f(), graph, args...; kw...) showlog(io::IO, graph::Graph, args...; kw...) = showlog(io, graph.data.rlog, args...; kw...) showlog(rlog::ResolveLog, args...; kw...) = showlog(stdout_f(), rlog, args...; kw...) """ Show the full resolution log. The `view` keyword controls how the events are displayed/grouped: * `:plain` for a shallow view, grouped by package, alphabetically (the default) * `:tree` for a tree view in which the log of a package is displayed as soon as it appears in the process (the top-level is still grouped by package, alphabetically) * `:chronological` for a flat view of all events in chronological order """ function showlog(io::IO, rlog::ResolveLog; view::Symbol = :plain) view โˆˆ [:plain, :tree, :chronological] || throw(ArgumentError("the view argument should be `:plain`, `:tree` or `:chronological`")) println(io, "Resolve log:") view === :chronological && return showlogjournal(io, rlog) seen = IdDict() recursive = (view === :tree) _show(io, rlog, rlog.globals, _logindent, seen, false) initentries = Union{ResolveLogEntry,Nothing}[event[1]::Union{ResolveLogEntry,Nothing} for event in rlog.init.events] for entry in sort!(initentries, by=(entry->pkgID(entry.pkg, rlog))) seen[entry] = true _show(io, rlog, entry, _logindent, seen, recursive) end end function showlogjournal(io::IO, rlog::ResolveLog) journal = rlog.journal id(p) = p == UUID0 ? "[global event]" : logstr(pkgID(p, rlog)) padding = maximum(length(id(p)) for (p,_) in journal; init=0) for (p,msg) in journal println(io, ' ', rpad(id(p), padding), ": ", msg) end end """ Show the resolution log for some package, and all the other packages that affected it during resolution. The `view` option can be either `:plain` or `:tree` (works the same as for `showlog(io, rlog)`); the default is `:tree`. """ function showlog(io::IO, rlog::ResolveLog, p::UUID; view::Symbol = :tree) view โˆˆ [:plain, :tree] || throw(ArgumentError("the view argument should be `:plain` or `:tree`")) entry = rlog.pool[p] if view === :tree _show(io, rlog, entry, _logindent, IdDict{Any,Any}(entry=>true), true) else entries = ResolveLogEntry[entry] function getentries(entry) for (other_entry,_) in entry.events (other_entry โ‰ก nothing || other_entry โˆˆ entries) && continue push!(entries, other_entry) getentries(other_entry) end end getentries(entry) for entry in entries _show(io, rlog, entry, _logindent, IdDict(), false) end end end # Show a recursive tree with requirements applied to a package, either directly or indirectly function _show(io::IO, rlog::ResolveLog, entry::ResolveLogEntry, indent::String, seen::IdDict, recursive::Bool) toplevel = (indent == _logindent) firstglyph = toplevel ? "" : "โ””โ”€" pre = toplevel ? "" : " " println(io, indent, firstglyph, entry.header) l = length(entry.events) for (i,(otheritem,msg)) in enumerate(entry.events) if !isempty(msg) print(io, indent * pre, (i==l ? 'โ””' : 'โ”œ'), 'โ”€') println(io, msg) newindent = indent * pre * (i==l ? " " : "โ”‚ ") else newindent = indent end otheritem โ‰ก nothing && continue recursive || continue if otheritem โˆˆ keys(seen) println(io, newindent, "โ””โ”€", otheritem.header, " see above") continue end seen[otheritem] = true _show(io, rlog, otheritem, newindent, seen, recursive) end end is_julia(graph::Graph, p0::Int) = graph.data.pkgs[p0] == uuid_julia "Check for contradictions in the constraints." function check_constraints(graph::Graph) np = graph.np gconstr = graph.gconstr pkgs = graph.data.pkgs pvers = graph.data.pvers rlog = graph.data.rlog exact = graph.data.rlog.exact id(p0::Int) = pkgID(p0, graph) for p0 = 1:np any(gconstr[p0]) && continue if exact err_msg = "Unsatisfiable requirements detected for package $(logstr(id(p0))):\n" else err_msg = "Resolve failed to satisfy requirements for package $(logstr(id(p0))):\n" end err_msg *= sprint(showlog, rlog, pkgs[p0]) throw(ResolverError(chomp(err_msg))) end return true end """ Propagates current constraints, determining new implicit constraints. Throws an error in case impossible requirements are detected, printing a log trace. """ function propagate_constraints!(graph::Graph, sources::Set{Int} = Set{Int}(); log_events::Bool = true) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr adjdict = graph.adjdict ignored = graph.ignored pkgs = graph.data.pkgs pvers = graph.data.pvers rlog = graph.data.rlog exact = rlog.exact id(p0::Int) = pkgID(pkgs[p0], graph) log_events && log_event_global!(graph, "propagating constraints") # unless otherwise specified, start from packages which # are not allowed to be uninstalled staged = isempty(sources) ? Set{Int}(p0 for p0 = 1:np if !gconstr[p0][end]) : sources seen = copy(staged) while !isempty(staged) staged_next = Set{Int}() for p0 in staged gconstr0 = gconstr[p0] for (j1,p1) in enumerate(gadj[p0]) # if p1 is ignored, the relation between it and all its neighbors # has already been propagated ignored[p1] && continue # we don't propagate to julia (purely to have better error messages) pkgs[p1] == uuid_julia && continue msk = gmsk[p0][j1] # consider the sub-mask with only allowed versions of p0 sub_msk = msk[:,gconstr0] # if an entire row of the sub-mask is false, that version of p1 # is effectively forbidden # (this is just like calling `any` row-wise) added_constr1 = any!(BitVector(undef, spp[p1]), sub_msk) # apply the new constraints, checking for contradictions # (keep the old ones for comparison) gconstr1 = gconstr[p1] old_gconstr1 = copy(gconstr1) gconstr1 .&= added_constr1 # if the new constraints are more restrictive than the # previous ones, record it and propagate them next if gconstr1 โ‰  old_gconstr1 push!(staged_next, p1) log_events && log_event_implicit_req!(graph, p1, added_constr1, p0) elseif p1 โˆ‰ seen push!(staged_next, p1) end if !any(gconstr1) if exact err_msg = "Unsatisfiable requirements detected for package $(logstr(id(p1))):\n" else err_msg = "Resolve failed to satisfy requirements for package $(logstr(id(p1))):\n" end err_msg *= sprint(showlog, rlog, pkgs[p1]) throw(ResolverError(chomp(err_msg))) end end end union!(seen, staged_next) staged = staged_next end return graph end """ Enforce the uninstalled state on all packages that are not reachable from the required ones or from the packages in the `sources` argument. """ function disable_unreachable!(graph::Graph, sources::Set{Int} = Set{Int}()) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr adjdict = graph.adjdict pkgs = graph.data.pkgs log_event_global!(graph, "disabling unreachable nodes") # 2nd argument are packages which are not allowed to be uninstalled staged = union(sources, Set{Int}(p0 for p0 = 1:np if !gconstr[p0][end])) seen = copy(staged) while !isempty(staged) staged_next = Set{Int}() for p0 in staged gconstr0idx = findall(gconstr[p0][1:(end-1)]) for (j1,p1) in enumerate(gadj[p0]) all(gmsk[p0][j1][end,gconstr0idx]) && continue # the package is not required by any of the allowed versions of p0 p1 โˆˆ seen || push!(staged_next, p1) end end union!(seen, staged_next) staged = staged_next end # Force uninstalled state for all unseen packages for p0 = 1:np p0 โˆˆ seen && continue gconstr0 = gconstr[p0] @assert gconstr0[end] fill!(gconstr0, false) gconstr0[end] = true end return graph end function deep_clean!(graph::Graph) np = graph.np spp = graph.spp log_event_global!(graph, "cleaning graph") sumspp = sum(count(graph.gconstr[p0]) for p0 = 1:np) while true gconstr_msk = [trues(spp[p0]) for p0 = 1:np] str_len = 0 for p0 = 1:np, v0 in findall(graph.gconstr[p0]) print("\r" * " "^str_len * "\r") msg = "> $p0 / $np" print(msg) str_len = length(msg) push_snapshot!(graph) fill!(graph.gconstr[p0], false) graph.gconstr[p0][v0] = true try propagate_constraints!(graph, Set{Int}([p0]), log_events = false) catch err err isa ResolverError || rethrow() gconstr_msk[p0][v0] = false end pop_snapshot!(graph) end println() affected = Int[] for p0 = 1:np gconstr0 = graph.gconstr[p0] old_gconstr0 = copy(gconstr0) gconstr0 .&= gconstr_msk[p0] if old_gconstr0 โ‰  gconstr0 push!(affected, p0) #TODO : log event end if !any(gconstr0) # TODO : what should we do here?? # throw(ResolverError("aaaaaaaaaaaaaahhhhhhhh")) # XXX end end println("> affected = $(length(affected))") isempty(affected) && break end sumspp_new = sum(count(graph.gconstr[p0]) for p0 = 1:np) log_event_global!(graph, "cleaned graph, stats (total n. of states): before = $(sumspp) after = $(sumspp_new) diff = $(sumspp-sumspp_new)") return graph end """ Reduce the number of versions in the graph by putting all the versions of a package that behave identically into equivalence classes, keeping only the highest version of the class as representative. """ function compute_eq_classes!(graph::Graph) log_event_global!(graph, "computing version equivalence classes") np = graph.np sumspp = sum(graph.spp) for p0 = 1:np build_eq_classes1!(graph, p0) end log_event_global!(graph, "computed version equivalence classes, stats (total n. of states): before = $(sumspp) after = $(sum(graph.spp))") @assert check_consistency(graph) return graph end function build_eq_classes1!(graph::Graph, p0::Int) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr adjdict = graph.adjdict data = graph.data pkgs = data.pkgs pvers = data.pvers vdict = data.vdict eq_classes = data.eq_classes rlog = data.rlog # concatenate all the constraints; the columns of the # result encode the behavior of each version cmat = vcat(BitMatrix(permutedims(gconstr[p0])), gmsk[p0]...) cvecs = [cmat[:,v0] for v0 = 1:spp[p0]] # find unique behaviors repr_vecs = unique(cvecs) # number of equivalent classes neq = length(repr_vecs) neq == spp[p0] && return # nothing to do here # group versions into sets that behave identically eq_sets = [Set{Int}(v0 for v0 in 1:spp[p0] if cvecs[v0] == rvec) for rvec in repr_vecs] sort!(eq_sets, by=maximum) # each set is represented by its highest-valued member repr_vers = map(maximum, eq_sets) # the last representative must always be the uninstalled state @assert repr_vers[end] == spp[p0] # update equivalence classes eq_vn(v0) = (v0 == spp[p0] ? nothing : pvers[p0][v0]) eq_classes0 = eq_classes[pkgs[p0]] for (v0,rvs) in zip(repr_vers, eq_sets) @assert v0 โˆˆ rvs vn0 = eq_vn(v0) for v1 in rvs v1 == v0 && continue vn1 = eq_vn(v1) @assert vn1 โ‰ข nothing union!(eq_classes0[vn0], eq_classes0[vn1]) delete!(eq_classes0, vn1) end end # reduce the constraints and the interaction matrices spp[p0] = neq gconstr[p0] = gconstr[p0][repr_vers] for (j1,p1) in enumerate(gadj[p0]) gmsk[p0][j1] = gmsk[p0][j1][:,repr_vers] j0 = adjdict[p0][p1] gmsk[p1][j0] = gmsk[p1][j0][repr_vers,:] end # reduce/rebuild version dictionaries pvers[p0] = pvers[p0][repr_vers[1:(end-1)]] vdict[p0] = Dict(vn => i for (i,vn) in enumerate(pvers[p0])) # put a record in the log log_event_eq_classes!(graph, p0) return end function compute_eq_classes_soft!(graph::Graph; log_events::Bool = true) log_events && log_event_global!(graph, "computing version equivalence classes") np = graph.np np == 0 && return graph ignored = graph.ignored gconstr = graph.gconstr sumspp = sum(count(gconstr[p0]) for p0 = 1:np) for p0 = 1:np ignored[p0] && continue build_eq_classes_soft1!(graph, p0) end sumspp_new = sum(count(gconstr[p0]) for p0 = 1:np) log_events && log_event_global!(graph, "computed version equivalence classes, stats (total n. of states): before = $(sumspp) after = $(sumspp_new) diff = $(sumspp_new-sumspp)") @assert check_consistency(graph) return graph end function build_eq_classes_soft1!(graph::Graph, p0::Int) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr ignored = graph.ignored # concatenate all the constraints; the columns of the # result encode the behavior of each version gadj0 = gadj[p0] gmsk0 = gmsk[p0] gconstr0 = gconstr[p0] eff_spp0 = count(gconstr0) cvecs = BitVector[vcat(BitVector(), (gmsk0[j1][gconstr[gadj0[j1]],v0] for j1 = 1:length(gadj0) if !ignored[gadj0[j1]])...) for v0 in findall(gconstr0)] @assert length(cvecs) == eff_spp0 # find unique behaviors repr_vecs = unique(cvecs) # number of equivalent classes neq = length(repr_vecs) neq == eff_spp0 && return # nothing to do here # group versions into sets that behave identically # each set is represented by its highest-valued member repr_vers = sort!(Int[findlast(isequal(repr_vecs[w0]), cvecs) for w0 = 1:neq]) @assert all(>(0), repr_vers) @assert repr_vers[end] == eff_spp0 # convert the version numbers into the original numbering repr_vers = findall(gconstr0)[repr_vers] @assert all(gconstr0[repr_vers]) # disable the other versions by introducing additional constraints fill!(gconstr0, false) gconstr0[repr_vers] .= true return end function update_ignored!(graph::Graph) np = graph.np gconstr = graph.gconstr ignored = graph.ignored for p0 = 1:np ignored[p0] = (count(gconstr[p0]) == 1) end return graph end """ Prune away fixed and unnecessary packages, and the disallowed versions for the remaining packages. """ function prune_graph!(graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gmsk = graph.gmsk gconstr = graph.gconstr adjdict = graph.adjdict req_inds = graph.req_inds fix_inds = graph.fix_inds ignored = graph.ignored data = graph.data pkgs = data.pkgs pdict = data.pdict pvers = data.pvers vdict = data.vdict pruned = data.pruned rlog = data.rlog # We will remove all packages that only have one allowed state # (includes fixed packages and forbidden packages) pkg_mask = BitVector(count(gconstr[p0]) โ‰  1 for p0 = 1:np) new_np = count(pkg_mask) # a map that translates the new index โˆˆ 1:new_np into its # corresponding old index โˆˆ 1:np old_idx = findall(pkg_mask) # the reverse of the above new_idx = Dict{Int,Int}() for new_p0 = 1:new_np new_idx[old_idx[new_p0]] = new_p0 end # Update requirement indices new_req_inds = Set{Int}() for p0 in req_inds pkg_mask[p0] || continue push!(new_req_inds, new_idx[p0]) end # Fixed packages will all be pruned new_fix_inds = Set{Int}() for p0 in fix_inds @assert !pkg_mask[p0] end # Record which packages we are going to prune for p0 in findall(.~(pkg_mask)) # Find the version s0 = findfirst(gconstr[p0]) # We don't record fixed packages p0 โˆˆ fix_inds && (@assert s0 โ‰  spp[p0]; continue) p0 โˆˆ req_inds && @assert s0 โ‰  spp[p0] log_event_pruned!(graph, p0, s0) # We don't record as pruned packages that are not going to be installed s0 == spp[p0] && continue @assert !haskey(pruned, pkgs[p0]) pruned[pkgs[p0]] = pvers[p0][s0] end # Update packages records new_pkgs = pkgs[pkg_mask] new_pdict = Dict(new_pkgs[new_p0]=>new_p0 for new_p0 = 1:new_np) new_ignored = ignored[pkg_mask] empty!(graph.solve_stack) # For each package (unless it's going to be pruned) we will remove all # versions that aren't allowed (but not the "uninstalled" state) function keep_vers(new_p0) p0 = old_idx[new_p0] return BitVector((v0 == spp[p0]) | gconstr[p0][v0] for v0 = 1:spp[p0]) end vers_mask = [keep_vers(new_p0) for new_p0 = 1:new_np] # Update number of states per package new_spp = Int[count(vers_mask[new_p0]) for new_p0 = 1:new_np] # Update versions maps function compute_pvers(new_p0) p0 = old_idx[new_p0] pvers0 = pvers[p0] vmsk0 = vers_mask[new_p0] return pvers0[vmsk0[1:(end-1)]] end new_pvers = [compute_pvers(new_p0) for new_p0 = 1:new_np] # explicitly writing out the following loop since the generator equivalent caused type inference failure new_vdict = Vector{Dict{VersionNumber, Int}}(undef, length(new_pvers)) for new_p0 in eachindex(new_vdict) new_vdict[new_p0] = Dict(vn => v0 for (v0,vn) in enumerate(new_pvers[new_p0])) end # The new constraints are all going to be `true`, except possibly # for the "uninstalled" state, which we copy over from the old function compute_gconstr(new_p0) p0 = old_idx[new_p0] new_gconstr0 = trues(new_spp[new_p0]) new_gconstr0[end] = gconstr[p0][end] return new_gconstr0 end new_gconstr = [compute_gconstr(new_p0) for new_p0 = 1:new_np] # Recreate the graph adjacency list, skipping some packages new_gadj = [Int[] for new_p0 = 1:new_np] new_adjdict = [Dict{Int,Int}() for new_p0 = 1:new_np] for new_p0 = 1:new_np, (j1,p1) in enumerate(gadj[old_idx[new_p0]]) pkg_mask[p1] || continue new_p1 = new_idx[p1] new_j0 = get(new_adjdict[new_p1], new_p0, length(new_gadj[new_p0]) + 1) new_j1 = get(new_adjdict[new_p0], new_p1, length(new_gadj[new_p1]) + 1) @assert (new_j0 > length(new_gadj[new_p0]) && new_j1 > length(new_gadj[new_p1])) || (new_j0 โ‰ค length(new_gadj[new_p0]) && new_j1 โ‰ค length(new_gadj[new_p1])) new_j0 > length(new_gadj[new_p0]) || continue push!(new_gadj[new_p0], new_p1) push!(new_gadj[new_p1], new_p0) @assert new_j0 == length(new_gadj[new_p0]) @assert new_j1 == length(new_gadj[new_p1]) new_adjdict[new_p1][new_p0] = new_j0 new_adjdict[new_p0][new_p1] = new_j1 end # Recompute compatibility masks on the new adjacency list, and filtering out some versions function compute_gmsk(new_p0, new_j0) p0 = old_idx[new_p0] new_p1 = new_gadj[new_p0][new_j0] p1 = old_idx[new_p1] j0 = adjdict[p1][p0] return gmsk[p0][j0][vers_mask[new_p1],vers_mask[new_p0]] end new_gmsk = [[compute_gmsk(new_p0, new_j0) for new_j0 = 1:length(new_gadj[new_p0])] for new_p0 = 1:new_np] # Reduce log pool (the other items are still reachable through rlog.init) rlog.pool = Dict(p=>rlog.pool[p] for p in new_pkgs) # Done log_event_global!(graph, "pruned graph โ€” stats (n. of packages, mean connectivity): before = ($np,$(sum(spp)/length(spp))) after = ($new_np,$(sum(new_spp)/length(new_spp)))") # Replace old data with new data.pkgs = new_pkgs data.np = new_np data.spp = new_spp data.pdict = new_pdict data.pvers = new_pvers data.vdict = new_vdict # Notes: # * uuid_to_name, eq_classes are unchanged # * pruned and rlog were updated in-place # Replace old structures with new ones graph.gadj = new_gadj graph.gmsk = new_gmsk graph.gconstr = new_gconstr graph.adjdict = new_adjdict graph.req_inds = new_req_inds graph.fix_inds = new_fix_inds graph.ignored = new_ignored graph.spp = new_spp graph.np = new_np # Note: solve_stack was emptied in-place @assert check_consistency(graph) return graph end """ Simplifies the graph by propagating constraints, disabling unreachable versions, pruning and grouping versions into equivalence classes. """ function simplify_graph!(graph::Graph, sources::Set{Int} = Set{Int}(); clean_graph::Bool = false) propagate_constraints!(graph) disable_unreachable!(graph, sources) clean_graph && deep_clean!(graph) prune_graph!(graph) compute_eq_classes!(graph) return graph end function simplify_graph_soft!(graph::Graph, sources::Set{Int} = Set{Int}(); log_events = true) propagate_constraints!(graph, sources, log_events = log_events) update_ignored!(graph) compute_eq_classes_soft!(graph, log_events = log_events) update_ignored!(graph) return graph end u���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Resolve/maxsum.jl/8������# This file is a part of Julia. License is MIT: https://julialang.org/license # An exception type used internally to signal that an unsatisfiable # constraint was detected struct UnsatError <: Exception p0::Int end # Some parameters to drive the decimation process mutable struct MaxSumParams dec_interval::Int # number of iterations between decimations dec_fraction::Float64 # fraction of nodes to decimate at every decimation # step function MaxSumParams() accuracy = parse(Int, get(ENV, "JULIA_PKG_RESOLVE_ACCURACY", # Allow for `JULIA_PKGRESOLVE_ACCURACY` for backward # compatibility with Julia v1.7- get(ENV, "JULIA_PKGRESOLVE_ACCURACY", "1"))) accuracy > 0 || error("JULIA_PKG_RESOLVE_ACCURACY must be > 0") dec_interval = accuracy * 5 dec_fraction = 0.05 / accuracy return new(dec_interval, dec_fraction) end end # Messages has the cavity messages and the total fields, and # gets updated iteratively (and occasionally decimated) until # convergence mutable struct Messages # cavity incoming messages: for each package p0, # for each neighbor p1 of p0, # msg[p0][p1] is a vector of length spp[p0] # messages are normalized (i.e. the max is always 0) msg::Vector{Vector{Field}} # overall fields: for each package p0, # fld[p0] is a vector of length spp[p0] # fields are not normalized fld::Vector{Field} # backup of the initial value of fld, to be used when resetting initial_fld::Vector{Field} function Messages(graph::Graph) np = graph.np spp = graph.spp gadj = graph.gadj gconstr = graph.gconstr req_inds = graph.req_inds ignored = graph.ignored pvers = graph.data.pvers pdict = graph.data.pdict ## generate wveights (v0 == spp[p0] is the "uninstalled" state) vweight = [[VersionWeight(v0 < spp[p0] ? pvers[p0][v0] : v"0") for v0 = 1:spp[p0]] for p0 = 1:np] # external fields: favor newest versions over older, and no-version over all; # explicit requirements use level l1 instead of l2 fv(p0, v0) = p0 โˆˆ req_inds ? FieldValue(0, vweight[p0][v0], zero(VersionWeight), (v0==spp[p0])) : FieldValue(0, zero(VersionWeight), vweight[p0][v0], (v0==spp[p0])) fld = [[fv(p0, v0) for v0 = 1:spp[p0]] for p0 = 1:np] initial_fld = [copy(f0) for f0 in fld] # allocate cavity messages msg = [[Field(undef, spp[p0]) for j1 = 1:length(gadj[p0])] for p0 = 1:np] msgs = new(msg, fld, initial_fld) reset_messages!(msgs, graph) return msgs end end # enforce constraints and normalize fields; # zero out all cavity messages function reset_messages!(msgs::Messages, graph::Graph) msg = msgs.msg fld = msgs.fld initial_fld = msgs.initial_fld np = graph.np spp = graph.spp gconstr = graph.gconstr ignored = graph.ignored for p0 = 1:np ignored[p0] && continue map(m->fill!(m, zero(FieldValue)), msg[p0]) copyto!(fld[p0], initial_fld[p0]) gconstr0 = gconstr[p0] for v0 = 1:spp[p0] gconstr0[v0] || (fld[p0][v0] = FieldValue(-1)) end fld[p0] .-= maximum(fld[p0]) end return msgs end mutable struct SolutionTrace # the solution is built progressively by a decimation process solution::Vector{Int} num_nondecimated::Int best::Vector{Int} staged::Union{Tuple{Int,Int},Nothing} function SolutionTrace(graph::Graph) np = graph.np solution = zeros(Int, np) num_nondecimated = np best = zeros(Int, np) staged = nothing return new(solution, num_nondecimated, best, staged) end end function update_solution!(strace::SolutionTrace, graph::Graph) np = graph.np ignored = graph.ignored gconstr = graph.gconstr solution = strace.solution best = strace.best nnd = np fill!(solution, 0) for p0 in findall(ignored) s0 = findfirst(gconstr[p0]) solution[p0] = s0 nnd -= 1 end strace.num_nondecimated = nnd if nnd โ‰ค sum(best .== 0) copyto!(best, solution) strace.staged = nothing return true else return false end end # This is the core of the max-sum solver: # for a given node p0 (i.e. a package) updates all # input cavity messages and fields of its neighbors function update!(p0::Int, graph::Graph, msgs::Messages) gadj = graph.gadj gmsk = graph.gmsk adjdict = graph.adjdict ignored = graph.ignored spp = graph.spp np = graph.np cavfld = graph.cavfld newmsg = graph.newmsg diff = graph.diff msg = msgs.msg fld = msgs.fld maxdiff = zero(FieldValue) gadj0 = gadj[p0] msg0 = msg[p0] fld0 = fld[p0] spp0 = spp[p0] adjdict0 = adjdict[p0] # iterate over all neighbors of p0 for j0 in 1:length(gadj0) p1 = gadj0[j0] ignored[p1] && continue j1 = adjdict0[p1] #@assert j0 == adjdict[p1][p0] bm1 = gmsk[p1][j1] spp1 = spp[p1] msg1 = msg[p1] # compute the output cavity field p0->p1 resize!(cavfld, length(fld0)) cavfld .= fld0 .- msg0[j0] # keep the old input cavity message p0->p1 oldmsg = msg1[j1] # init the new message to minus infinity resize!(newmsg, spp1) fill!(newmsg, FieldValue(-1)) # compute the new message by passing cavfld # through the constraint encoded in the bitmask # (equivalent to: # newmsg = [maximum(cavfld[bm1[:,v1]]) for v1 = 1:spp1] # ) # This is hot code for the resolver @inbounds for v1 = 1:spp1, v0 = 1:spp0 bm1[v0, v1] || continue newmsg[v1] = max(newmsg[v1], cavfld[v0]) end m = maximum(newmsg) validmax(m) || return Unsat(p0) # No state available without violating some # hard constraint # normalize the new message @inbounds for i in 1:length(newmsg) newmsg[i] -= m end resize!(diff, spp1) diff .= newmsg .- oldmsg maxdiff = max(maxdiff, maximum(abs, diff)) # update the field of p1 fld[p1] .+= diff # put the newly computed message in place copy!(msg1[j1], newmsg) end return maxdiff end # A simple shuffling machinery for the update order in iterate!() # (wouldn't pass any random quality test but it's arguably enough) mutable struct NodePerm p::Vector{Int} step::Int64 NodePerm(np::Integer) = new(collect(1:np), 1) end function Random.shuffle!(perm::NodePerm) p = perm.p for j = length(p):-1:2 k = perm.step % j + 1 p[j], p[k] = p[k], p[j] perm.step += isodd(j) ? 1 : k end #@assert isperm(p) end Base.iterate(perm::NodePerm, state...) = iterate(perm.p, state...) # Call update for all nodes (i.e. packages) in # random order function iterate!(graph::Graph, msgs::Messages, perm::NodePerm) maxdiff = zero(FieldValue) shuffle!(perm) for p0 in perm graph.ignored[p0] && continue maxdiff0 = update!(p0, graph, msgs) if maxdiff0 isa Unsat return maxdiff0 end maxdiff = max(maxdiff, maxdiff0) end return maxdiff end function decimate1!(p0::Int, graph::Graph, strace::SolutionTrace, msgs::Messages) solution = strace.solution fld = msgs.fld adjdict = graph.adjdict gmsk = graph.gmsk gconstr = graph.gconstr @assert solution[p0] == 0 @assert !graph.ignored[p0] fld0 = fld[p0] s0 = argmax(fld0) # only do the decimation if it is consistent with # the constraints... gconstr[p0][s0] || return 0 # ...and with the previously decimated nodes for p1 in findall(solution .> 0) haskey(adjdict[p0], p1) || continue s1 = solution[p1] j1 = adjdict[p0][p1] gmsk[p1][j1][s0,s1] || return 0 end solution[p0] = s0 strace.num_nondecimated -= 1 return s0 end function decimate!(graph::Graph, strace::SolutionTrace, msgs::Messages, n::Integer) np = graph.np gconstr = graph.gconstr ignored = graph.ignored fld = msgs.fld @assert n โ‰ฅ 1 dtrace = Tuple{Int,Int}[] dec = 0 fldorder = sort(findall(.!(ignored)), by=p0->secondmax(fld[p0], gconstr[p0])) for p0 in fldorder s0 = decimate1!(p0, graph, strace, msgs) s0 == 0 && continue push!(dtrace, (p0,s0)) dec += 1 dec == n && break end return dtrace end function clean_forbidden!(graph::Graph, msgs::Messages) np = graph.np gconstr = graph.gconstr ignored = graph.ignored fld = msgs.fld affected = Tuple{Int,Int}[] for p0 = 1:np ignored[p0] && continue fld0 = fld[p0] gconstr0 = gconstr[p0] for v0 in findall(gconstr0) validmax(fld0[v0]) && continue push!(affected, (p0,v0)) end end return affected end # Iterative solver: run iterate!() until convergence # (occasionally calling decimate()) function maxsum(graph::Graph) params = MaxSumParams() perm = NodePerm(graph.np) strace = SolutionTrace(graph) msgs = Messages(graph) push_snapshot!(graph) # gconstr_sav = graph.gconstr # ignored_sav = graph.ignored ok = converge!(graph, msgs, strace, perm, params) # @assert graph.gconstr โ‰ก gconstr_sav # @assert graph.ignored โ‰ก ignored_sav pop_snapshot!(graph) if ok @assert strace.best == strace.solution @assert strace.num_nondecimated == 0 @assert all(strace.solution .> 0) @assert strace.staged โ‰ก nothing else @assert strace.staged โ‰ข nothing end return ok, strace.best, strace.staged end function try_simplify_graph_soft!(graph, sources) try simplify_graph_soft!(graph, sources, log_events = false) catch err err isa ResolverError || rethrow() return false end return true end struct Unsat p0::Int end function converge!(graph::Graph, msgs::Messages, strace::SolutionTrace, perm::NodePerm, params::MaxSumParams) is_best_sofar = update_solution!(strace, graph) # this is the base of the recursion: the case when # the solver has succeeded in decimating everything strace.num_nondecimated == 0 && return true reset_messages!(msgs, graph) # perform some maxsum iterations, then decimate one node. # If failure happens during this process, we bail (return false) it = 0 for it = 1:params.dec_interval maxdiff = iterate!(graph, msgs, perm) if maxdiff isa Unsat if is_best_sofar p0 = maxdiff.p0 s0 = findlast(graph.gconstr[p0]) strace.staged = (p0, s0) end return false end maxdiff == zero(FieldValue) && break end # if maxsum has found some forbidden states, remove # them and propagate the effect affected = clean_forbidden!(graph, msgs) isempty(affected) && @goto decimate sources = Set{Int}() for (p0,v0) in affected graph.gconstr[p0][v0] = false push!(sources, p0) end if !try_simplify_graph_soft!(graph, sources) # found an implicit contradiction is_best_sofar && (strace.staged = first(affected)) return false end return converge!(graph, msgs, strace, perm, params) @label decimate ndec = max(1, round(Int, params.dec_fraction * strace.num_nondecimated)) dtrace = decimate!(graph, strace, msgs, ndec) if isempty(dtrace) # decimation has failed, all candidate states are forbidden # (which shouldn't really happen, this is a failsafe) if is_best_sofar # pick the first decimation candidate smx(p1) = secondmax(msgs.fld[p1], graph.gconstr[p1]) p0 = reduce((p1,p2)->(smx(p1)โ‰คsmx(p2) ? p1 : p2), findall(.!(graph.ignored))) s0 = argmax(fld[p0]) strace.staged = dec_firstcandidate(graph, msgs) end return false end while true # decimation has succeeded, at least nominally. # We need to propagate its effects and check for contradictions lentr = length(dtrace) if lentr == 1 && is_best_sofar strace.staged = dtrace[1] end push_snapshot!(graph) # info("setting dtrace=$dtrace") for (p0,s0) in dtrace @assert !graph.ignored[p0] @assert graph.gconstr[p0][s0] fill!(graph.gconstr[p0], false) graph.gconstr[p0][s0] = true graph.ignored[p0] = true end # if decimation has produced an implicit contradiction, backtrack try_simplify_graph_soft!(graph, Set{Int}(first.(dtrace))) || @goto backtrack # otherwise, keep going... converge!(graph, msgs, strace, perm, params) && (pop_snapshot!(graph); return true) @label backtrack # warn("reverting dtrace=$dtrace") # if we're here, the last decimation step has been proven to lead # to an unsat situation at some point, we need to roll it back # revert the state of the constraints pop_snapshot!(graph) lentr == 1 && break # halve the dtrace deleteat!(dtrace, ((lentrรท2)+1):lentr) end @assert length(dtrace) == 1 p0, s0 = dtrace[1] is_best_sofar && @assert strace.staged โ‰ข nothing # forbid the state used in the last attempt # (note that we're working on the "entry" snapshot here!) graph.gconstr[p0][s0] = false # check if we have finished all available possibilities any(graph.gconstr[p0]) || return false # if neither the s0 state nor its negation are valid, give up try_simplify_graph_soft!(graph, Set{Int}([p0])) || return false # keep going, with one possible state less... return converge!(graph, msgs, strace, perm, params) end l���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Types.jlทซ������# This file is a part of Julia. License is MIT: https://julialang.org/license module Types using UUIDs using Random using Dates import LibGit2 import REPL import Base.string using REPL.TerminalMenus using TOML import ..Pkg, ..Registry import ..Pkg: GitTools, depots, depots1, logdir, set_readonly, safe_realpath, pkg_server, stdlib_dir, stdlib_path, isurl, stderr_f, RESPECT_SYSIMAGE_VERSIONS import Base.BinaryPlatforms: Platform using ..Pkg.Versions import FileWatching import Base: SHA1 using SHA export UUID, SHA1, VersionRange, VersionSpec, PackageSpec, PackageEntry, EnvCache, Context, GitRepo, Context!, Manifest, Project, err_rep, PkgError, pkgerror, PkgPrecompileError, has_name, has_uuid, is_stdlib, is_or_was_stdlib, stdlib_version, is_unregistered_stdlib, stdlibs, write_env, write_env_usage, parse_toml, project_resolve!, project_deps_resolve!, manifest_resolve!, registry_resolve!, stdlib_resolve!, handle_repos_develop!, handle_repos_add!, ensure_resolved, registered_name, manifest_info, read_project, read_package, read_manifest, PackageMode, PKGMODE_MANIFEST, PKGMODE_PROJECT, PKGMODE_COMBINED, UpgradeLevel, UPLEVEL_FIXED, UPLEVEL_PATCH, UPLEVEL_MINOR, UPLEVEL_MAJOR, PreserveLevel, PRESERVE_ALL_INSTALLED, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_TIERED, PRESERVE_TIERED_INSTALLED, PRESERVE_NONE, projectfile_path, manifestfile_path # Load in data about historical stdlibs include("HistoricalStdlibs.jl") deepcopy_toml(x) = x function deepcopy_toml(@nospecialize(x::Vector)) d = similar(x) for (i, v) in enumerate(x) d[i] = deepcopy_toml(v) end return d end function deepcopy_toml(x::Dict{String, Any}) d = Dict{String, Any}() sizehint!(d, length(x)) for (k, v) in x d[k] = deepcopy_toml(v) end return d end # See loading.jl const TOML_CACHE = Base.TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}()) const TOML_LOCK = ReentrantLock() # Some functions mutate the returning Dict so return a copy of the cached value here parse_toml(toml_file::AbstractString) = Base.invokelatest(deepcopy_toml, Base.parsed_toml(toml_file, TOML_CACHE, TOML_LOCK))::Dict{String, Any} ################# # Pkg Error # ################# struct PkgError <: Exception msg::String end pkgerror(msg::String...) = throw(PkgError(join(msg))) Base.showerror(io::IO, err::PkgError) = print(io, err.msg) ################# # Pkg Precompile Error # ################# struct PkgPrecompileError <: Exception msg::String end Base.showerror(io::IO, err::PkgPrecompileError) = print(io, err.msg) # This needs a show method to make `julia> err` show nicely Base.show(io::IO, err::PkgPrecompileError) = print(io, "PkgPrecompileError: ", err.msg) ############### # PackageSpec # ############### @enum(UpgradeLevel, UPLEVEL_FIXED, UPLEVEL_PATCH, UPLEVEL_MINOR, UPLEVEL_MAJOR) @enum(PreserveLevel, PRESERVE_ALL_INSTALLED, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_TIERED, PRESERVE_TIERED_INSTALLED, PRESERVE_NONE) @enum(PackageMode, PKGMODE_PROJECT, PKGMODE_MANIFEST, PKGMODE_COMBINED) const VersionTypes = Union{VersionNumber,VersionSpec,UpgradeLevel} Base.@kwdef mutable struct GitRepo source::Union{Nothing,String} = nothing rev::Union{Nothing,String} = nothing subdir::Union{String, Nothing} = nothing end Base.:(==)(r1::GitRepo, r2::GitRepo) = r1.source == r2.source && r1.rev == r2.rev && r1.subdir == r2.subdir mutable struct PackageSpec name::Union{Nothing,String} uuid::Union{Nothing,UUID} version::Union{Nothing,VersionTypes,String} tree_hash::Union{Nothing,SHA1} repo::GitRepo path::Union{Nothing,String} pinned::Bool # used for input only url::Union{Nothing, String} rev::Union{Nothing, String} subdir::Union{Nothing, String} end function PackageSpec(; name::Union{Nothing,AbstractString} = nothing, uuid::Union{Nothing,UUID,AbstractString} = nothing, version::Union{Nothing,VersionTypes,AbstractString} = VersionSpec(), tree_hash::Union{Nothing,SHA1} = nothing, repo::GitRepo = GitRepo(), path::Union{Nothing,AbstractString} = nothing, pinned::Bool = false, url = nothing, rev = nothing, subdir = nothing) uuid = uuid === nothing ? nothing : UUID(uuid) return PackageSpec(name, uuid, version, tree_hash, repo, path, pinned, url, rev, subdir) end PackageSpec(name::AbstractString) = PackageSpec(;name=name)::PackageSpec PackageSpec(name::AbstractString, uuid::UUID) = PackageSpec(;name=name, uuid=uuid)::PackageSpec PackageSpec(name::AbstractString, version::VersionTypes) = PackageSpec(;name=name, version=version)::PackageSpec PackageSpec(n::AbstractString, u::UUID, v::VersionTypes) = PackageSpec(;name=n, uuid=u, version=v)::PackageSpec function Base.:(==)(a::PackageSpec, b::PackageSpec) return a.name == b.name && a.uuid == b.uuid && a.version == b.version && a.tree_hash == b.tree_hash && a.repo == b.repo && a.path == b.path && a.pinned == b.pinned end function err_rep(pkg::PackageSpec) x = pkg.name !== nothing && pkg.uuid !== nothing ? x = "$(pkg.name) [$(string(pkg.uuid)[1:8])]" : pkg.name !== nothing ? pkg.name : pkg.uuid !== nothing ? string(pkg.uuid)[1:8] : pkg.repo.source return "`$x`" end has_name(pkg::PackageSpec) = pkg.name !== nothing has_uuid(pkg::PackageSpec) = pkg.uuid !== nothing isresolved(pkg::PackageSpec) = pkg.uuid !== nothing && pkg.name !== nothing function Base.show(io::IO, pkg::PackageSpec) vstr = repr(pkg.version) f = Pair{String, Any}[] pkg.name !== nothing && push!(f, "name" => pkg.name) pkg.uuid !== nothing && push!(f, "uuid" => pkg.uuid) pkg.tree_hash !== nothing && push!(f, "tree_hash" => pkg.tree_hash) pkg.path !== nothing && push!(f, "path" => pkg.path) pkg.url !== nothing && push!(f, "url" => pkg.url) pkg.rev !== nothing && push!(f, "rev" => pkg.rev) pkg.subdir !== nothing && push!(f, "subdir" => pkg.subdir) pkg.pinned && push!(f, "pinned" => pkg.pinned) push!(f, "version" => (vstr == "VersionSpec(\"*\")" ? "*" : vstr)) if pkg.repo.source !== nothing push!(f, "repo/source" => string("\"", pkg.repo.source::String, "\"")) end if pkg.repo.rev !== nothing push!(f, "repo/rev" => pkg.repo.rev) end if pkg.repo.subdir !== nothing push!(f, "repo/subdir" => pkg.repo.subdir) end print(io, "PackageSpec(\n") for (field, value) in f print(io, " ", field, " = ", string(value)::String, "\n") end print(io, ")") end ############ # EnvCache # ############ function projectfile_path(env_path::String; strict=false) for name in Base.project_names maybe_file = joinpath(env_path, name) isfile(maybe_file) && return maybe_file end return strict ? nothing : joinpath(env_path, "Project.toml") end function manifestfile_path(env_path::String; strict=false) man_names = @static Base.manifest_names isa Tuple ? Base.manifest_names : Base.manifest_names() for name in man_names maybe_file = joinpath(env_path, name) isfile(maybe_file) && return maybe_file end if strict return nothing else n_names = length(man_names) if n_names == 1 return joinpath(env_path, only(man_name)) else project = basename(projectfile_path(env_path)::String) idx = findfirst(x -> x == project, Base.project_names) @assert idx !== nothing idx = idx + (n_names - length(Base.project_names)) # ignore custom name if present return joinpath(env_path, man_names[idx]) end end end function find_project_file(env::Union{Nothing,String}=nothing) project_file = nothing if env isa Nothing project_file = Base.active_project() project_file === nothing && pkgerror("no active project") elseif startswith(env, '@') project_file = Base.load_path_expand(env) project_file === nothing && pkgerror("package environment does not exist: $env") elseif env isa String if isdir(env) isempty(readdir(env)) || pkgerror("environment is a package directory: $env") project_file = joinpath(env, Base.project_names[end]) else project_file = endswith(env, ".toml") ? abspath(env) : abspath(env, Base.project_names[end]) end end @assert project_file isa String && (isfile(project_file) || !ispath(project_file) || isdir(project_file) && isempty(readdir(project_file))) return Pkg.safe_realpath(project_file) end Base.@kwdef mutable struct Compat val::VersionSpec str::String end Base.:(==)(t1::Compat, t2::Compat) = t1.val == t2.val Base.hash(t::Compat, h::UInt) = hash(t.val, h) Base.@kwdef mutable struct Project other::Dict{String,Any} = Dict{String,Any}() # Fields name::Union{String, Nothing} = nothing uuid::Union{UUID, Nothing} = nothing version::Union{VersionTypes, Nothing} = nothing manifest::Union{String, Nothing} = nothing # Sections deps::Dict{String,UUID} = Dict{String,UUID}() # deps that are also in weakdeps for backwards compat # we do not store them in deps because we want to ignore them # but for writing out the project file we need to remember them: _deps_weak::Dict{String,UUID} = Dict{String,UUID}() weakdeps::Dict{String,UUID} = Dict{String,UUID}() exts::Dict{String,Union{Vector{String}, String}} = Dict{String,String}() extras::Dict{String,UUID} = Dict{String,UUID}() targets::Dict{String,Vector{String}} = Dict{String,Vector{String}}() compat::Dict{String,Compat} = Dict{String,Compat}() end Base.:(==)(t1::Project, t2::Project) = all(x -> (getfield(t1, x) == getfield(t2, x))::Bool, fieldnames(Project)) Base.hash(t::Project, h::UInt) = foldr(hash, [getfield(t, x) for x in fieldnames(Project)], init=h) # only hash the deps and compat fields as they are the only fields that affect a resolve function project_resolve_hash(t::Project) iob = IOBuffer() foreach(((name, uuid),) -> println(iob, name, "=", uuid), sort!(collect(t.deps); by=first)) foreach(((name, compat),) -> println(iob, name, "=", compat.val), sort!(collect(t.compat); by=first)) return bytes2hex(sha1(seekstart(iob))) end Base.@kwdef mutable struct PackageEntry name::Union{String,Nothing} = nothing version::Union{VersionNumber,Nothing} = nothing path::Union{String,Nothing} = nothing pinned::Bool = false repo::GitRepo = GitRepo() tree_hash::Union{Nothing,SHA1} = nothing deps::Dict{String,UUID} = Dict{String,UUID}() weakdeps::Dict{String,UUID} = Dict{String,UUID}() exts::Dict{String,Union{Vector{String}, String}} = Dict{String,String}() uuid::Union{Nothing, UUID} = nothing other::Union{Dict,Nothing} = nothing end Base.:(==)(t1::PackageEntry, t2::PackageEntry) = t1.name == t2.name && t1.version == t2.version && t1.path == t2.path && t1.pinned == t2.pinned && t1.repo == t2.repo && t1.tree_hash == t2.tree_hash && t1.deps == t2.deps && t1.weakdeps == t2.weakdeps && t1.exts == t2.exts && t1.uuid == t2.uuid # omits `other` Base.hash(x::PackageEntry, h::UInt) = foldr(hash, [x.name, x.version, x.path, x.pinned, x.repo, x.tree_hash, x.deps, x.weakdeps, x.exts, x.uuid], init=h) # omits `other` Base.@kwdef mutable struct Manifest julia_version::Union{Nothing,VersionNumber} = nothing # only set to VERSION when resolving manifest_format::VersionNumber = v"2.0.0" deps::Dict{UUID,PackageEntry} = Dict{UUID,PackageEntry}() other::Dict{String,Any} = Dict{String,Any}() end Base.:(==)(t1::Manifest, t2::Manifest) = all(x -> (getfield(t1, x) == getfield(t2, x))::Bool, fieldnames(Manifest)) Base.hash(m::Manifest, h::UInt) = foldr(hash, [getfield(m, x) for x in fieldnames(Manifest)], init=h) Base.getindex(m::Manifest, i_or_key) = getindex(m.deps, i_or_key) Base.get(m::Manifest, key, default) = get(m.deps, key, default) Base.setindex!(m::Manifest, i_or_key, value) = setindex!(m.deps, i_or_key, value) Base.iterate(m::Manifest) = iterate(m.deps) Base.iterate(m::Manifest, i::Int) = iterate(m.deps, i) Base.length(m::Manifest) = length(m.deps) Base.empty!(m::Manifest) = empty!(m.deps) Base.values(m::Manifest) = values(m.deps) Base.keys(m::Manifest) = keys(m.deps) Base.haskey(m::Manifest, key) = haskey(m.deps, key) function Base.show(io::IO, pkg::PackageEntry) f = [] pkg.name !== nothing && push!(f, "name" => pkg.name) pkg.version !== nothing && push!(f, "version" => pkg.version) pkg.tree_hash !== nothing && push!(f, "tree_hash" => pkg.tree_hash) pkg.path !== nothing && push!(f, "dev/path" => pkg.path) pkg.pinned && push!(f, "pinned" => pkg.pinned) pkg.repo.source !== nothing && push!(f, "url/path" => "`$(pkg.repo.source)`") pkg.repo.rev !== nothing && push!(f, "rev" => pkg.repo.rev) pkg.repo.subdir !== nothing && push!(f, "subdir" => pkg.repo.subdir) print(io, "PackageEntry(\n") for (field, value) in f print(io, " ", field, " = ", value, "\n") end print(io, ")") end mutable struct EnvCache # environment info: env::Union{Nothing,String} # paths for files: project_file::String manifest_file::String # name / uuid of the project pkg::Union{PackageSpec, Nothing} # cache of metadata: project::Project manifest::Manifest # What these where at creation of the EnvCache original_project::Project original_manifest::Manifest end function EnvCache(env::Union{Nothing,String}=nothing) project_file = find_project_file(env) project_dir = dirname(project_file) # read project file project = read_project(project_file) # initialize project package if project.name !== nothing && project.uuid !== nothing project_package = PackageSpec( name = project.name, uuid = project.uuid, version = something(project.version, VersionNumber("0.0")), path = project_dir, ) else project_package = nothing end # determine manifest file dir = abspath(project_dir) manifest_file = project.manifest manifest_file = manifest_file !== nothing ? (isabspath(manifest_file) ? manifest_file : abspath(dir, manifest_file)) : manifestfile_path(dir)::String write_env_usage(manifest_file, "manifest_usage.toml") manifest = read_manifest(manifest_file) envโ€ฒ = EnvCache(env, project_file, manifest_file, project_package, project, manifest, deepcopy(project), deepcopy(manifest), ) return envโ€ฒ end include("project.jl") include("manifest.jl") function num_concurrent_downloads() val = get(ENV, "JULIA_PKG_CONCURRENT_DOWNLOADS", "8") num = tryparse(Int, val) isnothing(num) && error("Environment variable `JULIA_PKG_CONCURRENT_DOWNLOADS` expects an integer, instead found $(val)") if num < 1 error("Number of concurrent downloads must be greater than 0") end return num end # ENV variables to set some of these defaults? Base.@kwdef mutable struct Context env::EnvCache = EnvCache() io::IO = stderr_f() use_git_for_all_downloads::Bool = false use_only_tarballs_for_downloads::Bool = false num_concurrent_downloads::Int = num_concurrent_downloads() # Registris registries::Vector{Registry.RegistryInstance} = Registry.reachable_registries() # The Julia Version to resolve with respect to julia_version::Union{VersionNumber,Nothing} = VERSION end project_uuid(env::EnvCache) = env.pkg === nothing ? nothing : env.pkg.uuid collides_with_project(env::EnvCache, pkg::PackageSpec) = is_project_name(env, pkg.name) || is_project_uuid(env, pkg.uuid) is_project(env::EnvCache, pkg::PackageSpec) = is_project_uuid(env, pkg.uuid) is_project_name(env::EnvCache, name::String) = env.pkg !== nothing && env.pkg.name == name is_project_name(env::EnvCache, name::Nothing) = false is_project_uuid(env::EnvCache, uuid::UUID) = project_uuid(env) == uuid ########### # Context # ########### const FORMER_STDLIBS = ["DelimitedFiles"] const FORMER_STDLIBS_UUIDS = Set{UUID}() const STDLIB = Ref{DictStdLibs}() function load_stdlib() stdlib = DictStdLibs() for name in readdir(stdlib_dir()) projfile = projectfile_path(stdlib_path(name); strict=true) nothing === projfile && continue project = parse_toml(projfile) uuid = get(project, "uuid", nothing)::Union{String, Nothing} v_str = get(project, "version", nothing)::Union{String, Nothing} version = isnothing(v_str) ? nothing : VersionNumber(v_str) nothing === uuid && continue if name in FORMER_STDLIBS push!(FORMER_STDLIBS_UUIDS, UUID(uuid)) continue end stdlib[UUID(uuid)] = (name, version) end return stdlib end function stdlibs() if !isassigned(STDLIB) STDLIB[] = load_stdlib() end return STDLIB[] end is_stdlib(uuid::UUID) = uuid in keys(stdlibs()) # Includes former stdlibs function is_or_was_stdlib(uuid::UUID, julia_version::Union{VersionNumber, Nothing}) return is_stdlib(uuid, julia_version) || uuid in FORMER_STDLIBS_UUIDS end # Find the entry in `STDLIBS_BY_VERSION` # that corresponds to the requested version, and use that. # If we can't find one, defaults to `UNREGISTERED_STDLIBS` function get_last_stdlibs(julia_version::VersionNumber; use_historical_for_current_version = false) if !use_historical_for_current_version && julia_version == VERSION return stdlibs() end last_stdlibs = UNREGISTERED_STDLIBS if isempty(STDLIBS_BY_VERSION) pkgerror("If you want to set `julia_version`, you must first populate the `STDLIBS_BY_VERSION` global constant") end for (version, stdlibs) in STDLIBS_BY_VERSION if VersionNumber(julia_version.major, julia_version.minor, julia_version.patch) < version break end last_stdlibs = stdlibs end return last_stdlibs end # If `julia_version` is set to `nothing`, that means (essentially) treat all registered # stdlibs as normal packages so that we get the latest versions of everything, ignoring # julia compat. So we set the list of stdlibs to that of only the unregistered stdlibs. get_last_stdlibs(::Nothing) = UNREGISTERED_STDLIBS # Allow asking if something is an stdlib for a particular version of Julia function is_stdlib(uuid::UUID, julia_version::Union{VersionNumber, Nothing}) # Only use the cache if we are asking for stdlibs in a custom Julia version if julia_version == VERSION return is_stdlib(uuid) end last_stdlibs = get_last_stdlibs(julia_version) # Note that if the user asks for something like `julia_version = 0.7.0`, we'll # fall through with an empty `last_stdlibs`, which will always return `false`. return uuid in keys(last_stdlibs) end # Return the version of a stdlib with respect to a particular Julia version, or # `nothing` if that stdlib is not versioned. We only store version numbers for # stdlibs that are external and thus could be installed from their repositories, # e.g. things like `GMP_jll`, `Tar`, etc... function stdlib_version(uuid::UUID, julia_version::Union{VersionNumber,Nothing}) last_stdlibs = get_last_stdlibs(julia_version) if !(uuid in keys(last_stdlibs)) return nothing end return last_stdlibs[uuid][2] end is_unregistered_stdlib(uuid::UUID) = haskey(UNREGISTERED_STDLIBS, uuid) Context!(kw_context::Vector{Pair{Symbol,Any}})::Context = Context!(Context(); kw_context...) function Context!(ctx::Context; kwargs...) for (k, v) in kwargs setfield!(ctx, k, v) end return ctx end function write_env_usage(source_file::AbstractString, usage_filepath::AbstractString) # Don't record ghost usage !isfile(source_file) && return # Ensure that log dir exists !ispath(logdir()) && mkpath(logdir()) usage_file = joinpath(logdir(), usage_filepath) timestamp = now() ## Atomically write usage file using process id locking FileWatching.mkpidlock(usage_file * ".pid", stale_age = 3) do usage = if isfile(usage_file) TOML.parsefile(usage_file) else Dict{String, Any}() end # record new usage usage[source_file] = [Dict("time" => timestamp)] # keep only latest usage info for k in keys(usage) times = map(usage[k]) do d if haskey(d, "time") Dates.DateTime(d["time"]) else # if there's no time entry because of a write failure be conservative and mark it as being used now @debug "Usage file `$usage_filepath` has a missing `time` entry for `$k`. Marking as used `now()`" Dates.now() end end usage[k] = [Dict("time" => maximum(times))] end open(usage_file, "w") do io TOML.print(io, usage, sorted=true) end end return end function read_package(path::String) project = read_project(path) if project.name === nothing pkgerror("expected a `name` entry in project file at `$(abspath(path))`") end if project.uuid === nothing pkgerror("expected a `uuid` entry in project file at `$(abspath(path))`") end name = project.name if !isfile(joinpath(dirname(path), "src", "$name.jl")) pkgerror("expected the file `src/$name.jl` to exist for package `$name` at `$(dirname(path))`") end return project end const refspecs = ["+refs/*:refs/remotes/cache/*"] function relative_project_path(project_file::String, path::String) # compute path relative the project # realpath needed to expand symlinks before taking the relative path return relpath(Pkg.safe_realpath(abspath(path)), Pkg.safe_realpath(dirname(project_file))) end function devpath(env::EnvCache, name::AbstractString, shared::Bool) @assert name != "" dev_dir = shared ? abspath(Pkg.devdir()) : joinpath(dirname(env.project_file), "dev") return joinpath(dev_dir, name) end function error_if_in_sysimage(pkg::PackageSpec) RESPECT_SYSIMAGE_VERSIONS[] || return false if pkg.uuid === nothing @error "Expected package $(pkg.name) to have a set UUID, please file a bug report." return false end pkgid = Base.PkgId(pkg.uuid, pkg.name) if Base.in_sysimage(pkgid) pkgerror("Tried to develop or add by URL package $(pkgid) which is already in the sysimage, use `Pkg.respect_sysimage_versions(false)` to disable this check.") end end function handle_repo_develop!(ctx::Context, pkg::PackageSpec, shared::Bool) # First, check if we can compute the path easily (which requires a given local path or name) is_local_path = pkg.repo.source !== nothing && !isurl(pkg.repo.source) if is_local_path || pkg.name !== nothing dev_path = is_local_path ? pkg.repo.source : devpath(ctx.env, pkg.name, shared) if pkg.repo.subdir !== nothing dev_path = joinpath(dev_path, pkg.repo.subdir) end # If given an explicit local path, that needs to exist if is_local_path && !isdir(dev_path) if isfile(dev_path) pkgerror("Dev path `$(dev_path)` is a file, but a directory is required.") else pkgerror("Dev path `$(dev_path)` does not exist.") end end if isdir(dev_path) resolve_projectfile!(ctx.env, pkg, dev_path) error_if_in_sysimage(pkg) if is_local_path pkg.path = isabspath(dev_path) ? dev_path : relative_project_path(ctx.env.manifest_file, dev_path) else pkg.path = shared ? dev_path : relative_project_path(ctx.env.manifest_file, dev_path) end return false end end # If we dev by name and it is in the Project + tracking a repo in the source we can get the repo from the Manifest if pkg.name !== nothing && pkg.uuid === nothing uuid = get(ctx.env.project.deps, pkg.name, nothing) if uuid !== nothing entry = manifest_info(ctx.env.manifest, uuid) if entry !== nothing pkg.repo.source = entry.repo.source pkg.repo.subdir = entry.repo.subdir end end end # Still haven't found the source, try get it from the registry if pkg.repo.source === nothing set_repo_source_from_registry!(ctx, pkg) end @assert pkg.repo.source !== nothing repo_path = tempname() cloned = false package_path = pkg.repo.subdir === nothing ? repo_path : joinpath(repo_path, pkg.repo.subdir) if !has_name(pkg) LibGit2.close(GitTools.ensure_clone(ctx.io, repo_path, pkg.repo.source)) cloned = true resolve_projectfile!(ctx.env, pkg, package_path) end if pkg.repo.subdir !== nothing repo_name = split(pkg.repo.source, '/', keepempty=false)[end] # Make the develop path prettier. if endswith(repo_name, ".git") repo_name = chop(repo_name, tail=4) end if endswith(repo_name, ".jl") repo_name = chop(repo_name, tail=3) end dev_path = devpath(ctx.env, repo_name, shared) else dev_path = devpath(ctx.env, pkg.name, shared) end if isdir(dev_path) println(ctx.io, "Path `$(dev_path)` exists and looks like the correct repo. Using existing path.") new = false else mkpath(dirname(dev_path)) if !cloned LibGit2.close(GitTools.ensure_clone(ctx.io, dev_path, pkg.repo.source)) else mv(repo_path, dev_path) end new = true end if !has_uuid(pkg) resolve_projectfile!(ctx.env, pkg, dev_path) end error_if_in_sysimage(pkg) pkg.path = shared ? dev_path : relative_project_path(ctx.env.manifest_file, dev_path) if pkg.repo.subdir !== nothing pkg.path = joinpath(pkg.path, pkg.repo.subdir) end return new end function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, shared::Bool) new_uuids = Set{UUID}() for pkg in pkgs new = handle_repo_develop!(ctx, pkg, shared) new && push!(new_uuids, pkg.uuid) @assert pkg.path !== nothing @assert has_uuid(pkg) pkg.repo = GitRepo() # clear repo field, no longer needed end return new_uuids end add_repo_cache_path(url::String) = joinpath(depots1(), "clones", string(hash(url))) function set_repo_source_from_registry!(ctx, pkg) registry_resolve!(ctx.registries, pkg) # Didn't find the package in the registry, but maybe it exists in the updated registry if !isresolved(pkg) Pkg.Operations.update_registries(ctx; force=false) registry_resolve!(ctx.registries, pkg) end ensure_resolved(ctx, ctx.env.manifest, [pkg]; registry=true) # We might have been given a name / uuid combo that does not have an entry in the registry for reg in ctx.registries regpkg = get(reg, pkg.uuid, nothing) regpkg === nothing && continue info = Pkg.Registry.registry_info(regpkg) url = info.repo url === nothing && continue pkg.repo.source = url if info.subdir !== nothing pkg.repo.subdir = info.subdir end return end pkgerror("Repository for package with UUID `$(pkg.uuid)` could not be found in a registry.") end function handle_repo_add!(ctx::Context, pkg::PackageSpec) # The first goal is to populate pkg.repo.source if that wasn't given explicitly if pkg.repo.source === nothing @assert pkg.repo.rev !== nothing # First, we try resolving against the manifest and current registry to avoid updating registries if at all possible. # This also handles the case where we _only_ wish to switch the tracking branch for a package. manifest_resolve!(ctx.env.manifest, [pkg]; force=true) if isresolved(pkg) entry = manifest_info(ctx.env.manifest, pkg.uuid) if entry !== nothing pkg.repo.source = entry.repo.source pkg.repo.subdir = entry.repo.subdir end end if pkg.repo.source === nothing set_repo_source_from_registry!(ctx, pkg) end end @assert pkg.repo.source !== nothing # We now have the source of the package repo, check if it is a local path and if that exists repo_source = pkg.repo.source if !isurl(pkg.repo.source) if isdir(pkg.repo.source) if !isdir(joinpath(pkg.repo.source, ".git")) msg = "Did not find a git repository at `$(pkg.repo.source)`" if isfile(joinpath(pkg.repo.source, "Project.toml")) || isfile(joinpath(pkg.repo.source, "JuliaProject.toml")) msg *= ", perhaps you meant `Pkg.develop`?" end pkgerror(msg) end LibGit2.with(GitTools.check_valid_HEAD, LibGit2.GitRepo(pkg.repo.source)) # check for valid git HEAD pkg.repo.source = isabspath(pkg.repo.source) ? safe_realpath(pkg.repo.source) : relative_project_path(ctx.env.manifest_file, pkg.repo.source) repo_source = normpath(joinpath(dirname(ctx.env.manifest_file), pkg.repo.source)) else pkgerror("Path `$(pkg.repo.source)` does not exist.") end end let repo_source = repo_source # The type-assertions below are necessary presumably due to julia#36454 LibGit2.with(GitTools.ensure_clone(ctx.io, add_repo_cache_path(repo_source::Union{Nothing,String}), repo_source::Union{Nothing,String}; isbare=true)) do repo repo_source_typed = repo_source::Union{Nothing,String} GitTools.check_valid_HEAD(repo) # If the user didn't specify rev, assume they want the default (master) branch if on a branch, otherwise the current commit if pkg.repo.rev === nothing pkg.repo.rev = LibGit2.isattached(repo) ? LibGit2.branch(repo) : string(LibGit2.GitHash(LibGit2.head(repo))) end obj_branch = get_object_or_branch(repo, pkg.repo.rev) fetched = false if obj_branch === nothing fetched = true GitTools.fetch(ctx.io, repo, repo_source_typed; refspecs=refspecs) obj_branch = get_object_or_branch(repo, pkg.repo.rev) if obj_branch === nothing pkgerror("Did not find rev $(pkg.repo.rev) in repository") end end gitobject, isbranch = obj_branch # If we are tracking a branch and are not pinned we want to update the repo if we haven't done that yet innerentry = manifest_info(ctx.env.manifest, pkg.uuid) ispinned = innerentry !== nothing && innerentry.pinned if isbranch && !fetched && !ispinned GitTools.fetch(ctx.io, repo, repo_source_typed; refspecs=refspecs) gitobject, isbranch = get_object_or_branch(repo, pkg.repo.rev) end # Now we have the gitobject for our ref, time to find the tree hash for it tree_hash_object = LibGit2.peel(LibGit2.GitTree, gitobject) if pkg.repo.subdir !== nothing try tree_hash_object = tree_hash_object[pkg.repo.subdir] catch e e isa KeyError || rethrow() pkgerror("Did not find subdirectory `$(pkg.repo.subdir)`") end end pkg.tree_hash = SHA1(string(LibGit2.GitHash(tree_hash_object))) # If we already resolved a uuid, we can bail early if this package is already installed at the current tree_hash if has_uuid(pkg) error_if_in_sysimage(pkg) version_path = Pkg.Operations.source_path(ctx.env.project_file, pkg, ctx.julia_version) isdir(version_path) && return false end temp_path = mktempdir() GitTools.checkout_tree_to_path(repo, tree_hash_object, temp_path) resolve_projectfile!(ctx.env, pkg, temp_path) error_if_in_sysimage(pkg) # Now that we are fully resolved (name, UUID, tree_hash, repo.source, repo.rev), we can finally # check to see if the package exists at its canonical path. version_path = Pkg.Operations.source_path(ctx.env.project_file, pkg, ctx.julia_version) isdir(version_path) && return false # Otherwise, move the temporary path into its correct place and set read only mkpath(version_path) mv(temp_path, version_path; force=true) set_readonly(version_path) return true end end end function handle_repos_add!(ctx::Context, pkgs::AbstractVector{PackageSpec}) new_uuids = Set{UUID}() for pkg in pkgs handle_repo_add!(ctx, pkg) && push!(new_uuids, pkg.uuid) @assert pkg.name !== nothing && pkg.uuid !== nothing && pkg.tree_hash !== nothing end return new_uuids end function resolve_projectfile!(env::EnvCache, pkg, project_path) project_file = projectfile_path(project_path; strict=true) project_file === nothing && pkgerror(string("could not find project file (Project.toml or JuliaProject.toml) in package at `", something(pkg.repo.source, pkg.path, project_path), "` maybe `subdir` needs to be specified")) project_data = read_package(project_file) if pkg.uuid === nothing || pkg.uuid == project_data.uuid pkg.uuid = project_data.uuid else pkgerror("UUID `$(project_data.uuid)` given by project file `$project_file` does not match given UUID `$(pkg.uuid)`") end if pkg.name === nothing || pkg.name == project_data.name pkg.name = project_data.name else pkgerror("name `$(project_data.name)` given by project file `$project_file` does not match given name `$(pkg.name)`") end end get_object_or_branch(repo, rev::SHA1) = get_object_or_branch(repo, string(rev)) # Returns nothing if rev could not be found in repo function get_object_or_branch(repo, rev) try gitobject = LibGit2.GitObject(repo, "remotes/cache/heads/" * rev) return gitobject, true catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() end try gitobject = LibGit2.GitObject(repo, "remotes/origin/" * rev) return gitobject, true catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() end try gitobject = LibGit2.GitObject(repo, rev) return gitobject, false catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() end return nothing end ######################################## # Resolving packages from name or uuid # ######################################## function project_resolve!(env::EnvCache, pkgs::AbstractVector{PackageSpec}) for pkg in pkgs if has_uuid(pkg) && !has_name(pkg) && Types.is_project_uuid(env, pkg.uuid) pkg.name = env.pkg.name end if has_name(pkg) && !has_uuid(pkg) && Types.is_project_name(env, pkg.name) pkg.uuid = env.pkg.uuid end end end # Disambiguate name/uuid package specifications using project info. function project_deps_resolve!(env::EnvCache, pkgs::AbstractVector{PackageSpec}) uuids = env.project.deps names = Dict(uuid => name for (name, uuid) in uuids) for pkg in pkgs if has_name(pkg) && !has_uuid(pkg) && pkg.name in keys(uuids) pkg.uuid = uuids[pkg.name] end if has_uuid(pkg) && !has_name(pkg) && pkg.uuid in keys(names) pkg.name = names[pkg.uuid] end end end # Disambiguate name/uuid package specifications using manifest info. function manifest_resolve!(manifest::Manifest, pkgs::AbstractVector{PackageSpec}; force=false) uuids = Dict{String,Vector{UUID}}() names = Dict{UUID,String}() for (uuid, entry) in manifest push!(get!(uuids, entry.name, UUID[]), uuid) names[uuid] = entry.name # can be duplicate but doesn't matter end for pkg in pkgs if has_name(pkg) && !has_uuid(pkg) && pkg.name in keys(uuids) length(uuids[pkg.name]) == 1 && (pkg.uuid = uuids[pkg.name][1]) end if has_uuid(pkg) && !has_name(pkg) && pkg.uuid in keys(names) pkg.name = names[pkg.uuid] end end end # Disambiguate name/uuid package specifications using registry info. registry_resolve!(registries::Vector{Registry.RegistryInstance}, pkg::PackageSpec) = registry_resolve!(registries, [pkg]) function registry_resolve!(registries::Vector{Registry.RegistryInstance}, pkgs::AbstractVector{PackageSpec}) # if there are no half-specified packages, return early any(pkg -> has_name(pkg) โŠป has_uuid(pkg), pkgs) || return for pkg in pkgs @assert has_name(pkg) || has_uuid(pkg) if has_name(pkg) && !has_uuid(pkg) pkg.uuid = registered_uuid(registries, pkg.name) end if has_uuid(pkg) && !has_name(pkg) pkg.name = registered_name(registries, pkg.uuid) end end return pkgs end function stdlib_resolve!(pkgs::AbstractVector{PackageSpec}) for pkg in pkgs @assert has_name(pkg) || has_uuid(pkg) if has_name(pkg) && !has_uuid(pkg) for (uuid, (name, version)) in stdlibs() name == pkg.name && (pkg.uuid = uuid) end end if !has_name(pkg) && has_uuid(pkg) name, version = get(stdlibs(), pkg.uuid, (nothing, nothing)) nothing !== name && (pkg.name = name) end end end # Ensure that all packages are fully resolved function ensure_resolved(ctx::Context, manifest::Manifest, pkgs::AbstractVector{PackageSpec}; registry::Bool=false,)::Nothing unresolved_uuids = Dict{String,Vector{UUID}}() for pkg in pkgs has_uuid(pkg) && continue !has_name(pkg) && pkgerror("Package $pkg has neither name nor uuid") uuids = [uuid for (uuid, entry) in manifest if entry.name == pkg.name] sort!(uuids, by=uuid -> uuid.value) unresolved_uuids[pkg.name] = uuids end unresolved_names = UUID[] for pkg in pkgs has_name(pkg) && continue push!(unresolved_names, pkg.uuid) end isempty(unresolved_uuids) && isempty(unresolved_names) && return msg = sprint(context = ctx.io) do io if !isempty(unresolved_uuids) print(io, "The following package names could not be resolved:") for (name, uuids) in sort!(collect(unresolved_uuids), by=lowercase โˆ˜ first) print(io, "\n * $name (") if length(uuids) == 0 what = ["project", "manifest"] registry && push!(what, "registry") print(io, "not found in ") join(io, what, ", ", " or ") print(io, ")") all_names = available_names(ctx; manifest, include_registries = registry) all_names_ranked, any_score_gt_zero = fuzzysort(name, all_names) if any_score_gt_zero println(io) prefix = " Suggestions:" printstyled(io, prefix, color = Base.info_color()) REPL.printmatches(io, name, all_names_ranked; cols = REPL._displaysize(ctx.io)[2] - length(prefix)) end else join(io, uuids, ", ", " or ") print(io, " in manifest but not in project)") end end end if !isempty(unresolved_names) println(io, "The following package uuids could not be resolved:") for uuid in unresolved_names println(io, " * $uuid") end end end pkgerror(msg) end # copied from REPL to efficiently expose if any score is >0 function fuzzysort(search::String, candidates::Vector{String}) scores = map(cand -> (REPL.fuzzyscore(search, cand), -Float64(REPL.levenshtein(search, cand))), candidates) candidates[sortperm(scores)] |> reverse, any(s -> s[1] > 0, scores) end function available_names(ctx::Context = Context(); manifest::Manifest = ctx.env.manifest, include_registries::Bool = true) all_names = String[] for (_, pkgentry) in manifest push!(all_names, pkgentry.name) end if include_registries for reg in ctx.registries for (_, pkgentry) in reg.pkgs push!(all_names, pkgentry.name) end end end return unique(all_names) end function registered_uuids(registries::Vector{Registry.RegistryInstance}, name::String) uuids = Set{UUID}() for reg in registries union!(uuids, Registry.uuids_from_name(reg, name)) end return uuids end # Determine a single UUID for a given name, prompting if needed function registered_uuid(registries::Vector{Registry.RegistryInstance}, name::String)::Union{Nothing,UUID} uuids = registered_uuids(registries, name) length(uuids) == 0 && return nothing length(uuids) == 1 && return first(uuids) repo_infos = Tuple{String, String, UUID}[] for uuid in uuids for reg in registries pkg = get(reg, uuid, nothing) pkg === nothing && continue info = Pkg.Registry.registry_info(pkg) repo = info.repo repo === nothing && continue push!(repo_infos, (reg.name, repo, uuid)) end end unique!(repo_infos) if isinteractive() # prompt for which UUID was intended: menu = RadioMenu(String["Registry: $(value[1]) - Repo: $(value[2]) - UUID: $(value[3])" for value in repo_infos]) choice = request("There are multiple registered `$name` packages, choose one:", menu) choice == -1 && return nothing return repo_infos[choice][3] else pkgerror("there are multiple registered `$name` packages, explicitly set the uuid") end end # Determine current name for a given package UUID function registered_name(registries::Vector{Registry.RegistryInstance}, uuid::UUID)::Union{Nothing,String} name = nothing for reg in registries regpkg = get(reg, uuid, nothing) regpkg === nothing && continue nameโ€ฒ = regpkg.name if name !== nothing nameโ€ฒ == name || pkgerror("package `$uuid` has multiple registered name values: $name, $nameโ€ฒ") end name = nameโ€ฒ end return name end # Find package by UUID in the manifest file manifest_info(::Manifest, uuid::Nothing) = nothing function manifest_info(manifest::Manifest, uuid::UUID)::Union{PackageEntry,Nothing} return get(manifest, uuid, nothing) end function write_env(env::EnvCache; update_undo=true, skip_writing_project::Bool=false) if (env.project != env.original_project) && (!skip_writing_project) write_project(env) end if env.manifest != env.original_manifest write_manifest(env) end update_undo && Pkg.API.add_snapshot_to_undo(env) end end # module x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/HistoricalStdlibs.jl ������using Base: UUID const DictStdLibs = Dict{UUID,Tuple{String,Union{VersionNumber,Nothing}}} # Julia standard libraries with duplicate entries removed so as to store only the # first release in a set of releases that all contain the same set of stdlibs. # # This needs to be populated via HistoricalStdlibVersions.jl by consumers # (e.g. BinaryBuilder) that want to use the "resolve things as if it were a # different Julia version than what is currently running" feature. const STDLIBS_BY_VERSION = Pair{VersionNumber, DictStdLibs}[] # This is a list of stdlibs that must _always_ be treated as stdlibs, # because they cannot be resolved in the registry; they have only ever existed within # the Julia stdlib source tree, and because of that, trying to resolve them will fail. const UNREGISTERED_STDLIBS = DictStdLibs( UUID("2a0f44e3-6c83-55bd-87e4-b1978d98bd5f") => ("Base64", nothing), UUID("8bf52ea8-c179-5cab-976a-9e18b702a9bc") => ("CRC32c", nothing), UUID("ade2ca70-3891-5945-98fb-dc099432e06a") => ("Dates", nothing), UUID("8ba89e20-285c-5b6f-9357-94700520ee1b") => ("Distributed", nothing), UUID("7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee") => ("FileWatching", nothing), UUID("9fa8497b-333b-5362-9e8d-4d0656e87820") => ("Future", nothing), UUID("b77e0a4c-d291-57a0-90e8-8db25a27a240") => ("InteractiveUtils", nothing), UUID("76f85450-5226-5b5a-8eaa-529ad045b433") => ("LibGit2", nothing), UUID("8f399da3-3557-5675-b5ff-fb832c97cbdb") => ("Libdl", nothing), UUID("37e2e46d-f89d-539d-b4ee-838fcccc9c8e") => ("LinearAlgebra", nothing), UUID("56ddb016-857b-54e1-b83d-db4d58db5568") => ("Logging", nothing), UUID("d6f4376e-aef5-505a-96c1-9c027394607a") => ("Markdown", nothing), UUID("a63ad114-7e13-5084-954f-fe012c677804") => ("Mmap", nothing), UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f") => ("Pkg", nothing), UUID("de0858da-6303-5e67-8744-51eddeeeb8d7") => ("Printf", nothing), UUID("9abbd945-dff8-562f-b5e8-e1ebf5ef1b79") => ("Profile", nothing), UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb") => ("REPL", nothing), UUID("9a3f8284-a2c9-5f02-9a11-845980a1fd5c") => ("Random", nothing), UUID("9e88b42a-f829-5b0c-bbe9-9e923198166b") => ("Serialization", nothing), UUID("1a1011a3-84de-559e-8e89-a11a2f7dc383") => ("SharedArrays", nothing), UUID("6462fe0b-24de-5631-8697-dd941f90decc") => ("Sockets", nothing), UUID("2f01184e-e22b-5df5-ae63-d93ebab69eaf") => ("SparseArrays", nothing), UUID("10745b16-79ce-11e8-11f9-7d13ad32a3b2") => ("Statistics", nothing), UUID("4607b0f0-06f3-5cda-b6b1-a6196a1729e9") => ("SuiteSparse", nothing), UUID("8dfed614-e22c-5e08-85e1-65c5234f0b40") => ("Test", nothing), UUID("cf7118a7-6976-5b1a-9a39-7adc72f591a4") => ("UUIDs", nothing), UUID("4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5") => ("Unicode", nothing), ) n���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/project.jl๐������######### # UTILS # ######### listed_deps(project::Project) = append!(collect(keys(project.deps)), collect(keys(project.extras)), collect(keys(project.weakdeps))) ########### # READING # ########### read_project_uuid(::Nothing) = nothing function read_project_uuid(uuid::String) try uuid = UUID(uuid) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse project UUID as a UUID") end return uuid end read_project_uuid(uuid) = pkgerror("Expected project UUID to be a string") read_project_version(::Nothing) = nothing function read_project_version(version::String) try version = VersionNumber(version) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse project version as a version") end end read_project_version(version) = pkgerror("Expected project version to be a string") read_project_deps(::Nothing, section::String) = Dict{String,UUID}() function read_project_deps(raw::Dict{String,Any}, section_name::String) deps = Dict{String,UUID}() for (name, uuid) in raw try uuid = UUID(uuid) catch err err isa ArgumentError || rethrow() pkgerror("Malformed value for `$name` in `$(section_name)` section.") end deps[name] = uuid end return deps end function read_project_deps(raw, section_name::String) pkgerror("Expected `$(section_name)` section to be a key-value list") end read_project_targets(::Nothing, project::Project) = Dict{String,Any}() function read_project_targets(raw::Dict{String,Any}, project::Project) for (target, deps) in raw deps isa Vector{String} || pkgerror(""" Expected value for target `$target` to be a list of dependency names. """) end return raw end read_project_targets(raw, project::Project) = pkgerror("Expected `targets` section to be a key-value list") read_project_compat(::Nothing, project::Project) = Dict{String,Compat}() function read_project_compat(raw::Dict{String,Any}, project::Project) compat = Dict{String,Compat}() for (name, version) in raw version = version::String try compat[name] = Compat(semver_spec(version), version) catch err pkgerror("Could not parse compatibility version for dependency `$name`") end end return compat end read_project_compat(raw, project::Project) = pkgerror("Expected `compat` section to be a key-value list") function validate(project::Project; file=nothing) # deps location_string = file === nothing ? "" : " at $(repr(file))." dep_uuids = collect(values(project.deps)) if length(dep_uuids) != length(unique(dep_uuids)) pkgerror("Two different dependencies can not have the same uuid" * location_string) end weak_dep_uuids = collect(values(project.weakdeps)) if length(weak_dep_uuids) != length(unique(weak_dep_uuids)) pkgerror("Two different weak dependencies can not have the same uuid" * location_string) end # extras extra_uuids = collect(values(project.extras)) if length(extra_uuids) != length(unique(extra_uuids)) pkgerror("Two different `extra` dependencies can not have the same uuid" * location_string) end # TODO decide what to do in when `add`ing a dep that is already in `extras` # also, reintroduce test files for this #= dep_names = keys(project.deps) for (name, uuid) in project.extras name in dep_names && pkgerror("name `$name` is listed in both `deps` and `extras`") uuid in dep_uuids && pkgerror("uuid `$uuid` is listed in both `deps` and `extras`") end =# # targets listed = listed_deps(project) for (target, deps) in project.targets, dep in deps if length(deps) != length(unique(deps)) pkgerror("A dependency was named twice in target `$target`") end dep in listed || pkgerror(""" Dependency `$dep` in target `$target` not listed in `deps`, `weakdeps` or `extras` section """ * location_string) end # compat for (name, version) in project.compat name == "julia" && continue name in listed || pkgerror("Compat `$name` not listed in `deps`, `weakdeps` or `extras` section" * location_string) end end function Project(raw::Dict; file=nothing) project = Project() project.other = raw project.name = get(raw, "name", nothing)::Union{String, Nothing} project.manifest = get(raw, "manifest", nothing)::Union{String, Nothing} project.uuid = read_project_uuid(get(raw, "uuid", nothing)) project.version = read_project_version(get(raw, "version", nothing)) project.deps = read_project_deps(get(raw, "deps", nothing), "deps") project.weakdeps = read_project_deps(get(raw, "weakdeps", nothing), "weakdeps") project.exts = get(Dict{String, String}, raw, "extensions") project.extras = read_project_deps(get(raw, "extras", nothing), "extras") project.compat = read_project_compat(get(raw, "compat", nothing), project) project.targets = read_project_targets(get(raw, "targets", nothing), project) # Handle deps in both [deps] and [weakdeps] project._deps_weak = Dict(intersect(project.deps, project.weakdeps)) filter!(p->!haskey(project._deps_weak, p.first), project.deps) validate(project; file) return project end function read_project(f_or_io::Union{String, IO}) raw = try if f_or_io isa IO TOML.parse(read(f_or_io, String)) else isfile(f_or_io) ? parse_toml(f_or_io) : return Project() end catch e if e isa TOML.ParserError pkgerror("Could not parse project: ", sprint(showerror, e)) end rethrow() end return Project(raw; file= f_or_io isa IO ? nothing : f_or_io) end ########### # WRITING # ########### function destructure(project::Project)::Dict raw = deepcopy(project.other) # sanity check for consistency between compat value and string representation for (name, compat) in project.compat if compat.val != semver_spec(compat.str) pkgerror("inconsistency between compat values and string representation") end end # if a field is set to its default value, don't include it in the write function entry!(key::String, src) should_delete(x::Dict) = isempty(x) should_delete(x) = x === nothing should_delete(src) ? delete!(raw, key) : (raw[key] = src) end entry!("name", project.name) entry!("uuid", project.uuid) entry!("version", project.version) entry!("manifest", project.manifest) entry!("deps", merge(project.deps, project._deps_weak)) entry!("weakdeps", project.weakdeps) entry!("extras", project.extras) entry!("compat", Dict(name => x.str for (name, x) in project.compat)) entry!("targets", project.targets) return raw end const _project_key_order = ["name", "uuid", "keywords", "license", "desc", "deps", "weakdeps", "extensions", "compat"] project_key_order(key::String) = something(findfirst(x -> x == key, _project_key_order), length(_project_key_order) + 1) function write_project(env::EnvCache) mkpath(dirname(env.project_file)) write_project(env.project, env.project_file) end write_project(project::Project, project_file::AbstractString) = write_project(destructure(project), project_file) function write_project(io::IO, project::Dict) TOML.print(io, project, sorted=true, by=key -> (project_key_order(key), key)) do x x isa UUID || x isa VersionNumber || pkgerror("unhandled type `$(typeof(x))`") return string(x) end return nothing end function write_project(project::Dict, project_file::AbstractString) str = sprint(write_project, project) write(project_file, str) end o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/manifest.jlห7������########### # READING # ########### function read_field(name::String, default, info, map) x = get(info, name, default) if default === nothing x === nothing && return nothing else x == default && return default end x isa String || pkgerror("Expected field `$name` to be a String.") return map(x) end function read_pinned(pinned) pinned === nothing && return false pinned isa Bool && return pinned pkgerror("Expected field `pinned` to be a Boolean.") end function safe_SHA1(sha::String) try sha = SHA1(sha) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse `git-tree-sha1` field as a SHA.") end return sha end function safe_uuid(uuid::String)::UUID try uuid = UUID(uuid) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse `uuid` field as a UUID.") end return uuid end function safe_bool(bool::String) try bool = parse(Bool, bool) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse `pinned` field as a Bool.") end return bool end # note: returns raw version *not* parsed version function safe_version(version::String)::VersionNumber try version = VersionNumber(version) catch err err isa ArgumentError || rethrow() pkgerror("Could not parse `version` as a `VersionNumber`.") end return version end # turn /-paths into \-paths on Windows function safe_path(path::String) if Sys.iswindows() && !isabspath(path) path = joinpath(split(path, "/")...) end return path end read_deps(::Nothing) = Dict{String, UUID}() read_deps(deps) = pkgerror("Expected `deps` field to be either a list or a table.") function read_deps(deps::AbstractVector) ret = String[] for dep in deps dep isa String || pkgerror("Expected `dep` entry to be a String.") push!(ret, dep) end return ret end function read_deps(raw::Dict{String, Any})::Dict{String,UUID} deps = Dict{String,UUID}() for (name, uuid) in raw deps[name] = safe_uuid(uuid) end return deps end struct Stage1 uuid::UUID entry::PackageEntry deps::Union{Vector{String}, Dict{String,UUID}} weakdeps::Union{Vector{String}, Dict{String,UUID}} end normalize_deps(name, uuid, deps, manifest; isext=false) = deps function normalize_deps(name, uuid, deps::Vector{String}, manifest::Dict{String,Vector{Stage1}}; isext=false) if length(deps) != length(unique(deps)) pkgerror("Duplicate entry in `$name=$uuid`'s `deps` field.") end final = Dict{String,UUID}() for dep in deps infos = get(manifest, dep, nothing) if !isext if infos === nothing pkgerror("`$name=$uuid` depends on `$dep`, ", "but no such entry exists in the manifest.") end end # should have used dict format instead of vector format if isnothing(infos) || length(infos) != 1 pkgerror("Invalid manifest format. ", "`$name=$uuid`'s dependency on `$dep` is ambiguous.") end final[dep] = infos[1].uuid end return final end function validate_manifest(julia_version::Union{Nothing,VersionNumber}, manifest_format::VersionNumber, stage1::Dict{String,Vector{Stage1}}, other::Dict{String, Any}) # expand vector format deps for (name, infos) in stage1, info in infos info.entry.deps = normalize_deps(name, info.uuid, info.deps, stage1) end for (name, infos) in stage1, info in infos info.entry.weakdeps = normalize_deps(name, info.uuid, info.weakdeps, stage1; isext=true) end # invariant: all dependencies are now normalized to Dict{String,UUID} deps = Dict{UUID, PackageEntry}() for (name, infos) in stage1, info in infos deps[info.uuid] = info.entry end # now just verify the graph structure for (entry_uuid, entry) in deps for (deptype, isext) in [(entry.deps, false), (entry.weakdeps, true)] for (name, uuid) in deptype dep_entry = get(deps, uuid, nothing) if !isext if dep_entry === nothing pkgerror("`$(entry.name)=$(entry_uuid)` depends on `$name=$uuid`, ", "but no such entry exists in the manifest.") end if dep_entry.name != name pkgerror("`$(entry.name)=$(entry_uuid)` depends on `$name=$uuid`, ", "but entry with UUID `$uuid` has name `$(dep_entry.name)`.") end end end end end return Manifest(; julia_version, manifest_format, deps, other) end function Manifest(raw::Dict{String, Any}, f_or_io::Union{String, IO})::Manifest julia_version = haskey(raw, "julia_version") ? VersionNumber(raw["julia_version"]::String) : nothing manifest_format = VersionNumber(raw["manifest_format"]::String) if !in(manifest_format.major, 1:2) if f_or_io isa IO @warn "Unknown Manifest.toml format version detected in streamed manifest. Unexpected behavior may occur" manifest_format else @warn "Unknown Manifest.toml format version detected in file `$(f_or_io)`. Unexpected behavior may occur" manifest_format maxlog = 1 _id = Symbol(f_or_io) end end stage1 = Dict{String,Vector{Stage1}}() if haskey(raw, "deps") # deps field doesn't exist if there are no deps deps_raw = raw["deps"]::Dict{String, Any} for (name::String, infos) in deps_raw infos = infos::Vector{Any} for info in infos info = info::Dict{String, Any} entry = PackageEntry() entry.name = name uuid = nothing deps = nothing weakdeps = nothing try entry.pinned = read_pinned(get(info, "pinned", nothing)) uuid = read_field("uuid", nothing, info, safe_uuid)::UUID entry.version = read_field("version", nothing, info, safe_version) entry.path = read_field("path", nothing, info, safe_path) entry.repo.source = read_field("repo-url", nothing, info, identity) entry.repo.rev = read_field("repo-rev", nothing, info, identity) entry.repo.subdir = read_field("repo-subdir", nothing, info, identity) entry.tree_hash = read_field("git-tree-sha1", nothing, info, safe_SHA1) entry.uuid = uuid deps = read_deps(get(info::Dict, "deps", nothing)::Union{Nothing, Dict{String, Any}, Vector{String}}) weakdeps = read_deps(get(info::Dict, "weakdeps", nothing)::Union{Nothing, Dict{String, Any}, Vector{String}}) entry.exts = get(Dict{String, String}, info, "extensions") catch # TODO: Should probably not unconditionally log something # @debug "Could not parse manifest entry for `$name`" f_or_io rethrow() end entry.other = info stage1[name] = push!(get(stage1, name, Stage1[]), Stage1(uuid, entry, deps, weakdeps)) end end # by this point, all the fields of the `PackageEntry`s have been type casted # but we have *not* verified the _graph_ structure of the manifest end other = Dict{String, Any}() for (k, v) in raw if k in ("julia_version", "deps", "manifest_format") continue end other[k] = v end return validate_manifest(julia_version, manifest_format, stage1, other) end function read_manifest(f_or_io::Union{String, IO}) raw = try if f_or_io isa IO TOML.parse(read(f_or_io, String)) else isfile(f_or_io) ? parse_toml(f_or_io) : return Manifest() end catch e if e isa TOML.ParserError pkgerror("Could not parse manifest: ", sprint(showerror, e)) end rethrow() end if Base.is_v1_format_manifest(raw) raw = convert_v1_format_manifest(raw) end return Manifest(raw, f_or_io) end function convert_v1_format_manifest(old_raw_manifest::Dict) new_raw_manifest = Dict{String, Any}( "deps" => old_raw_manifest, "manifest_format" => "1.0.0" # must be a string here to match raw dict # don't set julia_version as it is unknown in old manifests ) return new_raw_manifest end ########### # WRITING # ########### function destructure(manifest::Manifest)::Dict function entry!(entry, key, value; default=nothing) if value == default delete!(entry, key) else entry[key] = value end end unique_name = Dict{String,Bool}() for (uuid, entry) in manifest unique_name[entry.name] = !haskey(unique_name, entry.name) end # maintain the format of the manifest when writing if manifest.manifest_format.major == 1 raw = Dict{String,Vector{Dict{String,Any}}}() elseif manifest.manifest_format.major == 2 raw = Dict{String,Any}() if !isnothing(manifest.julia_version) # don't write julia_version if nothing raw["julia_version"] = manifest.julia_version end raw["manifest_format"] = string(manifest.manifest_format.major, ".", manifest.manifest_format.minor) raw["deps"] = Dict{String,Vector{Dict{String,Any}}}() for (k, v) in manifest.other raw[k] = v end end for (uuid, entry) in manifest new_entry = something(entry.other, Dict{String,Any}()) new_entry["uuid"] = string(uuid) entry!(new_entry, "version", entry.version) entry!(new_entry, "git-tree-sha1", entry.tree_hash) entry!(new_entry, "pinned", entry.pinned; default=false) path = entry.path if path !== nothing && Sys.iswindows() && !isabspath(path) path = join(splitpath(path), "/") end entry!(new_entry, "path", path) repo_source = entry.repo.source if repo_source !== nothing && Sys.iswindows() && !isabspath(repo_source) && !isurl(repo_source) repo_source = join(splitpath(repo_source), "/") end entry!(new_entry, "repo-url", repo_source) entry!(new_entry, "repo-rev", entry.repo.rev) entry!(new_entry, "repo-subdir", entry.repo.subdir) for (deptype, depname) in [(entry.deps, "deps"), (entry.weakdeps, "weakdeps")] if isempty(deptype) delete!(new_entry, depname) else if all(dep -> haskey(unique_name, first(dep)), deptype) && all(dep -> unique_name[first(dep)], deptype) new_entry[depname] = sort(collect(keys(deptype))) else new_entry[depname] = Dict{String,String}() for (name, uuid) in deptype new_entry[depname][name] = string(uuid) end end end end # TODO: Write this inline if !isempty(entry.exts) entry!(new_entry, "extensions", entry.exts) end if manifest.manifest_format.major == 1 push!(get!(raw, entry.name, Dict{String,Any}[]), new_entry) elseif manifest.manifest_format.major == 2 push!(get!(raw["deps"], entry.name, Dict{String,Any}[]), new_entry) end end return raw end function write_manifest(env::EnvCache) mkpath(dirname(env.manifest_file)) write_manifest(env.manifest, env.manifest_file) end function write_manifest(manifest::Manifest, manifest_file::AbstractString) if manifest.manifest_format.major == 1 @warn """The active manifest file at `$(manifest_file)` has an old format that is being maintained. To update to the new format, which is supported by Julia versions โ‰ฅ 1.6.2, run `import Pkg; Pkg.upgrade_manifest()` which will upgrade the format without re-resolving. To then record the julia version re-resolve with `Pkg.resolve()` and if there are resolve conflicts consider `Pkg.update()`.""" maxlog = 1 _id = Symbol(manifest_file) end return write_manifest(destructure(manifest), manifest_file) end function write_manifest(io::IO, manifest::Manifest) return write_manifest(io, destructure(manifest)) end function write_manifest(io::IO, raw_manifest::Dict) print(io, "# This file is machine-generated - editing it directly is not advised\n\n") TOML.print(io, raw_manifest, sorted=true) do x (typeof(x) in [String, Nothing, UUID, SHA1, VersionNumber]) && return string(x) error("unhandled type `$(typeof(x))`") end return nothing end function write_manifest(raw_manifest::Dict, manifest_file::AbstractString) str = sprint(write_manifest, raw_manifest) write(manifest_file, str) end ############ # METADATA # ############ function check_warn_manifest_julia_version_compat(manifest::Manifest, manifest_file::String) isempty(manifest.deps) && return if manifest.manifest_format < v"2" @warn """The active manifest file is an older format with no julia version entry. Dependencies may have \ been resolved with a different julia version.""" maxlog = 1 _file = manifest_file _line = 0 _module = nothing return end v = manifest.julia_version if v === nothing @warn """The active manifest file is missing a julia version entry. Dependencies may have \ been resolved with a different julia version.""" maxlog = 1 _file = manifest_file _line = 0 _module = nothing return end if Base.thisminor(v) != Base.thisminor(VERSION) @warn """The active manifest file has dependencies that were resolved with a different julia \ version ($(manifest.julia_version)). Unexpected behavior may occur.""" maxlog = 1 _file = manifest_file _line = 0 _module = nothing end end }���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/BinaryPlatforms_compat.jl]������module BinaryPlatforms using Base.BinaryPlatforms export platform_key_abi, platform_dlext, valid_dl_path, arch, libc, libgfortran_version, libstdcxx_version, cxxstring_abi, parse_dl_name_version, detect_libgfortran_version, detect_libstdcxx_version, detect_cxxstring_abi, call_abi, wordsize, triplet, select_platform, platforms_match, CompilerABI, Platform, UnknownPlatform, Linux, MacOS, Windows, FreeBSD import Base.BinaryPlatforms: libgfortran_version, libstdcxx_version, platform_name, wordsize, platform_dlext, tags, arch, libc, call_abi, cxxstring_abi struct UnknownPlatform <: AbstractPlatform UnknownPlatform(args...; kwargs...) = new() end tags(::UnknownPlatform) = Dict{String,String}("os"=>"unknown") struct CompilerABI libgfortran_version::Union{Nothing,VersionNumber} libstdcxx_version::Union{Nothing,VersionNumber} cxxstring_abi::Union{Nothing,Symbol} function CompilerABI(;libgfortran_version::Union{Nothing, VersionNumber} = nothing, libstdcxx_version::Union{Nothing, VersionNumber} = nothing, cxxstring_abi::Union{Nothing, Symbol} = nothing) return new(libgfortran_version, libstdcxx_version, cxxstring_abi) end end # Easy replacement constructor function CompilerABI(cabi::CompilerABI; libgfortran_version=nothing, libstdcxx_version=nothing, cxxstring_abi=nothing) return CompilerABI(; libgfortran_version=something(libgfortran_version, Some(cabi.libgfortran_version)), libstdcxx_version=something(libstdcxx_version, Some(cabi.libstdcxx_version)), cxxstring_abi=something(cxxstring_abi, Some(cabi.cxxstring_abi)), ) end libgfortran_version(cabi::CompilerABI) = cabi.libgfortran_version libstdcxx_version(cabi::CompilerABI) = cabi.libstdcxx_version cxxstring_abi(cabi::CompilerABI) = cabi.cxxstring_abi for T in (:Linux, :Windows, :MacOS, :FreeBSD) @eval begin struct $(T) <: AbstractPlatform p::Platform function $(T)(arch::Symbol; compiler_abi=nothing, kwargs...) if compiler_abi !== nothing kwargs = (; kwargs..., :libgfortran_version => libgfortran_version(compiler_abi), :libstdcxx_version => libstdcxx_version(compiler_abi), :cxxstring_abi => cxxstring_abi(compiler_abi) ) end return new(Platform(string(arch), $(string(T)); kwargs..., validate_strict=true)) end end end end const PlatformUnion = Union{Linux,MacOS,Windows,FreeBSD} # First, methods we need to coerce to Symbol for backwards-compatibility for f in (:arch, :libc, :call_abi, :cxxstring_abi) @eval begin function $(f)(p::PlatformUnion) str = $(f)(p.p) if str === nothing return nothing end return Symbol(str) end end end # Next, things we don't need to coerce for f in (:libgfortran_version, :libstdcxx_version, :platform_name, :wordsize, :platform_dlext, :tags, :triplet) @eval begin $(f)(p::PlatformUnion) = $(f)(p.p) end end # Finally, add equality testing between these wrapper types and other AbstractPlatforms @eval begin Base.:(==)(a::PlatformUnion, b::AbstractPlatform) = b == a.p end # Add one-off functions MacOS(; kwargs...) = MacOS(:x86_64; kwargs...) FreeBSD(; kwargs...) = FreeBSD(:x86_64; kwargs...) function triplet(p::AbstractPlatform) # We are going to sub off to `Base.BinaryPlatforms.triplet()` here, # with the important exception that we override `os_version` to better # mimic the old behavior of `triplet()` if Sys.isfreebsd(p) p = deepcopy(p) p["os_version"] = "11.1.0" elseif Sys.isapple(p) p = deepcopy(p) p["os_version"] = "14.0.0" end return Base.BinaryPlatforms.triplet(p) end """ platform_key_abi(machine::AbstractString) Returns the platform key for the current platform, or any other though the the use of the `machine` parameter. This method is deprecated, import `Base.BinaryPlatforms` and use either `HostPlatform()` to get the current host platform, or `parse(Base.BinaryPlatforms.Platform, triplet)` to parse the triplet for some other platform instead. """ platform_key_abi() = HostPlatform() platform_key_abi(triplet::AbstractString) = parse(Platform, triplet) """ valid_dl_path(path::AbstractString, platform::Platform) Return `true` if the given `path` ends in a valid dynamic library filename. E.g. returns `true` for a path like `"usr/lib/libfoo.so.3.5"`, but returns `false` for a path like `"libbar.so.f.a"`. This method is deprecated and will be removed in Julia 2.0. """ function valid_dl_path(path::AbstractString, platform::AbstractPlatform) try parse_dl_name_version(path, string(os(platform))::String) return true catch e if isa(e, ArgumentError) return false end rethrow(e) end end end # module BinaryPlatforms p���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Artifacts.jl;a������module Artifacts using Artifacts, Base.BinaryPlatforms, SHA using ..MiniProgressBars, ..PlatformEngines using Tar: can_symlink import ..set_readonly, ..GitTools, ..TOML, ..pkg_server, ..can_fancyprint, ..stderr_f, ..printpkgstyle import Base: get, SHA1 import Artifacts: artifact_names, ARTIFACTS_DIR_OVERRIDE, ARTIFACT_OVERRIDES, artifact_paths, artifacts_dirs, pack_platform!, unpack_platform, load_artifacts_toml, query_override, with_artifacts_directory, load_overrides import ..Types: write_env_usage, parse_toml export create_artifact, artifact_exists, artifact_path, remove_artifact, verify_artifact, artifact_meta, artifact_hash, bind_artifact!, unbind_artifact!, download_artifact, find_artifacts_toml, ensure_artifact_installed, @artifact_str, archive_artifact, select_downloadable_artifacts """ create_artifact(f::Function) Creates a new artifact by running `f(artifact_path)`, hashing the result, and moving it to the artifact store (`~/.julia/artifacts` on a typical installation). Returns the identifying tree hash of this artifact. """ function create_artifact(f::Function) # Ensure the `artifacts` directory exists in our default depot artifacts_dir = first(artifacts_dirs()) mkpath(artifacts_dir) # Temporary directory where we'll do our creation business temp_dir = mktempdir(artifacts_dir) try # allow the user to do their work inside the temporary directory f(temp_dir) # Calculate the tree hash for this temporary directory artifact_hash = SHA1(GitTools.tree_hash(temp_dir)) # If we created a dupe, just let the temp directory get destroyed. It's got the # same contents as whatever already exists after all, so it doesn't matter. Only # move its contents if it actually contains new contents. Note that we explicitly # set `honor_overrides=false` here, as we wouldn't want to drop things into the # system directory by accidentally creating something with the same content-hash # as something that was foolishly overridden. This should be virtually impossible # unless the user has been very unwise, but let's be cautious. new_path = artifact_path(artifact_hash; honor_overrides=false) _mv_temp_artifact_dir(temp_dir, new_path) # Give the people what they want return artifact_hash finally # Always attempt to cleanup rm(temp_dir; recursive=true, force=true) end end """ _mv_temp_artifact_dir(temp_dir::String, new_path::String)::Nothing Either rename the directory at `temp_dir` to `new_path` and set it to read-only or if `new_path` artifact already exists try to do nothing. """ function _mv_temp_artifact_dir(temp_dir::String, new_path::String)::Nothing if !isdir(new_path) # This next step is like # `mv(temp_dir, new_path)`. # However, `mv` defaults to `cp` if `rename` returns an error. # `cp` is not atomic, so avoid the potential of calling it. err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), temp_dir, new_path) # Ignore rename error, but ensure `new_path` exists. if !isdir(new_path) error("$(repr(new_path)) could not be made") end chmod(new_path, filemode(dirname(new_path))) set_readonly(new_path) end nothing end """ remove_artifact(hash::SHA1; honor_overrides::Bool=false) Removes the given artifact (identified by its SHA1 git tree hash) from disk. Note that if an artifact is installed in multiple depots, it will be removed from all of them. If an overridden artifact is requested for removal, it will be silently ignored; this method will never attempt to remove an overridden artifact. In general, we recommend that you use `Pkg.gc()` to manage artifact installations and do not use `remove_artifact()` directly, as it can be difficult to know if an artifact is being used by another package. """ function remove_artifact(hash::SHA1) if query_override(hash) !== nothing # We never remove overridden artifacts. return end # Get all possible paths (rooted in all depots) possible_paths = artifacts_dirs(bytes2hex(hash.bytes)) for path in possible_paths if isdir(path) rm(path; recursive=true, force=true) end end end """ verify_artifact(hash::SHA1; honor_overrides::Bool=false) Verifies that the given artifact (identified by its SHA1 git tree hash) is installed on- disk, and retains its integrity. If the given artifact is overridden, skips the verification unless `honor_overrides` is set to `true`. """ function verify_artifact(hash::SHA1; honor_overrides::Bool=false) # Silently skip overridden artifacts unless we really ask for it if !honor_overrides if query_override(hash) !== nothing return true end end # If it doesn't even exist, then skip out if !artifact_exists(hash) return false end # Otherwise actually run the verification return all(hash.bytes .== GitTools.tree_hash(artifact_path(hash))) end """ archive_artifact(hash::SHA1, tarball_path::String; honor_overrides::Bool=false) Archive an artifact into a tarball stored at `tarball_path`, returns the SHA256 of the resultant tarball as a hexadecimal string. Throws an error if the artifact does not exist. If the artifact is overridden, throws an error unless `honor_overrides` is set. """ function archive_artifact(hash::SHA1, tarball_path::String; honor_overrides::Bool=false) if !honor_overrides if query_override(hash) !== nothing error("Will not archive an overridden artifact unless `honor_overrides` is set!") end end if !artifact_exists(hash) error("Unable to archive artifact $(bytes2hex(hash.bytes)): does not exist!") end # Package it up package(artifact_path(hash), tarball_path) # Calculate its sha256 and return that return open(tarball_path, "r") do io return bytes2hex(sha256(io)) end end """ bind_artifact!(artifacts_toml::String, name::String, hash::SHA1; platform::Union{AbstractPlatform,Nothing} = nothing, download_info::Union{Vector{Tuple},Nothing} = nothing, lazy::Bool = false, force::Bool = false) Writes a mapping of `name` -> `hash` within the given `(Julia)Artifacts.toml` file. If `platform` is not `nothing`, this artifact is marked as platform-specific, and will be a multi-mapping. It is valid to bind multiple artifacts with the same name, but different `platform`s and `hash`'es within the same `artifacts_toml`. If `force` is set to `true`, this will overwrite a pre-existant mapping, otherwise an error is raised. `download_info` is an optional vector that contains tuples of URLs and a hash. These URLs will be listed as possible locations where this artifact can be obtained. If `lazy` is set to `true`, even if download information is available, this artifact will not be downloaded until it is accessed via the `artifact"name"` syntax, or `ensure_artifact_installed()` is called upon it. """ function bind_artifact!(artifacts_toml::String, name::String, hash::SHA1; platform::Union{AbstractPlatform,Nothing} = nothing, download_info::Union{Vector{<:Tuple},Nothing} = nothing, lazy::Bool = false, force::Bool = false) # First, check to see if this artifact is already bound: if isfile(artifacts_toml) artifact_dict = parse_toml(artifacts_toml) if !force && haskey(artifact_dict, name) meta = artifact_dict[name] if !isa(meta, Vector) error("Mapping for '$name' within $(artifacts_toml) already exists!") elseif any(isequal(platform), unpack_platform(x, name, artifacts_toml) for x in meta) error("Mapping for '$name'/$(triplet(platform)) within $(artifacts_toml) already exists!") end end else artifact_dict = Dict{String, Any}() end # Otherwise, the new piece of data we're going to write out is this dict: meta = Dict{String,Any}( "git-tree-sha1" => bytes2hex(hash.bytes), ) # If we're set to be lazy, then lazy we shall be if lazy meta["lazy"] = true end # Integrate download info, if it is given. We represent the download info as a # vector of dicts, each with its own `url` and `sha256`, since different tarballs can # expand to the same tree hash. if download_info !== nothing meta["download"] = [ Dict("url" => dl[1], "sha256" => dl[2], ) for dl in download_info ] end if platform === nothing artifact_dict[name] = meta else # Add platform-specific keys to our `meta` dict pack_platform!(meta, platform) # Insert this entry into the list of artifacts if !haskey(artifact_dict, name) artifact_dict[name] = [meta] else # Delete any entries that contain identical platforms artifact_dict[name] = filter( x -> unpack_platform(x, name, artifacts_toml) != platform, artifact_dict[name] ) push!(artifact_dict[name], meta) end end # Spit it out onto disk let artifact_dict = artifact_dict parent_dir = dirname(artifacts_toml) temp_artifacts_toml = isempty(parent_dir) ? tempname(pwd()) : tempname(parent_dir) open(temp_artifacts_toml, "w") do io TOML.print(io, artifact_dict, sorted=true) end mv(temp_artifacts_toml, artifacts_toml; force=true) end # Mark that we have used this Artifact.toml write_env_usage(artifacts_toml, "artifact_usage.toml") return end """ unbind_artifact!(artifacts_toml::String, name::String; platform = nothing) Unbind the given `name` from an `(Julia)Artifacts.toml` file. Silently fails if no such binding exists within the file. """ function unbind_artifact!(artifacts_toml::String, name::String; platform::Union{AbstractPlatform,Nothing} = nothing) artifact_dict = parse_toml(artifacts_toml) if !haskey(artifact_dict, name) return end if platform === nothing delete!(artifact_dict, name) else artifact_dict[name] = filter( x -> unpack_platform(x, name, artifacts_toml) != platform, artifact_dict[name] ) end open(artifacts_toml, "w") do io TOML.print(io, artifact_dict, sorted=true) end return end """ download_artifact(tree_hash::SHA1, tarball_url::String, tarball_hash::String; verbose::Bool = false, io::IO=stderr) Download/install an artifact into the artifact store. Returns `true` on success, returns an error object on failure. !!! compat "Julia 1.8" As of Julia 1.8 this function returns the error object rather than `false` when failure occurs """ function download_artifact( tree_hash::SHA1, tarball_url::String, tarball_hash::Union{String, Nothing} = nothing; verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr_f(), ) if artifact_exists(tree_hash) return true end # Ensure the `artifacts` directory exists in our default depot artifacts_dir = first(artifacts_dirs()) mkpath(artifacts_dir) # expected artifact path dst = joinpath(artifacts_dir, bytes2hex(tree_hash.bytes)) # We download by using a temporary directory. We do this because the download may # be corrupted or even malicious; we don't want to clobber someone else's artifact # by trusting the tree hash that has been given to us; we will instead download it # to a temporary directory, calculate the true tree hash, then move it to the proper # location only after knowing what it is, and if something goes wrong in the process, # everything should be cleaned up. # Temporary directory where we'll do our creation business temp_dir = mktempdir(artifacts_dir) try download_verify_unpack(tarball_url, tarball_hash, temp_dir, ignore_existence=true, verbose=verbose, quiet_download=quiet_download, io=io) calc_hash = SHA1(GitTools.tree_hash(temp_dir)) # Did we get what we expected? If not, freak out. if calc_hash.bytes != tree_hash.bytes msg = """ Tree Hash Mismatch! Expected git-tree-sha1: $(bytes2hex(tree_hash.bytes)) Calculated git-tree-sha1: $(bytes2hex(calc_hash.bytes)) """ # Since tree hash calculation is rather fragile and file system dependent, # we allow setting JULIA_PKG_IGNORE_HASHES=1 to ignore the error and move # the artifact to the expected location and return true ignore_hash_env_set = get(ENV, "JULIA_PKG_IGNORE_HASHES", "") != "" if ignore_hash_env_set ignore_hash = Base.get_bool_env("JULIA_PKG_IGNORE_HASHES", false) ignore_hash === nothing && @error( "Invalid ENV[\"JULIA_PKG_IGNORE_HASHES\"] value", ENV["JULIA_PKG_IGNORE_HASHES"], ) ignore_hash = something(ignore_hash, false) else # default: false except Windows users who can't symlink ignore_hash = Sys.iswindows() && !mktempdir(can_symlink, artifacts_dir) end if ignore_hash desc = ignore_hash_env_set ? "Environment variable \$JULIA_PKG_IGNORE_HASHES is true" : "System is Windows and user cannot create symlinks" msg *= "\n$desc: \ ignoring hash mismatch and moving \ artifact to the expected location" @error(msg) else error(msg) end end # Move it to the location we expected _mv_temp_artifact_dir(temp_dir, dst) catch err @debug "download_artifact error" tree_hash tarball_url tarball_hash err if isa(err, InterruptException) rethrow(err) end # If something went wrong during download, return the error return err finally # Always attempt to cleanup rm(temp_dir; recursive=true, force=true) end return true end """ ensure_artifact_installed(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Base.UUID,Nothing}=nothing, verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr) Ensures an artifact is installed, downloading it via the download information stored in `artifacts_toml` if necessary. Throws an error if unable to install. """ function ensure_artifact_installed(name::String, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Base.UUID,Nothing}=nothing, verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr_f()) meta = artifact_meta(name, artifacts_toml; pkg_uuid=pkg_uuid, platform=platform) if meta === nothing error("Cannot locate artifact '$(name)' in '$(artifacts_toml)'") end return ensure_artifact_installed(name, meta, artifacts_toml; platform=platform, verbose=verbose, quiet_download=quiet_download, io=io) end function ensure_artifact_installed(name::String, meta::Dict, artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr_f()) hash = SHA1(meta["git-tree-sha1"]) if !artifact_exists(hash) errors = Any[] # first try downloading from Pkg server # TODO: only do this if Pkg server knows about this package if (server = pkg_server()) !== nothing url = "$server/artifact/$hash" download_success = let url=url @debug "Downloading artifact from Pkg server" name artifacts_toml platform url with_show_download_info(io, name, quiet_download) do download_artifact(hash, url; verbose=verbose, quiet_download=quiet_download, io=io) end end # download_success is either `true` or an error object if download_success === true return artifact_path(hash) else @debug "Failed to download artifact from Pkg server" download_success push!(errors, (url, download_success)) end end # If this artifact does not exist on-disk already, ensure it has download # information, then download it! if !haskey(meta, "download") error("Cannot automatically install '$(name)'; no download section in '$(artifacts_toml)'") end # Attempt to download from all sources for entry in meta["download"] url = entry["url"] tarball_hash = entry["sha256"] download_success = let url=url @debug "Downloading artifact" name artifacts_toml platform url with_show_download_info(io, name, quiet_download) do download_artifact(hash, url, tarball_hash; verbose=verbose, quiet_download=quiet_download, io=io) end end # download_success is either `true` or an error object if download_success === true return artifact_path(hash) else @debug "Failed to download artifact" download_success push!(errors, (url, download_success)) end end errmsg = """ Unable to automatically download/install artifact '$(name)' from sources listed in '$(artifacts_toml)'. Sources attempted: """ for (url, err) in errors errmsg *= "- $(url)\n" errmsg *= " Error: $(sprint(showerror, err))\n" end error(errmsg) else return artifact_path(hash) end end function with_show_download_info(f, io, name, quiet_download) fancyprint = can_fancyprint(io) if !quiet_download fancyprint && print_progress_bottom(io) printpkgstyle(io, :Downloading, "artifact: $name") end success = false try result = f() success = result === true return result finally if !quiet_download fancyprint && print(io, "\033[1A") # move cursor up one line fancyprint && print(io, "\033[2K") # clear line if success fancyprint && printpkgstyle(io, :Downloaded, "artifact: $name") else printpkgstyle(io, :Failure, "artifact: $name", color = :red) end end end end """ ensure_all_artifacts_installed(artifacts_toml::String; platform = HostPlatform(), pkg_uuid = nothing, include_lazy = false, verbose = false, quiet_download = false, io::IO=stderr) Installs all non-lazy artifacts from a given `(Julia)Artifacts.toml` file. `package_uuid` must be provided to properly support overrides from `Overrides.toml` entries in depots. If `include_lazy` is set to `true`, then lazy packages will be installed as well. This function is deprecated and should be replaced with the following snippet: artifacts = select_downloadable_artifacts(artifacts_toml; platform, include_lazy) for name in keys(artifacts) ensure_artifact_installed(name, artifacts[name], artifacts_toml; platform=platform) end !!! warning This function is deprecated in Julia 1.6 and will be removed in a future version. Use `select_downloadable_artifacts()` and `ensure_artifact_installed()` instead. """ function ensure_all_artifacts_installed(artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Nothing,Base.UUID} = nothing, include_lazy::Bool = false, verbose::Bool = false, quiet_download::Bool = false, io::IO=stderr_f()) # This function should not be called anymore; use `select_downloadable_artifacts()` directly. Base.depwarn("`ensure_all_artifacts_installed()` is deprecated; iterate over `select_downloadable_artifacts()` output with `ensure_artifact_installed()`.", :ensure_all_artifacts_installed) # Collect all artifacts we're supposed to install artifacts = select_downloadable_artifacts(artifacts_toml; platform, include_lazy, pkg_uuid) for name in keys(artifacts) # Otherwise, let's try and install it! ensure_artifact_installed(name, artifacts[name], artifacts_toml; platform=platform, verbose=verbose, quiet_download=quiet_download, io=io) end end """ extract_all_hashes(artifacts_toml::String; platform = HostPlatform(), pkg_uuid = nothing, include_lazy = false) Extract all hashes from a given `(Julia)Artifacts.toml` file. `package_uuid` must be provided to properly support overrides from `Overrides.toml` entries in depots. If `include_lazy` is set to `true`, then lazy packages will be installed as well. """ function extract_all_hashes(artifacts_toml::String; platform::AbstractPlatform = HostPlatform(), pkg_uuid::Union{Nothing,Base.UUID} = nothing, include_lazy::Bool = false) hashes = Base.SHA1[] if !isfile(artifacts_toml) return hashes end artifact_dict = load_artifacts_toml(artifacts_toml; pkg_uuid=pkg_uuid) for name in keys(artifact_dict) # Get the metadata about this name for the requested platform meta = artifact_meta(name, artifact_dict, artifacts_toml; platform=platform) # If there are no instances of this name for the desired platform, skip it meta === nothing && continue # If it's a lazy one and we aren't including lazy ones, skip if get(meta, "lazy", false) && !include_lazy continue end # Otherwise, add it to the list! push!(hashes, Base.SHA1(meta["git-tree-sha1"])) end return hashes end # Support `AbstractString`s, but avoid compilers needing to track backedges for callers # of these functions in case a user defines a new type that is `<: AbstractString` archive_artifact(hash::SHA1, tarball_path::AbstractString; kwargs...) = archive_artifact(hash, string(tarball_path)::String; kwargs...) bind_artifact!(artifacts_toml::AbstractString, name::AbstractString, hash::SHA1; kwargs...) = bind_artifact!(string(artifacts_toml)::String, string(name)::String, hash; kwargs...) unbind_artifact!(artifacts_toml::AbstractString, name::AbstractString) = unbind_artifact!(string(artifacts_toml)::String, string(name)::String) download_artifact(tree_hash::SHA1, tarball_url::AbstractString, args...; kwargs...) = download_artifact(tree_hash, string(tarball_url)::String, args...; kwargs...) ensure_artifact_installed(name::AbstractString, artifacts_toml::AbstractString; kwargs...) = ensure_artifact_installed(string(name)::String, string(artifacts_toml)::String; kwargs...) ensure_artifact_installed(name::AbstractString, meta::Dict, artifacts_toml::AbstractString; kwargs...) = ensure_artifact_installed(string(name)::String, meta, string(artifacts_toml)::String; kwargs...) ensure_all_artifacts_installed(artifacts_toml::AbstractString; kwargs...) = ensure_all_artifacts_installed(string(artifacts_toml)::String; kwargs...) extract_all_hashes(artifacts_toml::AbstractString; kwargs...) = extract_all_hashes(string(artifacts_toml)::String; kwargs...) end # module Artifacts q���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/Operations.jl๎ซ�����# This file is a part of Julia. License is MIT: https://julialang.org/license module Operations using UUIDs using Random: randstring import LibGit2, Dates, TOML import REPL using REPL.TerminalMenus using ..Types, ..Resolve, ..PlatformEngines, ..GitTools, ..MiniProgressBars import ..depots, ..depots1, ..devdir, ..set_readonly, ..Types.PackageEntry import ..Artifacts: ensure_artifact_installed, artifact_names, extract_all_hashes, artifact_exists, select_downloadable_artifacts using Base.BinaryPlatforms import ...Pkg import ...Pkg: pkg_server, Registry, pathrepr, can_fancyprint, printpkgstyle, stderr_f, OFFLINE_MODE import ...Pkg: UPDATED_REGISTRY_THIS_SESSION, RESPECT_SYSIMAGE_VERSIONS, should_autoprecompile ######### # Utils # ######### function default_preserve() if Base.get_bool_env("JULIA_PKG_PRESERVE_TIERED_INSTALLED", false) PRESERVE_TIERED_INSTALLED else PRESERVE_TIERED end end function find_installed(name::String, uuid::UUID, sha1::SHA1) slug_default = Base.version_slug(uuid, sha1) # 4 used to be the default so look there first for slug in (slug_default, Base.version_slug(uuid, sha1, 4)) for depot in depots() path = abspath(depot, "packages", name, slug) ispath(path) && return path end end return abspath(depots1(), "packages", name, slug_default) end # more accurate name is `should_be_tracking_registered_version` # the only way to know for sure is to key into the registries tracking_registered_version(pkg::Union{PackageSpec, PackageEntry}, julia_version=VERSION) = !is_stdlib(pkg.uuid, julia_version) && pkg.path === nothing && pkg.repo.source === nothing function source_path(manifest_file::String, pkg::Union{PackageSpec, PackageEntry}, julia_version = VERSION) pkg.tree_hash !== nothing ? find_installed(pkg.name, pkg.uuid, pkg.tree_hash) : pkg.path !== nothing ? joinpath(dirname(manifest_file), pkg.path) : is_or_was_stdlib(pkg.uuid, julia_version) ? Types.stdlib_path(pkg.name) : nothing end #TODO rename function load_version(version, fixed, preserve::PreserveLevel) if version === nothing return VersionSpec() # some stdlibs dont have a version elseif fixed return version # dont change state if a package is fixed elseif preserve == PRESERVE_ALL || preserve == PRESERVE_ALL_INSTALLED || preserve == PRESERVE_DIRECT return something(version, VersionSpec()) elseif preserve == PRESERVE_SEMVER && version != VersionSpec() return Types.semver_spec("$(version.major).$(version.minor).$(version.patch)") elseif preserve == PRESERVE_NONE return VersionSpec() end end function load_direct_deps(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[]; preserve::PreserveLevel=PRESERVE_DIRECT) pkgs = copy(pkgs) for (name::String, uuid::UUID) in env.project.deps findfirst(pkg -> pkg.uuid == uuid, pkgs) === nothing || continue # do not duplicate packages entry = manifest_info(env.manifest, uuid) push!(pkgs, entry === nothing ? PackageSpec(;uuid=uuid, name=name) : PackageSpec(; uuid = uuid, name = name, path = entry.path, repo = entry.repo, pinned = entry.pinned, tree_hash = entry.tree_hash, # TODO should tree_hash be changed too? version = load_version(entry.version, isfixed(entry), preserve), )) end return pkgs end function load_manifest_deps(manifest::Manifest, pkgs::Vector{PackageSpec}=PackageSpec[]; preserve::PreserveLevel=PRESERVE_ALL) pkgs = copy(pkgs) for (uuid, entry) in manifest findfirst(pkg -> pkg.uuid == uuid, pkgs) === nothing || continue # do not duplicate packages push!(pkgs, PackageSpec( uuid = uuid, name = entry.name, path = entry.path, pinned = entry.pinned, repo = entry.repo, tree_hash = entry.tree_hash, # TODO should tree_hash be changed too? version = load_version(entry.version, isfixed(entry), preserve), )) end return pkgs end function load_all_deps(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[]; preserve::PreserveLevel=PRESERVE_ALL) pkgs = load_manifest_deps(env.manifest, pkgs; preserve=preserve) return load_direct_deps(env, pkgs; preserve=preserve) end function is_instantiated(env::EnvCache; platform = HostPlatform())::Bool # Load everything pkgs = load_all_deps(env) # If the top-level project is a package, ensure it is instantiated as well if env.pkg !== nothing # Top-level project may already be in the manifest (cyclic deps) # so only add it if it isn't there idx = findfirst(x -> x.uuid == env.pkg.uuid, pkgs) if idx === nothing push!(pkgs, Types.PackageSpec(name=env.pkg.name, uuid=env.pkg.uuid, version=env.pkg.version, path=dirname(env.project_file))) end else # Make sure artifacts for project exist even if it is not a package check_artifacts_downloaded(dirname(env.project_file); platform) || return false end # Make sure all paths/artifacts exist return all(pkg -> is_package_downloaded(env.manifest_file, pkg; platform), pkgs) end function update_manifest!(env::EnvCache, pkgs::Vector{PackageSpec}, deps_map, julia_version) manifest = env.manifest empty!(manifest) # if we're updating env.pkg that refers to another manifest, we want to change # pkg.path, if present, to be relative to the manifest instead of an abspath if env.project.manifest !== nothing && env.pkg.path !== nothing env.pkg.path = Types.relative_project_path(env.manifest_file, project_rel_path(env, source_path(env.manifest_file, env.pkg))) end if env.pkg !== nothing pkgs = push!(copy(pkgs), env.pkg::PackageSpec) end for pkg in pkgs entry = PackageEntry(;name = pkg.name, version = pkg.version, pinned = pkg.pinned, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid=pkg.uuid) if is_stdlib(pkg.uuid, julia_version) # Only set stdlib versions for versioned (external) stdlibs entry.version = stdlib_version(pkg.uuid, julia_version) end if Types.is_project(env, pkg) entry.deps = env.project.deps else entry.deps = deps_map[pkg.uuid] end env.manifest[pkg.uuid] = entry end prune_manifest(env) record_project_hash(env) end # This has to be done after the packages have been downloaded # since we need access to the Project file to read the information # about extensions function fixup_ext!(env, pkgs) for pkg in pkgs v = joinpath(source_path(env.manifest_file, pkg), "Project.toml") if haskey(env.manifest, pkg.uuid) entry = env.manifest[pkg.uuid] if isfile(v) p = Types.read_project(v) entry.weakdeps = p.weakdeps entry.exts = p.exts for (name, _) in p.weakdeps if !haskey(p.deps, name) delete!(entry.deps, name) end end end end end prune_manifest(env) end #################### # Registry Loading # #################### function load_tree_hash!(registries::Vector{Registry.RegistryInstance}, pkg::PackageSpec, julia_version) tracking_registered_version(pkg, julia_version) || return pkg hash = nothing for reg in registries reg_pkg = get(reg, pkg.uuid, nothing) reg_pkg === nothing && continue pkg_info = Registry.registry_info(reg_pkg) version_info = get(pkg_info.version_info, pkg.version, nothing) version_info === nothing && continue hashโ€ฒ = version_info.git_tree_sha1 if hash !== nothing hash == hashโ€ฒ || pkgerror("hash mismatch in registries for $(pkg.name) at version $(pkg.version)") end hash = hashโ€ฒ end pkg.tree_hash = hash return pkg end ####################################### # Dependency gathering and resolution # ####################################### get_compat(proj::Project, name::String) = haskey(proj.compat, name) ? proj.compat[name].val : Types.VersionSpec() get_compat_str(proj::Project, name::String) = haskey(proj.compat, name) ? proj.compat[name].str : nothing function set_compat(proj::Project, name::String, compat::String) semverspec = Types.semver_spec(compat, throw = false) isnothing(semverspec) && return false proj.compat[name] = Types.Compat(semverspec, compat) return true end function set_compat(proj::Project, name::String, ::Nothing) delete!(proj.compat, name) return true end function reset_all_compat!(proj::Project) for name in keys(proj.compat) compat = proj.compat[name] if compat.val != Types.semver_spec(compat.str) proj.compat[name] = Types.Compat(Types.semver_spec(compat.str), compat.str) end end return nothing end function collect_project(pkg::PackageSpec, path::String) deps = PackageSpec[] weakdeps = Set{UUID}() project_file = projectfile_path(path; strict=true) if project_file === nothing pkgerror("could not find project file for package $(err_rep(pkg)) at `$path`") end project = read_package(project_file) #= # TODO, this should either error or be quiet julia_compat = get_compat(project, "julia") if julia_compat !== nothing && !(VERSION in julia_compat) println(io, "julia version requirement for package $(err_rep(pkg)) not satisfied") end =# for (name, uuid) in project.deps vspec = get_compat(project, name) push!(deps, PackageSpec(name, uuid, vspec)) end for (name, uuid) in project.weakdeps vspec = get_compat(project, name) push!(deps, PackageSpec(name, uuid, vspec)) push!(weakdeps, uuid) end if project.version !== nothing pkg.version = project.version else # @warn("project file for $(pkg.name) is missing a `version` entry") pkg.version = VersionNumber(0) end return deps, weakdeps end is_tracking_path(pkg) = pkg.path !== nothing is_tracking_repo(pkg) = pkg.repo.source !== nothing is_tracking_registry(pkg) = !is_tracking_path(pkg) && !is_tracking_repo(pkg) isfixed(pkg) = !is_tracking_registry(pkg) || pkg.pinned function collect_developed!(env::EnvCache, pkg::PackageSpec, developed::Vector{PackageSpec}) source = project_rel_path(env, source_path(env.manifest_file, pkg)) source_env = EnvCache(projectfile_path(source)) pkgs = load_all_deps(source_env) for pkg in filter(is_tracking_path, pkgs) if any(x -> x.uuid == pkg.uuid, developed) continue end # normalize path pkg.path = Types.relative_project_path(env.manifest_file, project_rel_path(source_env, source_path(source_env.manifest_file, pkg))) push!(developed, pkg) collect_developed!(env, pkg, developed) end end function collect_developed(env::EnvCache, pkgs::Vector{PackageSpec}) developed = PackageSpec[] for pkg in filter(is_tracking_path, pkgs) collect_developed!(env, pkg, developed) end return developed end function collect_fixed!(env::EnvCache, pkgs::Vector{PackageSpec}, names::Dict{UUID, String}) deps_map = Dict{UUID,Vector{PackageSpec}}() weak_map = Dict{UUID,Set{UUID}}() if env.pkg !== nothing pkg = env.pkg deps, weakdeps = collect_project(pkg, dirname(env.project_file)) deps_map[pkg.uuid] = deps weak_map[pkg.uuid] = weakdeps names[pkg.uuid] = pkg.name end for pkg in pkgs path = project_rel_path(env, source_path(env.manifest_file, pkg)) if !isdir(path) pkgerror("expected package $(err_rep(pkg)) to exist at path `$path`") end deps, weakdeps = collect_project(pkg, path) deps_map[pkg.uuid] = deps weak_map[pkg.uuid] = weakdeps end fixed = Dict{UUID,Resolve.Fixed}() # Collect the dependencies for the fixed packages for (uuid, deps) in deps_map q = Dict{UUID, VersionSpec}() for dep in deps names[dep.uuid] = dep.name q[dep.uuid] = dep.version end if Types.is_project_uuid(env, uuid) fix_pkg = env.pkg else idx = findfirst(pkg -> pkg.uuid == uuid, pkgs) fix_pkg = pkgs[idx] end fixed[uuid] = Resolve.Fixed(fix_pkg.version, q, weak_map[uuid]) end return fixed end # drops build detail in version but keeps the main prerelease context # i.e. dropbuild(v"2.0.1-rc1.21321") == v"2.0.1-rc1" dropbuild(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, isempty(v.prerelease) ? () : (v.prerelease[1],)) # Resolve a set of versions given package version specs # looks at uuid, version, repo/path, # sets version to a VersionNumber # adds any other packages which may be in the dependency graph # all versioned packages should have a `tree_hash` function resolve_versions!(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}, julia_version, installed_only::Bool) installed_only = installed_only || OFFLINE_MODE[] # compatibility if julia_version !== nothing # only set the manifest julia_version if ctx.julia_version is not nothing env.manifest.julia_version = dropbuild(VERSION) v = intersect(julia_version, get_compat(env.project, "julia")) if isempty(v) @warn "julia version requirement for project not satisfied" _module=nothing _file=nothing end end jll_fix = Dict{UUID, VersionNumber}() for pkg in pkgs if !is_stdlib(pkg.uuid) && endswith(pkg.name, "_jll") && pkg.version isa VersionNumber jll_fix[pkg.uuid] = pkg.version end end names = Dict{UUID, String}(uuid => name for (uuid, (name, version)) in stdlibs()) # recursive search for packages which are tracking a path developed = collect_developed(env, pkgs) # But we only want to use information for those packages that we don't know about for pkg in developed if !any(x -> x.uuid == pkg.uuid, pkgs) push!(pkgs, pkg) end end # this also sets pkg.version for fixed packages fixed = collect_fixed!(env, filter(!is_tracking_registry, pkgs), names) # non fixed packages are `add`ed by version: their version is either restricted or free # fixed packages are `dev`ed or `add`ed by repo # at this point, fixed packages have a version and `deps` @assert length(Set(pkg.uuid::UUID for pkg in pkgs)) == length(pkgs) # check compat for pkg in pkgs compat = get_compat(env.project, pkg.name) v = intersect(pkg.version, compat) if isempty(v) throw(Resolve.ResolverError( "empty intersection between $(pkg.name)@$(pkg.version) and project compatibility $(compat)")) end # Work around not clobbering 0.x.y+ for checked out old type of packages if !(pkg.version isa VersionNumber) pkg.version = v end end for pkg in pkgs names[pkg.uuid] = pkg.name end # Unless using the unbounded or historical resolver, always allow stdlibs to update. Helps if the previous resolve # happened on a different julia version / commit and the stdlib version in the manifest is not the current stdlib version unbind_stdlibs = julia_version === VERSION reqs = Resolve.Requires(pkg.uuid => is_stdlib(pkg.uuid) && unbind_stdlibs ? VersionSpec("*") : VersionSpec(pkg.version) for pkg in pkgs) graph, compat_map = deps_graph(env, registries, names, reqs, fixed, julia_version, installed_only) Resolve.simplify_graph!(graph) vers = Resolve.resolve(graph) # Fixup jlls that got their build numbers stripped vers_fix = copy(vers) for (uuid, vers) in vers old_v = get(jll_fix, uuid, nothing) # We only fixup a JLL if the old major/minor/patch matches the new major/minor/patch if old_v !== nothing && Base.thispatch(old_v) == Base.thispatch(vers_fix[uuid]) vers_fix[uuid] = old_v end end vers = vers_fix # update vector of package versions for (uuid, ver) in vers idx = findfirst(p -> p.uuid == uuid, pkgs) if idx !== nothing pkg = pkgs[idx] # Fixed packages are not returned by resolve (they already have their version set) pkg.version = vers[pkg.uuid] else name = is_stdlib(uuid) ? first(stdlibs()[uuid]) : registered_name(registries, uuid) push!(pkgs, PackageSpec(;name=name, uuid=uuid, version=ver)) end end final_deps_map = Dict{UUID, Dict{String, UUID}}() for pkg in pkgs load_tree_hash!(registries, pkg, julia_version) deps = begin if pkg.uuid in keys(fixed) deps_fixed = Dict{String, UUID}() for dep in keys(fixed[pkg.uuid].requires) deps_fixed[names[dep]] = dep end deps_fixed else d = Dict{String, UUID}() for (uuid, _) in compat_map[pkg.uuid][pkg.version] d[names[uuid]] = uuid end d end end # julia is an implicit dependency filter!(d -> d.first != "julia", deps) final_deps_map[pkg.uuid] = deps end return final_deps_map end get_or_make!(d::Dict{K,V}, k::K) where {K,V} = get!(d, k) do; V() end const JULIA_UUID = UUID("1222c4b2-2114-5bfd-aeef-88e4692bbb3e") const PKGORIGIN_HAVE_VERSION = :version in fieldnames(Base.PkgOrigin) function deps_graph(env::EnvCache, registries::Vector{Registry.RegistryInstance}, uuid_to_name::Dict{UUID,String}, reqs::Resolve.Requires, fixed::Dict{UUID,Resolve.Fixed}, julia_version, installed_only::Bool) uuids = Set{UUID}() union!(uuids, keys(reqs)) union!(uuids, keys(fixed)) for fixed_uuids in map(fx->keys(fx.requires), values(fixed)) union!(uuids, fixed_uuids) end stdlibs_for_julia_version = Types.get_last_stdlibs(julia_version) seen = Set{UUID}() # pkg -> version -> (dependency => compat): all_compat = Dict{UUID,Dict{VersionNumber,Dict{UUID,VersionSpec}}}() weak_compat = Dict{UUID,Dict{VersionNumber,Set{UUID}}}() for (fp, fx) in fixed all_compat[fp] = Dict(fx.version => Dict{UUID,VersionSpec}()) end while true unseen = setdiff(uuids, seen) isempty(unseen) && break for uuid in unseen push!(seen, uuid) uuid in keys(fixed) && continue all_compat_u = get_or_make!(all_compat, uuid) weak_compat_u = get_or_make!(weak_compat, uuid) uuid_is_stdlib = false stdlib_name = "" stdlib_version = nothing if haskey(stdlibs_for_julia_version, uuid) uuid_is_stdlib = true stdlib_name, stdlib_version = stdlibs_for_julia_version[uuid] end # If we're requesting resolution of a package that is an # unregistered stdlib we must special-case it here. This is further # complicated by the fact that we can ask this question relative to # a Julia version. if (julia_version != VERSION && is_unregistered_stdlib(uuid)) || uuid_is_stdlib path = Types.stdlib_path(stdlibs_for_julia_version[uuid][1]) proj_file = projectfile_path(path; strict=true) @assert proj_file !== nothing proj = read_package(proj_file) v = something(proj.version, VERSION) # TODO look at compat section for stdlibs? all_compat_u_vr = get_or_make!(all_compat_u, v) for (_, other_uuid) in proj.deps push!(uuids, other_uuid) all_compat_u_vr[other_uuid] = VersionSpec() end if !isempty(proj.weakdeps) weak_all_compat_u_vr = get_or_make!(weak_compat_u, v) for (_, other_uuid) in proj.weakdeps push!(uuids, other_uuid) all_compat_u_vr[other_uuid] = VersionSpec() push!(weak_all_compat_u_vr, other_uuid) end end else for reg in registries pkg = get(reg, uuid, nothing) pkg === nothing && continue info = Registry.registry_info(pkg) function add_compat!(d, cinfo) for (v, compat_info) in cinfo # Filter yanked and if we are in offline mode also downloaded packages # TODO, pull this into a function Registry.isyanked(info, v) && continue if installed_only pkg_spec = PackageSpec(name=pkg.name, uuid=pkg.uuid, version=v, tree_hash=Registry.treehash(info, v)) is_package_downloaded(env.manifest_file, pkg_spec) || continue end # Skip package version that are not the same as external packages in sysimage if PKGORIGIN_HAVE_VERSION && RESPECT_SYSIMAGE_VERSIONS[] && julia_version == VERSION pkgid = Base.PkgId(uuid, pkg.name) if Base.in_sysimage(pkgid) pkgorigin = get(Base.pkgorigins, pkgid, nothing) if pkgorigin !== nothing && pkgorigin.version !== nothing if v != pkgorigin.version continue end end end end dv = get_or_make!(d, v) merge!(dv, compat_info) union!(uuids, keys(compat_info)) end end add_compat!(all_compat_u, Registry.compat_info(info)) weak_compat_info = Registry.weak_compat_info(info) if weak_compat_info !== nothing add_compat!(all_compat_u, weak_compat_info) # Version to Set for (v, compat_info) in weak_compat_info weak_compat_u[v] = keys(compat_info) end end end end end end for uuid in uuids uuid == JULIA_UUID && continue if !haskey(uuid_to_name, uuid) name = registered_name(registries, uuid) name === nothing && pkgerror("cannot find name corresponding to UUID $(uuid) in a registry") uuid_to_name[uuid] = name entry = manifest_info(env.manifest, uuid) entry โ‰ก nothing && continue uuid_to_name[uuid] = entry.name end end return Resolve.Graph(all_compat, weak_compat, uuid_to_name, reqs, fixed, false, julia_version), all_compat end ######################## # Package installation # ######################## function get_archive_url_for_version(url::String, ref) if (m = match(r"https://github.com/(.*?)/(.*?).git", url)) !== nothing return "https://api.github.com/repos/$(m.captures[1])/$(m.captures[2])/tarball/$(ref)" end return nothing end # Returns if archive successfully installed function install_archive( urls::Vector{Pair{String,Bool}}, hash::SHA1, version_path::String; io::IO=stderr_f() )::Bool tmp_objects = String[] url_success = false for (url, top) in urls path = tempname() * randstring(6) push!(tmp_objects, path) # for cleanup url_success = true try PlatformEngines.download(url, path; verbose=false, io=io) catch e e isa InterruptException && rethrow() url_success = false end url_success || continue dir = joinpath(tempdir(), randstring(12)) push!(tmp_objects, dir) # for cleanup # Might fail to extract an archive (https://github.com/JuliaPackaging/PkgServer.jl/issues/126) try unpack(path, dir; verbose=false) catch e e isa InterruptException && rethrow() @warn "failed to extract archive downloaded from $(url)" url_success = false end url_success || continue if top unpacked = dir else dirs = readdir(dir) # 7z on Win might create this spurious file filter!(x -> x != "pax_global_header", dirs) @assert length(dirs) == 1 unpacked = joinpath(dir, dirs[1]) end # Assert that the tarball unpacked to the tree sha we wanted # TODO: Enable on Windows when tree_hash handles # executable bits correctly, see JuliaLang/julia #33212. if !Sys.iswindows() if SHA1(GitTools.tree_hash(unpacked)) != hash @warn "tarball content does not match git-tree-sha1" url_success = false end url_success || continue end # Move content to version path !isdir(version_path) && mkpath(version_path) mv(unpacked, version_path; force=true) break # successful install end # Clean up and exit foreach(x -> Base.rm(x; force=true, recursive=true), tmp_objects) return url_success end const refspecs = ["+refs/*:refs/remotes/cache/*"] function install_git( io::IO, uuid::UUID, name::String, hash::SHA1, urls::Set{String}, version_path::String )::Nothing repo = nothing tree = nothing # TODO: Consolidate this with some of the repo handling in Types.jl try clones_dir = joinpath(depots1(), "clones") ispath(clones_dir) || mkpath(clones_dir) repo_path = joinpath(clones_dir, string(uuid)) repo = GitTools.ensure_clone(io, repo_path, first(urls); isbare=true, header = "[$uuid] $name from $(first(urls))") git_hash = LibGit2.GitHash(hash.bytes) for url in urls try LibGit2.with(LibGit2.GitObject, repo, git_hash) do g end break # object was found, we can stop catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() end GitTools.fetch(io, repo, url, refspecs=refspecs) end tree = try LibGit2.GitObject(repo, git_hash) catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() error("$name: git object $(string(hash)) could not be found") end tree isa LibGit2.GitTree || error("$name: git object $(string(hash)) should be a tree, not $(typeof(tree))") mkpath(version_path) GitTools.checkout_tree_to_path(repo, tree, version_path) return finally repo !== nothing && LibGit2.close(repo) tree !== nothing && LibGit2.close(tree) end end function collect_artifacts(pkg_root::String; platform::AbstractPlatform=HostPlatform()) # Check to see if this package has an (Julia)Artifacts.toml artifacts_tomls = Tuple{String,Base.TOML.TOMLDict}[] for f in artifact_names artifacts_toml = joinpath(pkg_root, f) if isfile(artifacts_toml) selector_path = joinpath(pkg_root, ".pkg", "select_artifacts.jl") # If there is a dynamic artifact selector, run that in an appropriate sandbox to select artifacts if isfile(selector_path) # Despite the fact that we inherit the project, since the in-memory manifest # has not been updated yet, if we try to load any dependencies, it may fail. # Therefore, this project inheritance is really only for Preferences, not dependencies. select_cmd = Cmd(`$(gen_build_code(selector_path; inherit_project=true)) -t1 --startup-file=no $(triplet(platform))`) meta_toml = String(read(select_cmd)) res = TOML.tryparse(meta_toml) if res isa TOML.ParserError errstr = sprint(showerror, res; context=stderr) pkgerror("failed to parse TOML output from running $(repr(selector_path)), got: \n$errstr") else push!(artifacts_tomls, (artifacts_toml, TOML.parse(meta_toml))) end else # Otherwise, use the standard selector from `Artifacts` artifacts = select_downloadable_artifacts(artifacts_toml; platform) push!(artifacts_tomls, (artifacts_toml, artifacts)) end break end end return artifacts_tomls end function download_artifacts(env::EnvCache; platform::AbstractPlatform=HostPlatform(), julia_version = VERSION, verbose::Bool=false, io::IO=stderr_f()) pkg_roots = String[] for (uuid, pkg) in env.manifest pkg = manifest_info(env.manifest, uuid) pkg_root = source_path(env.manifest_file, pkg, julia_version) pkg_root === nothing || push!(pkg_roots, pkg_root) end push!(pkg_roots, dirname(env.project_file)) for pkg_root in pkg_roots for (artifacts_toml, artifacts) in collect_artifacts(pkg_root; platform) # For each Artifacts.toml, install each artifact we've collected from it for name in keys(artifacts) ensure_artifact_installed(name, artifacts[name], artifacts_toml; verbose, quiet_download=!(io isa Base.TTY), io=io) end write_env_usage(artifacts_toml, "artifact_usage.toml") end end end function check_artifacts_downloaded(pkg_root::String; platform::AbstractPlatform=HostPlatform()) for (artifacts_toml, artifacts) in collect_artifacts(pkg_root; platform) for name in keys(artifacts) if !artifact_exists(Base.SHA1(artifacts[name]["git-tree-sha1"])) return false end break end end return true end function find_urls(registries::Vector{Registry.RegistryInstance}, uuid::UUID) urls = Set{String}() for reg in registries reg_pkg = get(reg, uuid, nothing) reg_pkg === nothing && continue info = Registry.registry_info(reg_pkg) repo = info.repo repo === nothing && continue push!(urls, repo) end return urls end function download_source(ctx::Context; readonly=true) pkgs_to_install = NamedTuple{(:pkg, :urls, :path), Tuple{PackageEntry, Set{String}, String}}[] for pkg in values(ctx.env.manifest) tracking_registered_version(pkg, ctx.julia_version) || continue path = source_path(ctx.env.manifest_file, pkg, ctx.julia_version) path === nothing && continue ispath(path) && continue urls = find_urls(ctx.registries, pkg.uuid) push!(pkgs_to_install, (;pkg, urls, path)) end length(pkgs_to_install) == 0 && return Set{UUID}() ######################################## # Install from archives asynchronously # ######################################## missed_packages = eltype(pkgs_to_install)[] widths = [textwidth(pkg.name) for (pkg, _) in pkgs_to_install] max_name = maximum(widths; init=0) # Check what registries the current pkg server tracks server_registry_info = Registry.pkg_server_registry_info() @sync begin jobs = Channel{eltype(pkgs_to_install)}(ctx.num_concurrent_downloads) results = Channel(ctx.num_concurrent_downloads) @async begin for pkg in pkgs_to_install put!(jobs, pkg) end end for i in 1:ctx.num_concurrent_downloads @async begin for (pkg, urls, path) in jobs if ctx.use_git_for_all_downloads put!(results, (pkg, false, (urls, path))) continue end try archive_urls = Pair{String,Bool}[] # Check if the current package is available in one of the registries being tracked by the pkg server # In that case, download from the package server if server_registry_info !== nothing server, registry_info = server_registry_info for reg in ctx.registries if reg.uuid in keys(registry_info) if haskey(reg, pkg.uuid) url = "$server/package/$(pkg.uuid)/$(pkg.tree_hash)" push!(archive_urls, url => true) break end end end end for repo_url in urls url = get_archive_url_for_version(repo_url, pkg.tree_hash) url !== nothing && push!(archive_urls, url => false) end success = install_archive(archive_urls, pkg.tree_hash, path, io=ctx.io) if success && readonly set_readonly(path) # In add mode, files should be read-only end if ctx.use_only_tarballs_for_downloads && !success pkgerror("failed to get tarball from $(urls)") end put!(results, (pkg, success, (urls, path))) catch err put!(results, (pkg, err, catch_backtrace())) end end end end bar = MiniProgressBar(; indent=2, header = "Progress", color = Base.info_color(), percentage=false, always_reprint=true) bar.max = length(pkgs_to_install) fancyprint = can_fancyprint(ctx.io) try for i in 1:length(pkgs_to_install) pkg::PackageEntry, exc_or_success, bt_or_pathurls = take!(results) exc_or_success isa Exception && pkgerror("Error when installing package $(pkg.name):\n", sprint(Base.showerror, exc_or_success, bt_or_pathurls)) success, (urls, path) = exc_or_success, bt_or_pathurls success || push!(missed_packages, (; pkg, urls, path)) bar.current = i str = sprint(; context=ctx.io) do io if success fancyprint && print_progress_bottom(io) vstr = if pkg.version !== nothing "v$(pkg.version)" else short_treehash = string(pkg.tree_hash)[1:16] "[$short_treehash]" end printpkgstyle(io, :Installed, string(rpad(pkg.name * " ", max_name + 2, "โ”€"), " ", vstr)) fancyprint && show_progress(io, bar) end end print(ctx.io, str) end finally fancyprint && end_progress(ctx.io, bar) close(jobs) end end ################################################## # Use LibGit2 to download any remaining packages # ################################################## for (pkg, urls, path) in missed_packages uuid = pkg.uuid install_git(ctx.io, pkg.uuid, pkg.name, pkg.tree_hash, urls, path) readonly && set_readonly(path) vstr = if pkg.version !== nothing "v$(pkg.version)" else short_treehash = string(pkg.tree_hash)[1:16] "[$short_treehash]" end printpkgstyle(ctx.io, :Installed, string(rpad(pkg.name * " ", max_name + 2, "โ”€"), " ", vstr)) end return Set{UUID}(entry.pkg.uuid for entry in pkgs_to_install) end ################################ # Manifest update and pruning # ################################ project_rel_path(env::EnvCache, path::String) = normpath(joinpath(dirname(env.manifest_file), path)) function prune_manifest(env::EnvCache) # if project uses another manifest, only prune project entry in manifest if dirname(env.project_file) != dirname(env.manifest_file) proj_entry = env.manifest[env.project.uuid] proj_entry.deps = env.project.deps else keep = collect(values(env.project.deps)) env.manifest = prune_manifest(env.manifest, keep) end return env.manifest end function prune_manifest(manifest::Manifest, keep::Vector{UUID}) while !isempty(keep) clean = true for (uuid, entry) in manifest uuid in keep || continue for dep in values(entry.deps) dep in keep && continue push!(keep, dep) clean = false end end clean && break end manifest.deps = Dict(uuid => entry for (uuid, entry) in manifest if uuid in keep) return manifest end function record_project_hash(env::EnvCache) env.manifest.other["project_hash"] = Types.project_resolve_hash(env.project) end ######### # Build # ######### get_deps(env::EnvCache, new_uuids::Set{UUID}) = _get_deps!(Set{UUID}(), env, new_uuids) function _get_deps!(collected_uuids::Set{UUID}, env::EnvCache, new_uuids) for uuid in new_uuids is_stdlib(uuid) && continue uuid in collected_uuids && continue push!(collected_uuids, uuid) children_uuids = if Types.is_project_uuid(env, uuid) Set(values(env.project.deps)) else info = manifest_info(env.manifest, uuid) if info === nothing pkgerror("could not find manifest entry for package with uuid $(uuid)") end Set(values(info.deps)) end _get_deps!(collected_uuids, env, children_uuids) end return collected_uuids end # TODO: This function should be replaceable with `is_instantiated` but # see https://github.com/JuliaLang/Pkg.jl/issues/2470 function any_package_not_installed(manifest::Manifest) for (uuid, entry) in manifest if Base.locate_package(Base.PkgId(uuid, entry.name)) === nothing return true end end return false end function build(ctx::Context, uuids::Set{UUID}, verbose::Bool) if any_package_not_installed(ctx.env.manifest) || !isfile(ctx.env.manifest_file) Pkg.instantiate(ctx, allow_build = false, allow_autoprecomp = false) end all_uuids = get_deps(ctx.env, uuids) build_versions(ctx, all_uuids; verbose) end function dependency_order_uuids(env::EnvCache, uuids::Vector{UUID})::Dict{UUID,Int} order = Dict{UUID,Int}() seen = UUID[] k::Int = 0 function visit(uuid::UUID) uuid in seen && return @warn("Dependency graph not a DAG, linearizing anyway") haskey(order, uuid) && return push!(seen, uuid) if Types.is_project_uuid(env, uuid) deps = values(env.project.deps) else entry = manifest_info(env.manifest, uuid) deps = values(entry.deps) end foreach(visit, deps) pop!(seen) order[uuid] = k += 1 end visit(uuid::String) = visit(UUID(uuid)) foreach(visit, uuids) return order end function gen_build_code(build_file::String; inherit_project::Bool = false) code = """ $(Base.load_path_setup_code(false)) cd($(repr(dirname(build_file)))) include($(repr(build_file))) """ # This will make it so that running Pkg.build runs the build in a session with --startup=no # *unless* the parent julia session is started with --startup=yes explicitly. startup_flag = Base.JLOptions().startupfile == 1 ? "yes" : "no" return ``` $(Base.julia_cmd()) -O0 --color=no --history-file=no --startup-file=$startup_flag $(inherit_project ? `--project=$(Base.active_project())` : ``) --eval $code ``` end with_load_path(f::Function, new_load_path::String) = with_load_path(f, [new_load_path]) function with_load_path(f::Function, new_load_path::Vector{String}) old_load_path = copy(Base.LOAD_PATH) copy!(Base.LOAD_PATH, new_load_path) try f() finally copy!(LOAD_PATH, old_load_path) end end const PkgUUID = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" pkg_scratchpath() = joinpath(depots1(), "scratchspaces", PkgUUID) builddir(source_path::String) = joinpath(source_path, "deps") buildfile(source_path::String) = joinpath(builddir(source_path), "build.jl") function build_versions(ctx::Context, uuids::Set{UUID}; verbose=false) # collect builds for UUIDs with `deps/build.jl` files builds = Tuple{UUID,String,String,VersionNumber}[] for uuid in uuids is_stdlib(uuid) && continue if Types.is_project_uuid(ctx.env, uuid) path = dirname(ctx.env.project_file) name = ctx.env.pkg.name version = ctx.env.pkg.version else entry = manifest_info(ctx.env.manifest, uuid) if entry === nothing error("could not find entry with uuid $uuid in manifest $(ctx.env.manifest_file)") end name = entry.name path = source_path(ctx.env.manifest_file, entry) if path === nothing pkgerror("Failed to find path for package $name") end version = something(entry.version, v"0.0") end ispath(path) || error("Build path for $name does not exist: $path") ispath(buildfile(path)) && push!(builds, (uuid, name, path, version)) end # toposort builds by dependencies order = dependency_order_uuids(ctx.env, map(first, builds)) sort!(builds, by = build -> order[first(build)]) max_name = maximum(build->textwidth(build[2]), builds; init=0) bar = MiniProgressBar(; indent=2, header = "Progress", color = Base.info_color(), percentage=false, always_reprint=true) bar.max = length(builds) fancyprint = can_fancyprint(ctx.io) fancyprint && start_progress(ctx.io, bar) # build each package versions in a child process try for (n, (uuid, name, source_path, version)) in enumerate(builds) pkg = PackageSpec(;uuid=uuid, name=name, version=version) build_file = buildfile(source_path) # compatibility shim local build_project_override, build_project_preferences if isfile(projectfile_path(builddir(source_path))) build_project_override = nothing with_load_path([builddir(source_path), Base.LOAD_PATH...]) do build_project_preferences = Base.get_preferences() end else build_project_override = gen_target_project(ctx, pkg, source_path, "build") with_load_path([something(projectfile_path(source_path)), Base.LOAD_PATH...]) do build_project_preferences = Base.get_preferences() end end # Put log output in Pkg's scratchspace if the package is content addressed # by tree sha and in the build directory if it is tracked by path etc. entry = manifest_info(ctx.env.manifest, uuid) if entry !== nothing && entry.tree_hash !== nothing key = string(entry.tree_hash) scratch = joinpath(pkg_scratchpath(), key) mkpath(scratch) log_file = joinpath(scratch, "build.log") # Associate the logfile with the package being built dict = Dict{String,Any}(scratch => [ Dict{String,Any}("time" => Dates.now(), "parent_projects" => [projectfile_path(source_path)]) ]) open(joinpath(depots1(), "logs", "scratch_usage.toml"), "a") do io TOML.print(io, dict) end else log_file = splitext(build_file)[1] * ".log" end fancyprint && print_progress_bottom(ctx.io) printpkgstyle(ctx.io, :Building, rpad(name * " ", max_name + 1, "โ”€") * "โ†’ " * pathrepr(log_file)) bar.current = n-1 fancyprint && show_progress(ctx.io, bar) let log_file=log_file sandbox(ctx, pkg, source_path, builddir(source_path), build_project_override; preferences=build_project_preferences) do flush(ctx.io) ok = open(log_file, "w") do log std = verbose ? ctx.io : log success(pipeline(gen_build_code(buildfile(source_path)), stdout=std, stderr=std)) end ok && return n_lines = isinteractive() ? 100 : 5000 # TODO: Extract last n lines more efficiently log_lines = readlines(log_file) log_show = join(log_lines[max(1, length(log_lines) - n_lines):end], '\n') full_log_at, last_lines = if length(log_lines) > n_lines "\n\nFull log at $log_file", ", showing the last $n_lines of log" else "", "" end pkgerror("Error building `$(pkg.name)`$last_lines: \n$log_show$full_log_at") end end end finally fancyprint && end_progress(ctx.io, bar) end return end ############## # Operations # ############## function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode::PackageMode) drop = UUID[] # find manifest-mode drops if mode == PKGMODE_MANIFEST for pkg in pkgs info = manifest_info(ctx.env.manifest, pkg.uuid) if info !== nothing pkg.uuid in drop || push!(drop, pkg.uuid) else str = has_name(pkg) ? pkg.name : string(pkg.uuid) @warn("`$str` not in manifest, ignoring") end end end # drop reverse dependencies while !isempty(drop) clean = true for (uuid, entry) in ctx.env.manifest deps = values(entry.deps) isempty(drop โˆฉ deps) && continue uuid โˆ‰ drop || continue push!(drop, uuid) clean = false end clean && break end # find project-mode drops if mode == PKGMODE_PROJECT for pkg in pkgs found = false for (name::String, uuid::UUID) in ctx.env.project.deps pkg.name == name || pkg.uuid == uuid || continue pkg.name == name || error("project file name mismatch for `$uuid`: $(pkg.name) โ‰  $name") pkg.uuid == uuid || error("project file UUID mismatch for `$name`: $(pkg.uuid) โ‰  $uuid") uuid in drop || push!(drop, uuid) found = true break end found && continue str = has_name(pkg) ? pkg.name : string(pkg.uuid) @warn("`$str` not in project, ignoring") end end # delete drops from project n = length(ctx.env.project.deps) filter!(ctx.env.project.deps) do (_, uuid) uuid โˆ‰ drop end if length(ctx.env.project.deps) == n println(ctx.io, "No changes") return end # only declare `compat` for remaining direct or `extra` dependencies # `julia` is always an implicit direct dependency filter!(ctx.env.project.compat) do (name, _) name == "julia" || name in keys(ctx.env.project.deps) || name in keys(ctx.env.project.extras) || name in keys(ctx.env.project.weakdeps) end deps_names = union(keys(ctx.env.project.deps), keys(ctx.env.project.extras)) filter!(ctx.env.project.targets) do (target, deps) !isempty(filter!(in(deps_names), deps)) end # only keep reachable manifest entries prune_manifest(ctx.env) record_project_hash(ctx.env) # update project & manifest write_env(ctx.env) show_update(ctx.env, ctx.registries; io=ctx.io) end update_package_add(ctx::Context, pkg::PackageSpec, ::Nothing, is_dep::Bool) = pkg function update_package_add(ctx::Context, pkg::PackageSpec, entry::PackageEntry, is_dep::Bool) if entry.pinned if pkg.version == VersionSpec() println(ctx.io, "`$(pkg.name)` is pinned at `v$(entry.version)`: maintaining pinned version") end return PackageSpec(; uuid=pkg.uuid, name=pkg.name, pinned=true, version=entry.version, tree_hash=entry.tree_hash, path=entry.path, repo=entry.repo) end if entry.path !== nothing || entry.repo.source !== nothing || pkg.repo.source !== nothing return pkg # overwrite everything, nothing to copy over end if is_stdlib(pkg.uuid) return pkg # stdlibs are not versioned like other packages elseif is_dep && ((isa(pkg.version, VersionNumber) && entry.version == pkg.version) || (!isa(pkg.version, VersionNumber) && entry.version โˆˆ pkg.version)) # leave the package as is at the installed version return PackageSpec(; uuid=pkg.uuid, name=pkg.name, version=entry.version, tree_hash=entry.tree_hash) end # adding a new version not compatible with the old version, so we just overwrite return pkg end # Update registries AND read them back in. function update_registries(ctx::Context; force::Bool=true, kwargs...) OFFLINE_MODE[] && return !force && UPDATED_REGISTRY_THIS_SESSION[] && return Registry.update(; io=ctx.io, kwargs...) copy!(ctx.registries, Registry.reachable_registries()) UPDATED_REGISTRY_THIS_SESSION[] = true end function is_all_registered(registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}) pkgs = filter(tracking_registered_version, pkgs) for pkg in pkgs if !any(r->haskey(r, pkg.uuid), registries) return pkg end end return true end function check_registered(registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}) pkg = is_all_registered(registries, pkgs) if pkg isa PackageSpec pkgerror("expected package $(err_rep(pkg)) to be registered") end return nothing end # Check if the package can be added without colliding/overwriting things function assert_can_add(ctx::Context, pkgs::Vector{PackageSpec}) for pkg in pkgs @assert pkg.name !== nothing && pkg.uuid !== nothing # package with the same name exist in the project: assert that they have the same uuid existing_uuid = get(ctx.env.project.deps, pkg.name, pkg.uuid) existing_uuid == pkg.uuid || pkgerror("""Refusing to add package $(err_rep(pkg)). Package `$(pkg.name)=$(existing_uuid)` with the same name already exists as a direct dependency. To remove the existing package, use `import Pkg; Pkg.rm("$(pkg.name)")`. """) # package with the same uuid exist in the project: assert they have the same name name = findfirst(==(pkg.uuid), ctx.env.project.deps) name === nothing || name == pkg.name || pkgerror("""Refusing to add package $(err_rep(pkg)). Package `$name=$(pkg.uuid)` with the same UUID already exists as a direct dependency. To remove the existing package, use `import Pkg; Pkg.rm("$name")`. """) # package with the same uuid exist in the manifest: assert they have the same name entry = get(ctx.env.manifest, pkg.uuid, nothing) entry === nothing || entry.name == pkg.name || pkgerror("""Refusing to add package $(err_rep(pkg)). Package `$(entry.name)=$(pkg.uuid)` with the same UUID already exists in the manifest. To remove the existing package, use `import Pkg; Pkg.rm(Pkg.PackageSpec(uuid="$(pkg.uuid)"); mode=Pkg.PKGMODE_MANIFEST)`. """) end end function tiered_resolve(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}, julia_version, try_all_installed::Bool) if try_all_installed try # do not modify existing subgraph and only add installed versions of the new packages @debug "tiered_resolve: trying PRESERVE_ALL_INSTALLED" return targeted_resolve(env, registries, pkgs, PRESERVE_ALL_INSTALLED, julia_version) catch err err isa Resolve.ResolverError || rethrow() end end try # do not modify existing subgraph @debug "tiered_resolve: trying PRESERVE_ALL" return targeted_resolve(env, registries, pkgs, PRESERVE_ALL, julia_version) catch err err isa Resolve.ResolverError || rethrow() end try # do not modify existing direct deps @debug "tiered_resolve: trying PRESERVE_DIRECT" return targeted_resolve(env, registries, pkgs, PRESERVE_DIRECT, julia_version) catch err err isa Resolve.ResolverError || rethrow() end try @debug "tiered_resolve: trying PRESERVE_SEMVER" return targeted_resolve(env, registries, pkgs, PRESERVE_SEMVER, julia_version) catch err err isa Resolve.ResolverError || rethrow() end @debug "tiered_resolve: trying PRESERVE_NONE" return targeted_resolve(env, registries, pkgs, PRESERVE_NONE, julia_version) end function targeted_resolve(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}, preserve::PreserveLevel, julia_version) if preserve == PRESERVE_ALL || preserve == PRESERVE_ALL_INSTALLED pkgs = load_all_deps(env, pkgs; preserve) else pkgs = load_direct_deps(env, pkgs; preserve) end check_registered(registries, pkgs) deps_map = resolve_versions!(env, registries, pkgs, julia_version, preserve == PRESERVE_ALL_INSTALLED) return pkgs, deps_map end function _resolve(io::IO, env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}, preserve::PreserveLevel, julia_version) printpkgstyle(io, :Resolving, "package versions...") if preserve == PRESERVE_TIERED_INSTALLED tiered_resolve(env, registries, pkgs, julia_version, true) elseif preserve == PRESERVE_TIERED tiered_resolve(env, registries, pkgs, julia_version, false) else targeted_resolve(env, registries, pkgs, preserve, julia_version) end end function add(ctx::Context, pkgs::Vector{PackageSpec}, new_git=Set{UUID}(); preserve::PreserveLevel=default_preserve(), platform::AbstractPlatform=HostPlatform()) assert_can_add(ctx, pkgs) # load manifest data for (i, pkg) in pairs(pkgs) entry = manifest_info(ctx.env.manifest, pkg.uuid) is_dep = any(uuid -> uuid == pkg.uuid, [uuid for (name, uuid) in ctx.env.project.deps]) pkgs[i] = update_package_add(ctx, pkg, entry, is_dep) end foreach(pkg -> ctx.env.project.deps[pkg.name] = pkg.uuid, pkgs) # update set of deps # resolve pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, pkgs, preserve, ctx.julia_version) update_manifest!(ctx.env, pkgs, deps_map, ctx.julia_version) new_apply = download_source(ctx) fixup_ext!(ctx.env, pkgs) # After downloading resolutionary packages, search for (Julia)Artifacts.toml files # and ensure they are all downloaded and unpacked as well: download_artifacts(ctx.env, platform=platform, julia_version=ctx.julia_version, io=ctx.io) write_env(ctx.env) # write env before building show_update(ctx.env, ctx.registries; io=ctx.io) build_versions(ctx, union(new_apply, new_git)) end # Input: name, uuid, and path function develop(ctx::Context, pkgs::Vector{PackageSpec}, new_git::Set{UUID}; preserve::PreserveLevel=default_preserve(), platform::AbstractPlatform=HostPlatform()) assert_can_add(ctx, pkgs) # no need to look at manifest.. dev will just nuke whatever is there before for pkg in pkgs ctx.env.project.deps[pkg.name] = pkg.uuid end # resolve & apply package versions pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, pkgs, preserve, ctx.julia_version) update_manifest!(ctx.env, pkgs, deps_map, ctx.julia_version) new_apply = download_source(ctx) fixup_ext!(ctx.env, pkgs) download_artifacts(ctx.env; platform=platform, julia_version=ctx.julia_version, io=ctx.io) write_env(ctx.env) # write env before building show_update(ctx.env, ctx.registries; io=ctx.io) build_versions(ctx, union(new_apply, new_git)) end # load version constraint # if version isa VersionNumber -> set tree_hash too up_load_versions!(ctx::Context, pkg::PackageSpec, ::Nothing, level::UpgradeLevel) = false function up_load_versions!(ctx::Context, pkg::PackageSpec, entry::PackageEntry, level::UpgradeLevel) entry.version !== nothing || return false # no version to set if entry.pinned || level == UPLEVEL_FIXED pkg.version = entry.version pkg.tree_hash = entry.tree_hash elseif entry.repo.source !== nothing # repo packages have a version but are treated special pkg.repo = entry.repo if level == UPLEVEL_MAJOR # Updating a repo package is equivalent to adding it new = Types.handle_repo_add!(ctx, pkg) pkg.version = entry.version if pkg.tree_hash != entry.tree_hash # TODO parse find_installed and set new version end return new else pkg.version = entry.version pkg.tree_hash = entry.tree_hash end else ver = entry.version r = level == UPLEVEL_PATCH ? VersionRange(ver.major, ver.minor) : level == UPLEVEL_MINOR ? VersionRange(ver.major) : level == UPLEVEL_MAJOR ? VersionRange() : error("unexpected upgrade level: $level") pkg.version = VersionSpec(r) end return false end up_load_manifest_info!(pkg::PackageSpec, ::Nothing) = nothing function up_load_manifest_info!(pkg::PackageSpec, entry::PackageEntry) pkg.name = entry.name # TODO check name is same pkg.repo = entry.repo # TODO check that repo is same pkg.path = entry.path pkg.pinned = entry.pinned # `pkg.version` and `pkg.tree_hash` is set by `up_load_versions!` end function load_manifest_deps_up(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[]; preserve::PreserveLevel=PRESERVE_ALL) manifest = env.manifest project = env.project explicit_upgraded = Set(pkg.uuid for pkg in pkgs) recursive_indirect_dependencies_of_explicitly_upgraded = Set{UUID}() frontier = copy(explicit_upgraded) new_frontier = Set{UUID}() while !(isempty(frontier)) for uuid in frontier entry = get(env.manifest, uuid, nothing) entry === nothing && continue uuid_deps = values(entry.deps) for uuid_dep in uuid_deps if !(uuid_dep in recursive_indirect_dependencies_of_explicitly_upgraded) # push!(recursive_indirect_dependencies_of_explicitly_upgraded, uuid_dep) push!(new_frontier, uuid_dep) end end end copy!(frontier, new_frontier) empty!(new_frontier) end pkgs = copy(pkgs) for (uuid, entry) in manifest findfirst(pkg -> pkg.uuid == uuid, pkgs) === nothing || continue # do not duplicate packages uuid in explicit_upgraded && continue # Allow explicit upgraded packages to upgrade. if preserve == PRESERVE_NONE && uuid in recursive_indirect_dependencies_of_explicitly_upgraded continue elseif preserve == PRESERVE_DIRECT && uuid in recursive_indirect_dependencies_of_explicitly_upgraded && !(uuid in values(project.deps)) continue end # The rest of the packages get fixed push!(pkgs, PackageSpec( uuid = uuid, name = entry.name, path = entry.path, pinned = entry.pinned, repo = entry.repo, tree_hash = entry.tree_hash, # TODO should tree_hash be changed too? version = something(entry.version, VersionSpec()) )) end return pkgs end function targeted_resolve_up(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}, preserve::PreserveLevel, julia_version) pkgs = load_manifest_deps_up(env, pkgs; preserve=preserve) check_registered(registries, pkgs) deps_map = resolve_versions!(env, registries, pkgs, julia_version, preserve == PRESERVE_ALL_INSTALLED) return pkgs, deps_map end function up(ctx::Context, pkgs::Vector{PackageSpec}, level::UpgradeLevel; skip_writing_project::Bool=false, preserve::Union{Nothing,PreserveLevel}=nothing) new_git = Set{UUID}() # TODO check all pkg.version == VersionSpec() # set version constraints according to `level` for pkg in pkgs new = up_load_versions!(ctx, pkg, manifest_info(ctx.env.manifest, pkg.uuid), level) new && push!(new_git, pkg.uuid) #TODO put download + push! in utility function end # load rest of manifest data (except for version info) for pkg in pkgs up_load_manifest_info!(pkg, manifest_info(ctx.env.manifest, pkg.uuid)) end if preserve !== nothing pkgs, deps_map = targeted_resolve_up(ctx.env, ctx.registries, pkgs, preserve, ctx.julia_version) else pkgs = load_direct_deps(ctx.env, pkgs; preserve = (level == UPLEVEL_FIXED ? PRESERVE_NONE : PRESERVE_DIRECT)) check_registered(ctx.registries, pkgs) deps_map = resolve_versions!(ctx.env, ctx.registries, pkgs, ctx.julia_version, false) end update_manifest!(ctx.env, pkgs, deps_map, ctx.julia_version) new_apply = download_source(ctx) fixup_ext!(ctx.env, pkgs) download_artifacts(ctx.env, julia_version=ctx.julia_version, io=ctx.io) write_env(ctx.env; skip_writing_project) # write env before building show_update(ctx.env, ctx.registries; io=ctx.io, hidden_upgrades_info = true) build_versions(ctx, union(new_apply, new_git)) end function update_package_pin!(registries::Vector{Registry.RegistryInstance}, pkg::PackageSpec, entry::Union{Nothing, PackageEntry}) if entry === nothing pkgerror("package $(err_rep(pkg)) not found in the manifest, run `Pkg.resolve()` and retry.") end #if entry.pinned && pkg.version == VersionSpec() # println(ctx.io, "package $(err_rep(pkg)) already pinned") #end # update pinned package pkg.pinned = true if is_stdlib(pkg.uuid) return nothing # nothing left to do elseif pkg.version == VersionSpec() pkg.version = entry.version # pin at current version pkg.repo = entry.repo pkg.tree_hash = entry.tree_hash pkg.path = entry.path else # given explicit registered version if entry.repo.source !== nothing || entry.path !== nothing # A pin in this case includes an implicit `free` to switch to tracking registered versions # First, make sure the package is registered so we have something to free to if is_all_registered(registries, [pkg]) !== true pkgerror("unable to pin unregistered package $(err_rep(pkg)) to an arbitrary version") end end end end is_fully_pinned(ctx::Context) = !isempty(ctx.env.manifest.deps) && all(kv -> last(kv).pinned, ctx.env.manifest.deps) function pin(ctx::Context, pkgs::Vector{PackageSpec}) foreach(pkg -> update_package_pin!(ctx.registries, pkg, manifest_info(ctx.env.manifest, pkg.uuid)), pkgs) pkgs = load_direct_deps(ctx.env, pkgs) # TODO: change pin to not take a version and just have it pin on the current version. Then there is no need to resolve after a pin pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, pkgs, PRESERVE_TIERED, ctx.julia_version) update_manifest!(ctx.env, pkgs, deps_map, ctx.julia_version) new = download_source(ctx) fixup_ext!(ctx.env, pkgs) download_artifacts(ctx.env; julia_version=ctx.julia_version, io=ctx.io) write_env(ctx.env) # write env before building show_update(ctx.env, ctx.registries; io=ctx.io) build_versions(ctx, new) end function update_package_free!(registries::Vector{Registry.RegistryInstance}, pkg::PackageSpec, entry::PackageEntry, err_if_free::Bool) if entry.pinned pkg.pinned = false is_stdlib(pkg.uuid) && return # nothing left to do pkg.version = entry.version pkg.repo = entry.repo pkg.tree_hash = entry.tree_hash return end if entry.path !== nothing || entry.repo.source !== nothing # make sure the package is registered so we have something to free to if is_all_registered(registries, [pkg]) !== true pkgerror("unable to free unregistered package $(err_rep(pkg))") end return # -> name, uuid end if err_if_free pkgerror("expected package $(err_rep(pkg)) to be pinned, tracking a path,", " or tracking a repository") end return end # TODO: this is two technically different operations with the same name # split into two subfunctions ... function free(ctx::Context, pkgs::Vector{PackageSpec}; err_if_free=true) foreach(pkg -> update_package_free!(ctx.registries, pkg, manifest_info(ctx.env.manifest, pkg.uuid), err_if_free), pkgs) if any(pkg -> pkg.version == VersionSpec(), pkgs) pkgs = load_direct_deps(ctx.env, pkgs) check_registered(ctx.registries, pkgs) # TODO: change free to not take a version and just have it pin on the current version. Then there is no need to resolve after a pin pkgs, deps_map = _resolve(ctx.io, ctx.env, ctx.registries, pkgs, PRESERVE_TIERED, ctx.julia_version) update_manifest!(ctx.env, pkgs, deps_map, ctx.julia_version) new = download_source(ctx) fixup_ext!(ctx.env, pkgs) download_artifacts(ctx.env, io=ctx.io) write_env(ctx.env) # write env before building show_update(ctx.env, ctx.registries; io=ctx.io) build_versions(ctx, new) else foreach(pkg -> manifest_info(ctx.env.manifest, pkg.uuid).pinned = false, pkgs) write_env(ctx.env) show_update(ctx.env, ctx.registries; io=ctx.io) end end function gen_test_code(source_path::String; coverage, julia_args::Cmd, test_args::Cmd) test_file = testfile(source_path) code = """ $(Base.load_path_setup_code(false)) cd($(repr(dirname(test_file)))) append!(empty!(ARGS), $(repr(test_args.exec))) include($(repr(test_file))) """ return gen_subprocess_cmd(code, source_path; coverage, julia_args) end function gen_test_precompile_code(source_path::String; coverage, julia_args::Cmd, test_args::Cmd) # Note that we cannot load the dev-ed Pkg here during Pkg testing # so the `Pkg.precompile` that is run here is the one in the sysimage code = """ Pkg = Base.require(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")) $(Base.load_path_setup_code(false)) append!(empty!(ARGS), $(repr(test_args.exec))) Pkg.precompile(warn_loaded = false) """ return gen_subprocess_cmd(code, source_path; coverage, julia_args) end function get_threads_spec() if Threads.nthreads(:interactive) > 0 "$(Threads.nthreads(:default)),$(Threads.nthreads(:interactive))" else "$(Threads.nthreads(:default))" end end function gen_subprocess_cmd(code::String, source_path::String; coverage, julia_args) coverage_arg = if coverage isa Bool coverage ? string("@", source_path) : "none" elseif coverage isa AbstractString coverage else throw(ArgumentError("coverage should be a boolean or a string.")) end return ``` $(Base.julia_cmd()) --code-coverage=$(coverage_arg) --color=$(Base.have_color === nothing ? "auto" : Base.have_color ? "yes" : "no") --compiled-modules=$(Bool(Base.JLOptions().use_compiled_modules) ? "yes" : "no") --check-bounds=yes --warn-overwrite=yes --depwarn=$(Base.JLOptions().depwarn == 2 ? "error" : "yes") --inline=$(Bool(Base.JLOptions().can_inline) ? "yes" : "no") --startup-file=$(Base.JLOptions().startupfile == 1 ? "yes" : "no") --track-allocation=$(("none", "user", "all")[Base.JLOptions().malloc_log + 1]) --threads=$(get_threads_spec()) $(julia_args) --eval $(code) ``` end function with_temp_env(fn::Function, temp_env::String) load_path = copy(LOAD_PATH) active_project = Base.ACTIVE_PROJECT[] try push!(empty!(LOAD_PATH), "@", temp_env) Base.ACTIVE_PROJECT[] = nothing fn() finally append!(empty!(LOAD_PATH), load_path) Base.ACTIVE_PROJECT[] = active_project end end # pick out a set of subgraphs and preserve their versions function sandbox_preserve(env::EnvCache, target::PackageSpec, test_project::String) env = deepcopy(env) # include root in manifest (in case any dependencies point back to it) if env.pkg !== nothing env.manifest[env.pkg.uuid] = PackageEntry(;name=env.pkg.name, path=dirname(env.project_file), deps=env.project.deps) end # if the source manifest is an old format, upgrade the manifest_format so # that warnings aren't thrown for the temp sandbox manifest if env.manifest.manifest_format < v"2.0" env.manifest.manifest_format = v"2.0" end # preserve important nodes keep = [target.uuid] append!(keep, collect(values(read_project(test_project).deps))) record_project_hash(env) # prune and return return prune_manifest(env.manifest, keep) end function abspath!(env::EnvCache, manifest::Manifest) for (uuid, entry) in manifest if entry.path !== nothing entry.path = project_rel_path(env, entry.path) end end return manifest end # ctx + pkg used to compute parent dep graph function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::String, sandbox_path::String, sandbox_project_override; preferences::Union{Nothing,Dict{String,Any}} = nothing, force_latest_compatible_version::Bool=false, allow_earlier_backwards_compatible_versions::Bool=true, allow_reresolve::Bool=true) active_manifest = manifestfile_path(dirname(ctx.env.manifest_file)) sandbox_project = projectfile_path(sandbox_path) mktempdir() do tmp tmp_project = projectfile_path(tmp) tmp_manifest = manifestfile_path(tmp) tmp_preferences = joinpath(tmp, first(Base.preferences_names)) # Copy env info over to temp env if sandbox_project_override !== nothing Types.write_project(sandbox_project_override, tmp_project) elseif isfile(sandbox_project) cp(sandbox_project, tmp_project) chmod(tmp_project, 0o600) end # create merged manifest # - copy over active subgraph # - abspath! to maintain location of all deved nodes working_manifest = abspath!(ctx.env, sandbox_preserve(ctx.env, target, tmp_project)) # - copy over fixed subgraphs from test subgraph # really only need to copy over "special" nodes sandbox_env = Types.EnvCache(projectfile_path(sandbox_path)) sandbox_manifest = abspath!(sandbox_env, sandbox_env.manifest) for (name, uuid) in sandbox_env.project.deps entry = get(sandbox_manifest, uuid, nothing) if entry !== nothing && isfixed(entry) subgraph = prune_manifest(sandbox_manifest, [uuid]) for (uuid, entry) in subgraph if haskey(working_manifest, uuid) pkgerror("can not merge projects") end working_manifest[uuid] = entry end end end Types.write_manifest(working_manifest, tmp_manifest) # Copy over preferences if preferences !== nothing open(tmp_preferences, "w") do io TOML.print(io, preferences::Dict{String, Any}) end end # sandbox with_temp_env(tmp) do temp_ctx = Context() temp_ctx.env.project.deps[target.name] = target.uuid if force_latest_compatible_version apply_force_latest_compatible_version!( temp_ctx; target_name = target.name, allow_earlier_backwards_compatible_versions, ) end try Pkg.resolve(temp_ctx; io=devnull, skip_writing_project=true) @debug "Using _parent_ dep graph" catch err# TODO err isa Resolve.ResolverError || rethrow() allow_reresolve || rethrow() @debug err @warn "Could not use exact versions of packages in manifest, re-resolving" temp_ctx.env.manifest.deps = Dict(uuid => entry for (uuid, entry) in temp_ctx.env.manifest.deps if isfixed(entry)) Pkg.resolve(temp_ctx; io=devnull, skip_writing_project=true) @debug "Using _clean_ dep graph" end reset_all_compat!(temp_ctx.env.project) write_env(temp_ctx.env, update_undo = false) # Run sandboxed code path_sep = Sys.iswindows() ? ';' : ':' withenv(fn, "JULIA_LOAD_PATH" => "@$(path_sep)$(tmp)", "JULIA_PROJECT" => nothing) end end end # Mostly here to give PkgEval some more coverage for packages # that still use test/REQUIRE. Ignores version bounds function parse_REQUIRE(require_path::String) packages = String[] for entry in eachline(require_path) if startswith(entry, '#') || isempty(entry) continue end # For lines like @osx Foo, ignore @osx words = split(entry) if startswith(words[1], '@') popfirst!(words) end push!(packages, popfirst!(words)) end return packages end # "targets" based test deps -> "test/Project.toml" based deps function gen_target_project(ctx::Context, pkg::PackageSpec, source_path::String, target::String) env = ctx.env registries = ctx.registries test_project = Types.Project() if projectfile_path(source_path; strict=true) === nothing # no project file, assuming this is an old REQUIRE package test_project.deps = copy(env.manifest[pkg.uuid].deps) if target == "test" test_REQUIRE_path = joinpath(source_path, "test", "REQUIRE") if isfile(test_REQUIRE_path) @warn "using test/REQUIRE files is deprecated and current support is lacking in some areas" test_pkgs = parse_REQUIRE(test_REQUIRE_path) package_specs = [PackageSpec(name=pkg) for pkg in test_pkgs] registry_resolve!(registries, package_specs) stdlib_resolve!(package_specs) ensure_resolved(ctx, env.manifest, package_specs, registry=true) for spec in package_specs test_project.deps[spec.name] = spec.uuid end end end return test_project end # collect relevant info from source source_env = EnvCache(projectfile_path(source_path)) # collect regular dependencies test_project.deps = source_env.project.deps # collect test dependencies for name in get(source_env.project.targets, target, String[]) uuid = nothing for list in [source_env.project.extras, source_env.project.weakdeps] uuid = get(list, name, nothing) uuid === nothing || break end if uuid === nothing pkgerror("`$name` declared as a `$target` dependency, but no such entry in `extras` or `weakdeps`") end test_project.deps[name] = uuid end # collect compat entries for (name, uuid) in test_project.deps compat = get_compat_str(source_env.project, name) compat === nothing && continue set_compat(test_project, name, compat) end return test_project end testdir(source_path::String) = joinpath(source_path, "test") testfile(source_path::String) = joinpath(testdir(source_path), "runtests.jl") function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, julia_args::Cmd=``, test_args::Cmd=``, test_fn=nothing, force_latest_compatible_version::Bool=false, allow_earlier_backwards_compatible_versions::Bool=true, allow_reresolve::Bool=true) Pkg.instantiate(ctx; allow_autoprecomp = false) # do precomp later within sandbox # load manifest data for pkg in pkgs is_stdlib(pkg.uuid) && continue if Types.is_project_uuid(ctx.env, pkg.uuid) pkg.path = dirname(ctx.env.project_file) pkg.version = ctx.env.pkg.version else entry = manifest_info(ctx.env.manifest, pkg.uuid) pkg.version = entry.version pkg.tree_hash = entry.tree_hash pkg.repo = entry.repo pkg.path = entry.path pkg.pinned = entry.pinned end end # See if we can find the test files for all packages missing_runtests = String[] source_paths = String[] # source_path is the package root (not /src) for pkg in pkgs sourcepath = project_rel_path(ctx.env, source_path(ctx.env.manifest_file, pkg, ctx.julia_version)) # TODO !isfile(testfile(sourcepath)) && push!(missing_runtests, pkg.name) push!(source_paths, sourcepath) end if !isempty(missing_runtests) pkgerror(length(missing_runtests) == 1 ? "Package " : "Packages ", join(missing_runtests, ", "), " did not provide a `test/runtests.jl` file") end # sandbox pkgs_errored = Tuple{String, Base.Process}[] for (pkg, source_path) in zip(pkgs, source_paths) # compatibility shim between "targets" and "test/Project.toml" local test_project_preferences, test_project_override if isfile(projectfile_path(testdir(source_path))) test_project_override = nothing with_load_path([testdir(source_path), Base.LOAD_PATH...]) do test_project_preferences = Base.get_preferences() end else test_project_override = gen_target_project(ctx, pkg, source_path, "test") with_load_path([something(projectfile_path(source_path)), Base.LOAD_PATH...]) do test_project_preferences = Base.get_preferences() end end # now we sandbox printpkgstyle(ctx.io, :Testing, pkg.name) sandbox(ctx, pkg, source_path, testdir(source_path), test_project_override; preferences=test_project_preferences, force_latest_compatible_version, allow_earlier_backwards_compatible_versions, allow_reresolve) do test_fn !== nothing && test_fn() sandbox_ctx = Context(;io=ctx.io) status(sandbox_ctx.env, sandbox_ctx.registries; mode=PKGMODE_COMBINED, io=sandbox_ctx.io, ignore_indent = false, show_usagetips = false) if should_autoprecompile() # Precompile in a child process with the test julia args to ensure native caches match test setup cmd = gen_test_precompile_code(source_path; coverage, julia_args, test_args) p, interrupted = subprocess_handler(cmd, ctx, sandbox_ctx, "Precompilation of test environment interrupted. Exiting the test precompilation process") if !success(p) if interrupted return else printpkgstyle(ctx.io, :Testing, "Precompilation of test environment failed. Continuing to tests", color = Base.warn_color()) end end end printpkgstyle(ctx.io, :Testing, "Running tests...") flush(ctx.io) cmd = gen_test_code(source_path; coverage, julia_args, test_args) p, interrupted = subprocess_handler(cmd, ctx, sandbox_ctx, "Tests interrupted. Exiting the test process") if success(p) printpkgstyle(ctx.io, :Testing, pkg.name * " tests passed ") elseif !interrupted push!(pkgs_errored, (pkg.name, p)) end end end # TODO: Should be included in Base function signal_name(signal::Integer) if signal == Base.SIGHUP "HUP" elseif signal == Base.SIGINT "INT" elseif signal == Base.SIGQUIT "QUIT" elseif signal == Base.SIGKILL "KILL" elseif signal == Base.SIGPIPE "PIPE" elseif signal == Base.SIGTERM "TERM" else string(signal) end end # report errors if !isempty(pkgs_errored) function reason(p) if Base.process_signaled(p) " (received signal: " * signal_name(p.termsignal) * ")" elseif Base.process_exited(p) && p.exitcode != 1 " (exit code: " * string(p.exitcode) * ")" else "" end end if length(pkgs_errored) == 1 pkg_name, p = first(pkgs_errored) pkgerror("Package $pkg_name errored during testing$(reason(p))") else failures = ["โ€ข $pkg_name$(reason(p))" for (pkg_name, p) in pkgs_errored] pkgerror("Packages errored during testing:\n", join(failures, "\n")) end end end # Handles the interrupting of a subprocess gracefully to avoid orphaning function subprocess_handler(cmd::Cmd, ctx, sandbox_ctx, error_msg::String) @debug "Running command" cmd p = run(pipeline(ignorestatus(cmd), stdout = sandbox_ctx.io, stderr = stderr_f()), wait = false) interrupted = false try wait(p) catch e if e isa InterruptException interrupted = true print("\n") printpkgstyle(ctx.io, :Testing, "$error_msg\n", color = Base.error_color()) # Give some time for the child interrupt handler to print a stacktrace and exit, # then kill the process if still running if timedwait(() -> !process_running(p), 4) == :timed_out kill(p, Base.SIGKILL) end else rethrow() end end return p, interrupted end # Display function stat_rep(x::PackageSpec; name=true) name = name ? "$(x.name)" : "" version = x.version == VersionSpec() ? "" : "v$(x.version)" rev = "" if x.repo.rev !== nothing rev = occursin(r"\b([a-f0-9]{40})\b", x.repo.rev) ? x.repo.rev[1:7] : x.repo.rev end subdir_str = x.repo.subdir === nothing ? "" : ":$(x.repo.subdir)" repo = Operations.is_tracking_repo(x) ? "`$(x.repo.source)$(subdir_str)#$(rev)`" : "" path = Operations.is_tracking_path(x) ? "$(pathrepr(x.path))" : "" pinned = x.pinned ? "โšฒ" : "" return join(filter(!isempty, [name,version,repo,path,pinned]), " ") end print_single(io::IO, pkg::PackageSpec) = print(io, stat_rep(pkg)) is_instantiated(::Nothing) = false is_instantiated(x::PackageSpec) = x.version != VersionSpec() || is_stdlib(x.uuid) # Compare an old and new node of the dependency graph and print a single line to summarize the change function print_diff(io::IO, old::Union{Nothing,PackageSpec}, new::Union{Nothing,PackageSpec}) if !is_instantiated(old) && is_instantiated(new) printstyled(io, "+ $(stat_rep(new))"; color=:light_green) elseif !is_instantiated(new) printstyled(io, "- $(stat_rep(old))"; color=:light_red) elseif is_tracking_registry(old) && is_tracking_registry(new) && new.version isa VersionNumber && old.version isa VersionNumber && new.version != old.version if new.version > old.version printstyled(io, "โ†‘ $(stat_rep(old)) โ‡’ $(stat_rep(new; name=false))"; color=:light_yellow) else printstyled(io, "โ†“ $(stat_rep(old)) โ‡’ $(stat_rep(new; name=false))"; color=:light_magenta) end else printstyled(io, "~ $(stat_rep(old)) โ‡’ $(stat_rep(new; name=false))"; color=:light_yellow) end end function status_compat_info(pkg::PackageSpec, env::EnvCache, regs::Vector{Registry.RegistryInstance}) pkg.version isa VersionNumber || return nothing # Can happen when there is no manifest manifest, project = env.manifest, env.project packages_holding_back = String[] max_version, max_version_in_compat = v"0", v"0" for reg in regs reg_pkg = get(reg, pkg.uuid, nothing) reg_pkg === nothing && continue info = Registry.registry_info(reg_pkg) reg_compat_info = Registry.compat_info(info) versions = keys(reg_compat_info) versions = filter(v -> !Registry.isyanked(info, v), versions) max_version_reg = maximum(versions; init=v"0") max_version = max(max_version, max_version_reg) compat_spec = get_compat(env.project, pkg.name) versions_in_compat = filter(in(compat_spec), keys(reg_compat_info)) max_version_in_compat = max(max_version_in_compat, maximum(versions_in_compat; init=v"0")) end max_version == v"0" && return nothing pkg.version >= max_version && return nothing pkgid = Base.PkgId(pkg.uuid, pkg.name) if PKGORIGIN_HAVE_VERSION && RESPECT_SYSIMAGE_VERSIONS[] && Base.in_sysimage(pkgid) pkgorigin = get(Base.pkgorigins, pkgid, nothing) if pkgorigin !== nothing && pkg.version !== nothing && pkg.version == pkgorigin.version return ["sysimage"], max_version, max_version_in_compat end end # Check compat of project if pkg.version == max_version_in_compat && max_version_in_compat != max_version return ["compat"], max_version, max_version_in_compat end manifest_info = get(manifest, pkg.uuid, nothing) manifest_info === nothing && return nothing # Check compat of dependencies for (uuid, dep_pkg) in manifest is_stdlib(uuid) && continue if !(pkg.uuid in values(dep_pkg.deps)) continue end dep_info = get(manifest, uuid, nothing) dep_info === nothing && continue for reg in regs reg_pkg = get(reg, uuid, nothing) reg_pkg === nothing && continue info = Registry.registry_info(reg_pkg) reg_compat_info = Registry.compat_info(info) compat_info_v = get(reg_compat_info, dep_info.version, nothing) compat_info_v === nothing && continue compat_info_v_uuid = get(compat_info_v, pkg.uuid, nothing) compat_info_v_uuid === nothing && continue if !(max_version in compat_info_v_uuid) push!(packages_holding_back, dep_pkg.name) end end end # Check compat with Julia itself julia_compatible_versions = Set{VersionNumber}() for reg in regs reg_pkg = get(reg, pkg.uuid, nothing) reg_pkg === nothing && continue info = Registry.registry_info(reg_pkg) reg_compat_info = Registry.compat_info(info) compat_info_v = get(reg_compat_info, pkg.version, nothing) versions = keys(reg_compat_info) for v in versions compat_info_v = get(reg_compat_info, v, nothing) compat_info_v === nothing && continue compat_info_v_uuid = compat_info_v[JULIA_UUID] if VERSION in compat_info_v_uuid push!(julia_compatible_versions, v) end end end if !(max_version in julia_compatible_versions) push!(packages_holding_back, "julia") end return sort!(unique!(packages_holding_back)), max_version, max_version_in_compat end function diff_array(old_env::Union{EnvCache,Nothing}, new_env::EnvCache; manifest=true) function index_pkgs(pkgs, uuid) idx = findfirst(pkg -> pkg.uuid == uuid, pkgs) return idx === nothing ? nothing : pkgs[idx] end # load deps new = manifest ? load_manifest_deps(new_env.manifest) : load_direct_deps(new_env) T, S = Union{UUID,Nothing}, Union{PackageSpec,Nothing} if old_env === nothing return Tuple{T,S,S}[(pkg.uuid, nothing, pkg)::Tuple{T,S,S} for pkg in new] end old = manifest ? load_manifest_deps(old_env.manifest) : load_direct_deps(old_env) # merge old and new into single array all_uuids = union(T[pkg.uuid for pkg in old], T[pkg.uuid for pkg in new]) return Tuple{T,S,S}[(uuid, index_pkgs(old, uuid), index_pkgs(new, uuid))::Tuple{T,S,S} for uuid in all_uuids] end function is_package_downloaded(manifest_file::String, pkg::PackageSpec; platform=HostPlatform()) sourcepath = source_path(manifest_file, pkg) identifier = pkg.name !== nothing ? pkg.name : pkg.uuid (sourcepath === nothing) && pkgerror("Could not locate the source code for the $(identifier) package. Are you trying to use a manifest generated by a different version of Julia?") isdir(sourcepath) || return false check_artifacts_downloaded(sourcepath; platform) || return false return true end function status_ext_info(pkg::PackageSpec, env::EnvCache) manifest = env.manifest manifest_info = get(manifest, pkg.uuid, nothing) manifest_info === nothing && return nothing weakdepses = manifest_info.weakdeps exts = manifest_info.exts if !isempty(weakdepses) && !isempty(exts) v = ExtInfo[] for (ext, extdeps) in exts extdeps isa String && (extdeps = String[extdeps]) ext_loaded = (Base.get_extension(Base.PkgId(pkg.uuid, pkg.name), Symbol(ext)) !== nothing) # Check if deps are loaded extdeps_info= Tuple{String, Bool}[] for extdep in extdeps uuid = weakdepses[extdep] loaded = haskey(Base.loaded_modules, Base.PkgId(uuid, extdep)) push!(extdeps_info, (extdep, loaded)) end push!(v, ExtInfo((ext, ext_loaded), extdeps_info)) end return v end return nothing end struct ExtInfo ext::Tuple{String, Bool} # name, loaded weakdeps::Vector{Tuple{String, Bool}} # name, loaded end struct PackageStatusData uuid::UUID old::Union{Nothing, PackageSpec} new::Union{Nothing, PackageSpec} downloaded::Bool upgradable::Bool heldback::Bool compat_data::Union{Nothing, Tuple{Vector{String}, VersionNumber, VersionNumber}} changed::Bool extinfo::Union{Nothing, Vector{ExtInfo}} end function print_status(env::EnvCache, old_env::Union{Nothing,EnvCache}, registries::Vector{Registry.RegistryInstance}, header::Symbol, uuids::Vector, names::Vector; manifest=true, diff=false, ignore_indent::Bool, outdated::Bool, extensions::Bool, io::IO, mode::PackageMode, hidden_upgrades_info::Bool, show_usagetips::Bool=true) not_installed_indicator = sprint((io, args) -> printstyled(io, args...; color=Base.error_color()), "โ†’", context=io) upgradable_indicator = sprint((io, args) -> printstyled(io, args...; color=:green), "โŒƒ", context=io) heldback_indicator = sprint((io, args) -> printstyled(io, args...; color=Base.warn_color()), "โŒ…", context=io) filter = !isempty(uuids) || !isempty(names) # setup xs = diff_array(old_env, env; manifest=manifest) # filter and return early if possible if isempty(xs) && !diff printpkgstyle(io, header, "$(pathrepr(manifest ? env.manifest_file : env.project_file)) (empty " * (manifest ? "manifest" : "project") * ")", ignore_indent) return nothing end no_changes = all(p-> p[2] == p[3], xs) if no_changes printpkgstyle(io, Symbol("No Changes"), "to $(pathrepr(manifest ? env.manifest_file : env.project_file))", ignore_indent) else xs = !filter ? xs : eltype(xs)[(id, old, new) for (id, old, new) in xs if (id in uuids || something(new, old).name in names)] if isempty(xs) printpkgstyle(io, Symbol("No Matches"), "in $(diff ? "diff for " : "")$(pathrepr(manifest ? env.manifest_file : env.project_file))", ignore_indent) return nothing end # main print printpkgstyle(io, header, pathrepr(manifest ? env.manifest_file : env.project_file), ignore_indent) # Sort stdlibs and _jlls towards the end in status output xs = sort!(xs, by = (x -> (is_stdlib(x[1]), endswith(something(x[3], x[2]).name, "_jll"), something(x[3], x[2]).name, x[1]))) end all_packages_downloaded = true no_packages_upgradable = true no_visible_packages_heldback = true no_packages_heldback = true lpadding = 2 package_statuses = PackageStatusData[] for (uuid, old, new) in xs if Types.is_project_uuid(env, uuid) continue end latest_version = true # Outdated info cinfo = nothing ext_info = nothing if !isnothing(new) && !is_stdlib(new.uuid) cinfo = status_compat_info(new, env, registries) if cinfo !== nothing latest_version = false end end # if we are running with outdated, only show packages that are upper bounded if outdated && latest_version continue end if !isnothing(new) && !is_stdlib(new.uuid) ext_info = status_ext_info(new, env) end if extensions && ext_info === nothing continue end # TODO: Show extension deps for project as well? pkg_downloaded = !is_instantiated(new) || is_package_downloaded(env.manifest_file, new) new_ver_avail = !latest_version && !Operations.is_tracking_repo(new) && !Operations.is_tracking_path(new) pkg_upgradable = new_ver_avail && isempty(cinfo[1]) pkg_heldback = new_ver_avail && !isempty(cinfo[1]) if !pkg_downloaded && (pkg_upgradable || pkg_heldback) # allow space in the gutter for two icons on a single line lpadding = 3 end changed = old != new all_packages_downloaded &= (!changed || pkg_downloaded) no_packages_upgradable &= (!changed || !pkg_upgradable) no_visible_packages_heldback &= (!changed || !pkg_heldback) no_packages_heldback &= !pkg_heldback push!(package_statuses, PackageStatusData(uuid, old, new, pkg_downloaded, pkg_upgradable, pkg_heldback, cinfo, changed, ext_info)) end for pkg in package_statuses diff && !pkg.changed && continue # in diff mode don't print packages that didn't change pad = 0 print_padding(x) = (print(io, x); pad += 1) if !pkg.downloaded print_padding(not_installed_indicator) elseif lpadding > 2 print_padding(" ") end if pkg.upgradable print_padding(upgradable_indicator) elseif pkg.heldback print_padding(heldback_indicator) end # Fill the remaining padding with spaces while pad < lpadding print_padding(" ") end printstyled(io, "[", string(pkg.uuid)[1:8], "] "; color = :light_black) diff ? print_diff(io, pkg.old, pkg.new) : print_single(io, pkg.new) if outdated && !diff && pkg.compat_data !== nothing packages_holding_back, max_version, max_version_compat = pkg.compat_data if pkg.new.version !== max_version_compat && max_version_compat != max_version printstyled(io, " [<v", max_version_compat, "]", color=:light_magenta) printstyled(io, ",") end printstyled(io, " (<v", max_version, ")"; color=Base.warn_color()) if packages_holding_back == ["compat"] printstyled(io, " [compat]"; color=:light_magenta) elseif packages_holding_back == ["sysimage"] printstyled(io, " [sysimage]"; color=:light_magenta) else pkg_str = isempty(packages_holding_back) ? "" : string(": ", join(packages_holding_back, ", ")) printstyled(io, pkg_str; color=Base.warn_color()) end end if extensions && !diff && pkg.extinfo !== nothing println(io) for (i, ext) in enumerate(pkg.extinfo) sym = i == length(pkg.extinfo) ? 'โ””' : 'โ”œ' function print_ext_entry(io, (name, installed)) color = installed ? :light_green : :light_black printstyled(io, name, ;color) end print(io, " ", sym, "โ”€ ") print_ext_entry(io, ext.ext) print(io, " [") join(io,sprint.(print_ext_entry, ext.weakdeps; context=io), ", ") print(io, "]") if i != length(pkg.extinfo) println(io) end end end println(io) end if !no_changes && !all_packages_downloaded printpkgstyle(io, :Info, "Packages marked with $not_installed_indicator are not downloaded, use `instantiate` to download", color=Base.info_color(), ignore_indent) end if !outdated && (mode != PKGMODE_COMBINED || (manifest == true)) tipend = manifest ? " -m" : "" tip = show_usagetips ? " To see why use `status --outdated$tipend`" : "" if !no_packages_upgradable && no_visible_packages_heldback printpkgstyle(io, :Info, "Packages marked with $upgradable_indicator have new versions available and may be upgradable.", color=Base.info_color(), ignore_indent) end if !no_visible_packages_heldback && no_packages_upgradable printpkgstyle(io, :Info, "Packages marked with $heldback_indicator have new versions available but compatibility constraints restrict them from upgrading.$tip", color=Base.info_color(), ignore_indent) end if !no_visible_packages_heldback && !no_packages_upgradable printpkgstyle(io, :Info, "Packages marked with $upgradable_indicator and $heldback_indicator have new versions available. Those with $upgradable_indicator may be upgradable, but those with $heldback_indicator are restricted by compatibility constraints from upgrading.$tip", color=Base.info_color(), ignore_indent) end if !manifest && hidden_upgrades_info && no_visible_packages_heldback && !no_packages_heldback # only warn if showing project and outdated indirect deps are hidden printpkgstyle(io, :Info, "Some packages have new versions but compatibility constraints restrict them from upgrading.$tip", color=Base.info_color(), ignore_indent) end end return nothing end function git_head_env(env, project_dir) new_env = EnvCache() try LibGit2.with(LibGit2.GitRepo(project_dir)) do repo git_path = LibGit2.path(repo) project_path = relpath(env.project_file, git_path) manifest_path = relpath(env.manifest_file, git_path) new_env.project = read_project(GitTools.git_file_stream(repo, "HEAD:$project_path", fakeit=true)) new_env.manifest = read_manifest(GitTools.git_file_stream(repo, "HEAD:$manifest_path", fakeit=true)) return new_env end catch err err isa PkgError || rethrow(err) return nothing end end function show_update(env::EnvCache, registries::Vector{Registry.RegistryInstance}; io::IO, hidden_upgrades_info = false) old_env = EnvCache() old_env.project = env.original_project old_env.manifest = env.original_manifest status(env, registries; header=:Updating, mode=PKGMODE_COMBINED, env_diff=old_env, ignore_indent=false, io=io, hidden_upgrades_info) return nothing end function status(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}=PackageSpec[]; header=nothing, mode::PackageMode=PKGMODE_PROJECT, git_diff::Bool=false, env_diff=nothing, ignore_indent=true, io::IO, outdated::Bool=false, extensions::Bool=false, hidden_upgrades_info::Bool=false, show_usagetips::Bool=true) io == Base.devnull && return # if a package, print header if header === nothing && env.pkg !== nothing printpkgstyle(io, :Project, string(env.pkg.name, " v", env.pkg.version), true; color=Base.info_color()) end # load old env old_env = nothing if git_diff project_dir = dirname(env.project_file) if !ispath(joinpath(project_dir, ".git")) @warn "diff option only available for environments in git repositories, ignoring." else old_env = git_head_env(env, project_dir) if old_env === nothing @warn "could not read project from HEAD, displaying absolute status instead." end end elseif env_diff !== nothing old_env = env_diff end # display filter_uuids = [pkg.uuid::UUID for pkg in pkgs if pkg.uuid !== nothing] filter_names = [pkg.name::String for pkg in pkgs if pkg.name !== nothing] diff = old_env !== nothing header = something(header, diff ? :Diff : :Status) if mode == PKGMODE_PROJECT || mode == PKGMODE_COMBINED print_status(env, old_env, registries, header, filter_uuids, filter_names; manifest=false, diff, ignore_indent, io, outdated, extensions, mode, hidden_upgrades_info, show_usagetips) end if mode == PKGMODE_MANIFEST || mode == PKGMODE_COMBINED print_status(env, old_env, registries, header, filter_uuids, filter_names; diff, ignore_indent, io, outdated, extensions, mode, hidden_upgrades_info, show_usagetips) end if is_manifest_current(env) === false tip = show_usagetips ? " It is recommended to `Pkg.resolve()` or consider `Pkg.update()` if necessary." : "" printpkgstyle(io, :Warning, "The project dependencies or compat requirements have changed since the manifest was last resolved.$tip", ignore_indent; color=Base.warn_color()) end end function is_manifest_current(env::EnvCache) if haskey(env.manifest.other, "project_hash") recorded_hash = env.manifest.other["project_hash"] current_hash = Types.project_resolve_hash(env.project) return recorded_hash == current_hash else # Manifest doesn't have a hash of the source Project recorded return nothing end end function compat_line(io, pkg, uuid, compat_str, longest_dep_len; indent = " ") iob = IOBuffer() ioc = IOContext(iob, :color => get(io, :color, false)::Bool) if isnothing(uuid) print(ioc, "$indent ") else printstyled(ioc, "$indent[", string(uuid)[1:8], "] "; color = :light_black) end print(ioc, rpad(pkg, longest_dep_len)) if isnothing(compat_str) printstyled(ioc, " none"; color = :light_black) else print(ioc, " ", compat_str) end return String(take!(iob)) end function print_compat(ctx::Context, pkgs_in::Vector{PackageSpec} = PackageSpec[]; io = nothing) io = something(io, ctx.io) printpkgstyle(io, :Compat, pathrepr(ctx.env.project_file)) names = [pkg.name for pkg in pkgs_in] pkgs = isempty(pkgs_in) ? ctx.env.project.deps : filter(pkg -> in(first(pkg), names), ctx.env.project.deps) add_julia = isempty(pkgs_in) || any(p->p.name == "julia", pkgs_in) longest_dep_len = isempty(pkgs) ? length("julia") : max(reduce(max, map(length, collect(keys(pkgs)))), length("julia")) if add_julia println(io, compat_line(io, "julia", nothing, get_compat_str(ctx.env.project, "julia"), longest_dep_len)) end for (dep, uuid) in pkgs println(io, compat_line(io, dep, uuid, get_compat_str(ctx.env.project, dep), longest_dep_len)) end end print_compat(pkg::String; kwargs...) = print_compat(Context(), pkg; kwargs...) print_compat(; kwargs...) = print_compat(Context(); kwargs...) function apply_force_latest_compatible_version!(ctx::Types.Context; target_name = nothing, allow_earlier_backwards_compatible_versions::Bool = true) deps_from_env = load_direct_deps(ctx.env) deps = [(; name = x.name, uuid = x.uuid) for x in deps_from_env] for dep in deps if !is_stdlib(dep.uuid) apply_force_latest_compatible_version!( ctx, dep; target_name, allow_earlier_backwards_compatible_versions, ) end end return nothing end function apply_force_latest_compatible_version!(ctx::Types.Context, dep::NamedTuple{(:name, :uuid), Tuple{String, Base.UUID}}; target_name = nothing, allow_earlier_backwards_compatible_versions::Bool = true) name, uuid = dep has_compat = haskey(ctx.env.project.compat, name) if !has_compat if name != target_name @warn( "Dependency does not have a [compat] entry", name, uuid, target_name, ) end return nothing end old_compat_spec = ctx.env.project.compat[name].val latest_compatible_version = get_latest_compatible_version( ctx, uuid, old_compat_spec, ) earliest_backwards_compatible_version = get_earliest_backwards_compatible_version(latest_compatible_version) if allow_earlier_backwards_compatible_versions version_for_intersect = only_major_minor_patch(earliest_backwards_compatible_version) else version_for_intersect = only_major_minor_patch(latest_compatible_version) end compat_for_intersect = Pkg.Types.semver_spec("โ‰ฅ $(version_for_intersect)") new_compat_spec = Base.intersect(old_compat_spec, compat_for_intersect) ctx.env.project.compat[name].val = new_compat_spec return nothing end function only_major_minor_patch(ver::Base.VersionNumber) return Base.VersionNumber(ver.major, ver.minor, ver.patch) end function get_earliest_backwards_compatible_version(ver::Base.VersionNumber) (ver.major > 0) && return Base.VersionNumber(ver.major, 0, 0) (ver.minor > 0) && return Base.VersionNumber(0, ver.minor, 0) return Base.VersionNumber(0, 0, ver.patch) end function get_latest_compatible_version(ctx::Types.Context, uuid::Base.UUID, compat_spec::VersionSpec) all_registered_versions = get_all_registered_versions(ctx, uuid) compatible_versions = filter(in(compat_spec), all_registered_versions) latest_compatible_version = maximum(compatible_versions) return latest_compatible_version end function get_all_registered_versions(ctx::Types.Context, uuid::Base.UUID) versions = Set{VersionNumber}() for reg in ctx.registries pkg = get(reg, uuid, nothing) if pkg !== nothing info = Registry.registry_info(pkg) union!(versions, keys(info.version_info)) end end return versions end end # module j���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/API.jlax�����# This file is a part of Julia. License is MIT: https://julialang.org/license module API using UUIDs using Printf import Random using Dates import LibGit2 import Logging using Serialization using REPL.TerminalMenus import FileWatching import Base: StaleCacheKey import ..depots, ..depots1, ..logdir, ..devdir, ..printpkgstyle import ..Operations, ..GitTools, ..Pkg, ..Registry import ..can_fancyprint, ..pathrepr, ..isurl, ..PREV_ENV_PATH using ..Types, ..TOML using ..Types: VersionTypes using Base.BinaryPlatforms import ..stderr_f, ..stdout_f using ..Artifacts: artifact_paths using ..MiniProgressBars import ..Resolve: ResolverError include("generate.jl") Base.@kwdef struct PackageInfo name::String version::Union{Nothing,VersionNumber} tree_hash::Union{Nothing,String} is_direct_dep::Bool is_pinned::Bool is_tracking_path::Bool is_tracking_repo::Bool is_tracking_registry::Bool git_revision::Union{Nothing,String} git_source::Union{Nothing,String} source::String dependencies::Dict{String,UUID} end function Base.:(==)(a::PackageInfo, b::PackageInfo) return a.name == b.name && a.version == b.version && a.tree_hash == b.tree_hash && a.is_direct_dep == b.is_direct_dep && a.is_pinned == b.is_pinned && a.is_tracking_path == b.is_tracking_path && a.is_tracking_repo == b.is_tracking_repo && a.is_tracking_registry == b.is_tracking_registry && a.git_revision == b.git_revision && a.git_source == b.git_source && a.source == b.source && a.dependencies == b.dependencies end function package_info(env::EnvCache, pkg::PackageSpec)::PackageInfo entry = manifest_info(env.manifest, pkg.uuid) if entry === nothing pkgerror("expected package $(err_rep(pkg)) to exist in the manifest", " (use `resolve` to populate the manifest)") end package_info(env, pkg, entry) end function package_info(env::EnvCache, pkg::PackageSpec, entry::PackageEntry)::PackageInfo git_source = pkg.repo.source === nothing ? nothing : isurl(pkg.repo.source::String) ? pkg.repo.source::String : Operations.project_rel_path(env, pkg.repo.source::String) info = PackageInfo( name = pkg.name, version = pkg.version != VersionSpec() ? pkg.version : nothing, tree_hash = pkg.tree_hash === nothing ? nothing : string(pkg.tree_hash), # TODO or should it just be a SHA? is_direct_dep = pkg.uuid in values(env.project.deps), is_pinned = pkg.pinned, is_tracking_path = pkg.path !== nothing, is_tracking_repo = pkg.repo.rev !== nothing || pkg.repo.source !== nothing, is_tracking_registry = Operations.is_tracking_registry(pkg), git_revision = pkg.repo.rev, git_source = git_source, source = Operations.project_rel_path(env, Operations.source_path(env.manifest_file, pkg)), dependencies = copy(entry.deps), #TODO is copy needed? ) return info end dependencies() = dependencies(EnvCache()) function dependencies(env::EnvCache) pkgs = Operations.load_all_deps(env) return Dict(pkg.uuid::UUID => package_info(env, pkg) for pkg in pkgs) end function dependencies(fn::Function, uuid::UUID) dep = get(dependencies(), uuid, nothing) if dep === nothing pkgerror("dependency with UUID `$uuid` does not exist") end fn(dep) end Base.@kwdef struct ProjectInfo name::Union{Nothing,String} uuid::Union{Nothing,UUID} version::Union{Nothing,VersionNumber} ispackage::Bool dependencies::Dict{String,UUID} path::String end project() = project(EnvCache()) function project(env::EnvCache)::ProjectInfo pkg = env.pkg return ProjectInfo( name = pkg === nothing ? nothing : pkg.name, uuid = pkg === nothing ? nothing : pkg.uuid, version = pkg === nothing ? nothing : pkg.version::VersionNumber, ispackage = pkg !== nothing, dependencies = env.project.deps, path = env.project_file ) end function check_package_name(x::AbstractString, mode::Union{Nothing,String,Symbol}=nothing) if !Base.isidentifier(x) message = sprint() do iostr print(iostr, "`$x` is not a valid package name") if endswith(lowercase(x), ".jl") print(iostr, ". Perhaps you meant `$(chop(x; tail=3))`") end if mode !== nothing && any(occursin.(['\\','/'], x)) # maybe a url or a path print(iostr, "\nThe argument appears to be a URL or path, perhaps you meant ", "`Pkg.$mode(url=\"...\")` or `Pkg.$mode(path=\"...\")`.") end end pkgerror(message) end return end check_package_name(::Nothing, ::Any) = nothing function require_not_empty(pkgs, f::Symbol) isempty(pkgs) && pkgerror("$f requires at least one package") end # Provide some convenience calls for f in (:develop, :add, :rm, :up, :pin, :free, :test, :build, :status, :why, :precompile) @eval begin $f(pkg::Union{AbstractString, PackageSpec}; kwargs...) = $f([pkg]; kwargs...) $f(pkgs::Vector{<:AbstractString}; kwargs...) = $f([PackageSpec(pkg) for pkg in pkgs]; kwargs...) function $f(pkgs::Vector{PackageSpec}; io::IO=$(f === :status ? :stdout_f : :stderr_f)(), kwargs...) $(f != :precompile) && Registry.download_default_registries(io) ctx = Context() # Save initial environment for undo/redo functionality if $(f != :precompile) && !saved_initial_snapshot[] add_snapshot_to_undo(ctx.env) saved_initial_snapshot[] = true end kwargs = merge((;kwargs...), (:io => io,)) pkgs = deepcopy(pkgs) # don't mutate input foreach(handle_package_input!, pkgs) ret = $f(ctx, pkgs; kwargs...) $(f in (:add, :up, :pin, :free, :build)) && Pkg._auto_precompile(ctx) $(f in (:up, :pin, :free, :rm)) && Pkg._auto_gc(ctx) return ret end $f(ctx::Context; kwargs...) = $f(ctx, PackageSpec[]; kwargs...) function $f(; name::Union{Nothing,AbstractString}=nothing, uuid::Union{Nothing,String,UUID}=nothing, version::Union{VersionNumber, String, VersionSpec, Nothing}=nothing, url=nothing, rev=nothing, path=nothing, mode=PKGMODE_PROJECT, subdir=nothing, kwargs...) pkg = PackageSpec(; name, uuid, version, url, rev, path, subdir) if $f === status || $f === rm || $f === up kwargs = merge((;kwargs...), (:mode => mode,)) end # Handle $f() case if all(isnothing, [name,uuid,version,url,rev,path,subdir]) $f(PackageSpec[]; kwargs...) else $f(pkg; kwargs...) end end function $f(pkgs::Vector{<:NamedTuple}; kwargs...) $f([PackageSpec(;pkg...) for pkg in pkgs]; kwargs...) end end end function develop(ctx::Context, pkgs::Vector{PackageSpec}; shared::Bool=true, preserve::PreserveLevel=Operations.default_preserve(), platform::AbstractPlatform=HostPlatform(), kwargs...) require_not_empty(pkgs, :develop) Context!(ctx; kwargs...) for pkg in pkgs check_package_name(pkg.name, "develop") if pkg.name == "julia" # if julia is passed as a package the solver gets tricked pkgerror("`julia` is not a valid package name") end if pkg.name === nothing && pkg.uuid === nothing && pkg.repo.source === nothing pkgerror("name, UUID, URL, or filesystem path specification required when calling `develop`") end if pkg.repo.rev !== nothing pkgerror("rev argument not supported by `develop`; consider using `add` instead") end if pkg.version != VersionSpec() pkgerror("version specification invalid when calling `develop`:", " `$(pkg.version)` specified for package $(err_rep(pkg))") end # not strictly necessary to check these fields early, but it is more efficient if pkg.name !== nothing && (length(findall(x -> x.name == pkg.name, pkgs)) > 1) pkgerror("it is invalid to specify multiple packages with the same name: $(err_rep(pkg))") end if pkg.uuid !== nothing && (length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1) pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))") end end new_git = handle_repos_develop!(ctx, pkgs, shared) for pkg in pkgs if Types.collides_with_project(ctx.env, pkg) pkgerror("package $(err_rep(pkg)) has the same name or UUID as the active project") end if length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1 pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))") end end Operations.develop(ctx, pkgs, new_git; preserve=preserve, platform=platform) return end function add(ctx::Context, pkgs::Vector{PackageSpec}; preserve::PreserveLevel=Operations.default_preserve(), platform::AbstractPlatform=HostPlatform(), kwargs...) require_not_empty(pkgs, :add) Context!(ctx; kwargs...) for pkg in pkgs check_package_name(pkg.name, "add") if pkg.name == "julia" # if julia is passed as a package the solver gets tricked pkgerror("`julia` is not a valid package name") end if pkg.name === nothing && pkg.uuid === nothing && pkg.repo.source === nothing pkgerror("name, UUID, URL, or filesystem path specification required when calling `add`") end if pkg.repo.source !== nothing || pkg.repo.rev !== nothing if pkg.version != VersionSpec() pkgerror("version specification invalid when tracking a repository:", " `$(pkg.version)` specified for package $(err_rep(pkg))") end end # not strictly necessary to check these fields early, but it is more efficient if pkg.name !== nothing && (length(findall(x -> x.name == pkg.name, pkgs)) > 1) pkgerror("it is invalid to specify multiple packages with the same name: $(err_rep(pkg))") end if pkg.uuid !== nothing && (length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1) pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))") end end repo_pkgs = [pkg for pkg in pkgs if (pkg.repo.source !== nothing || pkg.repo.rev !== nothing)] new_git = handle_repos_add!(ctx, repo_pkgs) # repo + unpinned -> name, uuid, repo.rev, repo.source, tree_hash # repo + pinned -> name, uuid, tree_hash Operations.update_registries(ctx; force=false, update_cooldown=Day(1)) project_deps_resolve!(ctx.env, pkgs) registry_resolve!(ctx.registries, pkgs) stdlib_resolve!(pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs, registry=true) for pkg in pkgs if Types.collides_with_project(ctx.env, pkg) pkgerror("package $(err_rep(pkg)) has same name or UUID as the active project") end if length(findall(x -> x.uuid == pkg.uuid, pkgs)) > 1 pkgerror("it is invalid to specify multiple packages with the same UUID: $(err_rep(pkg))") end end Operations.add(ctx, pkgs, new_git; preserve, platform) return end function rm(ctx::Context, pkgs::Vector{PackageSpec}; mode=PKGMODE_PROJECT, all_pkgs::Bool=false, kwargs...) Context!(ctx; kwargs...) if all_pkgs !isempty(pkgs) && pkgerror("cannot specify packages when operating on all packages") append_all_pkgs!(pkgs, ctx, mode) else require_not_empty(pkgs, :rm) end for pkg in pkgs if pkg.name === nothing && pkg.uuid === nothing pkgerror("name or UUID specification required when calling `rm`") end if !(pkg.version == VersionSpec() && pkg.pinned == false && pkg.tree_hash === nothing && pkg.repo.source === nothing && pkg.repo.rev === nothing && pkg.path === nothing) pkgerror("packages may only be specified by name or UUID when calling `rm`") end end mode == PKGMODE_PROJECT && project_deps_resolve!(ctx.env, pkgs) mode == PKGMODE_MANIFEST && manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) Operations.rm(ctx, pkgs; mode) return end function append_all_pkgs!(pkgs, ctx, mode) if mode == PKGMODE_PROJECT || mode == PKGMODE_COMBINED for (name::String, uuid::UUID) in ctx.env.project.deps push!(pkgs, PackageSpec(name=name, uuid=uuid)) end end if mode == PKGMODE_MANIFEST || mode == PKGMODE_COMBINED for (uuid, entry) in ctx.env.manifest push!(pkgs, PackageSpec(name=entry.name, uuid=uuid)) end end return end function up(ctx::Context, pkgs::Vector{PackageSpec}; level::UpgradeLevel=UPLEVEL_MAJOR, mode::PackageMode=PKGMODE_PROJECT, preserve::Union{Nothing,PreserveLevel}= isempty(pkgs) ? nothing : PRESERVE_ALL, update_registry::Bool=true, skip_writing_project::Bool=false, kwargs...) Context!(ctx; kwargs...) if Operations.is_fully_pinned(ctx) printpkgstyle(ctx.io, :Update, "All dependencies are pinned - nothing to update.", color = Base.info_color()) return end if update_registry Registry.download_default_registries(ctx.io) Operations.update_registries(ctx; force=true) end Operations.prune_manifest(ctx.env) if isempty(pkgs) append_all_pkgs!(pkgs, ctx, mode) else mode == PKGMODE_PROJECT && project_deps_resolve!(ctx.env, pkgs) mode == PKGMODE_MANIFEST && manifest_resolve!(ctx.env.manifest, pkgs) project_deps_resolve!(ctx.env, pkgs) manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) end Operations.up(ctx, pkgs, level; skip_writing_project, preserve) return end resolve(; io::IO=stderr_f(), kwargs...) = resolve(Context(;io); kwargs...) function resolve(ctx::Context; skip_writing_project::Bool=false, kwargs...) up(ctx; level=UPLEVEL_FIXED, mode=PKGMODE_MANIFEST, update_registry=false, skip_writing_project, kwargs...) return nothing end function pin(ctx::Context, pkgs::Vector{PackageSpec}; all_pkgs::Bool=false, kwargs...) Context!(ctx; kwargs...) if all_pkgs !isempty(pkgs) && pkgerror("cannot specify packages when operating on all packages") append_all_pkgs!(pkgs, ctx, PKGMODE_MANIFEST) else require_not_empty(pkgs, :pin) end for pkg in pkgs if pkg.name === nothing && pkg.uuid === nothing pkgerror("name or UUID specification required when calling `pin`") end if pkg.repo.source !== nothing pkgerror("repository specification invalid when calling `pin`:", " `$(pkg.repo.source)` specified for package $(err_rep(pkg))") end if pkg.repo.rev !== nothing pkgerror("git revision specification invalid when calling `pin`:", " `$(pkg.repo.rev)` specified for package $(err_rep(pkg))") end version = pkg.version if version isa VersionSpec if version.ranges[1].lower != version.ranges[1].upper # TODO test this pkgerror("pinning a package requires a single version, not a versionrange") end end end project_deps_resolve!(ctx.env, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) Operations.pin(ctx, pkgs) return end function free(ctx::Context, pkgs::Vector{PackageSpec}; all_pkgs::Bool=false, kwargs...) Context!(ctx; kwargs...) if all_pkgs !isempty(pkgs) && pkgerror("cannot specify packages when operating on all packages") append_all_pkgs!(pkgs, ctx, PKGMODE_MANIFEST) else require_not_empty(pkgs, :free) end for pkg in pkgs if pkg.name === nothing && pkg.uuid === nothing pkgerror("name or UUID specification required when calling `free`") end if !(pkg.version == VersionSpec() && pkg.pinned == false && pkg.tree_hash === nothing && pkg.repo.source === nothing && pkg.repo.rev === nothing && pkg.path === nothing) pkgerror("packages may only be specified by name or UUID when calling `free`") end end manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) Operations.free(ctx, pkgs; err_if_free = !all_pkgs) return end function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, test_fn=nothing, julia_args::Union{Cmd, AbstractVector{<:AbstractString}}=``, test_args::Union{Cmd, AbstractVector{<:AbstractString}}=``, force_latest_compatible_version::Bool=false, allow_earlier_backwards_compatible_versions::Bool=true, allow_reresolve::Bool=true, kwargs...) julia_args = Cmd(julia_args) test_args = Cmd(test_args) Context!(ctx; kwargs...) if isempty(pkgs) ctx.env.pkg === nothing && pkgerror("trying to test unnamed project") #TODO Allow this? push!(pkgs, ctx.env.pkg) else project_resolve!(ctx.env, pkgs) project_deps_resolve!(ctx.env, pkgs) manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) end Operations.test( ctx, pkgs; coverage, test_fn, julia_args, test_args, force_latest_compatible_version, allow_earlier_backwards_compatible_versions, allow_reresolve, ) return end is_manifest_current(ctx::Context = Context()) = Operations.is_manifest_current(ctx.env) const UsageDict = Dict{String,DateTime} const UsageByDepotDict = Dict{String,UsageDict} """ gc(ctx::Context=Context(); collect_delay::Period=Day(7), verbose=false, kwargs...) Garbage-collect package and artifact installations by sweeping over all known `Manifest.toml` and `Artifacts.toml` files, noting those that have been deleted, and then finding artifacts and packages that are thereafter not used by any other projects, marking them as "orphaned". This method will only remove orphaned objects (package versions, artifacts, and scratch spaces) that have been continually un-used for a period of `collect_delay`; which defaults to seven days. Garbage collection is only applied to the "user depot", e.g. the first entry in the depot path. If you want to run `gc` on all depots set `force=true` (this might require admin privileges depending on the setup). Use verbose mode (`verbose=true`) for detailed output. """ function gc(ctx::Context=Context(); collect_delay::Period=Day(7), verbose=false, force=false, kwargs...) Context!(ctx; kwargs...) env = ctx.env # Only look at user-depot unless force=true gc_depots = force ? depots() : [depots1()] # First, we load in our `manifest_usage.toml` files which will tell us when our # "index files" (`Manifest.toml`, `Artifacts.toml`) were last used. We will combine # this knowledge across depots, condensing it all down to a single entry per extant # index file, to manage index file growth with would otherwise continue unbounded. We # keep the lists of index files separated by depot so that we can write back condensed # versions that are only ever subsets of what we read out of them in the first place. # Collect last known usage dates of manifest and artifacts toml files, split by depot manifest_usage_by_depot = UsageByDepotDict() artifact_usage_by_depot = UsageByDepotDict() # Collect both last known usage dates, as well as parent projects for each scratch space scratch_usage_by_depot = UsageByDepotDict() scratch_parents_by_depot = Dict{String, Dict{String, Set{String}}}() # Load manifest files from all depots for depot in gc_depots # When a manifest/artifact.toml is installed/used, we log it within the # `manifest_usage.toml` files within `write_env_usage()` and `bind_artifact!()` function reduce_usage!(f::Function, usage_filepath) if !isfile(usage_filepath) return end for (filename, infos) in parse_toml(usage_filepath) f.(Ref(filename), infos) end end # Extract usage data from this depot, (taking only the latest state for each # tracked manifest/artifact.toml), then merge the usage values from each file # into the overall list across depots to create a single, coherent view across # all depots. usage = UsageDict() let usage=usage reduce_usage!(joinpath(logdir(depot), "manifest_usage.toml")) do filename, info # For Manifest usage, store only the last DateTime for each filename found usage[filename] = max(get(usage, filename, DateTime(0)), DateTime(info["time"])::DateTime) end end manifest_usage_by_depot[depot] = usage usage = UsageDict() let usage=usage reduce_usage!(joinpath(logdir(depot), "artifact_usage.toml")) do filename, info # For Artifact usage, store only the last DateTime for each filename found usage[filename] = max(get(usage, filename, DateTime(0)), DateTime(info["time"])::DateTime) end end artifact_usage_by_depot[depot] = usage # track last-used usage = UsageDict() parents = Dict{String, Set{String}}() let usage=usage reduce_usage!(joinpath(logdir(depot), "scratch_usage.toml")) do filename, info # For Artifact usage, store only the last DateTime for each filename found usage[filename] = max(get(usage, filename, DateTime(0)), DateTime(info["time"])::DateTime) if !haskey(parents, filename) parents[filename] = Set{String}() end for parent in info["parent_projects"] push!(parents[filename], parent) end end end scratch_usage_by_depot[depot] = usage scratch_parents_by_depot[depot] = parents end # Next, figure out which files are still existent all_manifest_tomls = unique(f for (_, files) in manifest_usage_by_depot for f in keys(files)) all_artifact_tomls = unique(f for (_, files) in artifact_usage_by_depot for f in keys(files)) all_scratch_dirs = unique(f for (_, dirs) in scratch_usage_by_depot for f in keys(dirs)) all_scratch_parents = Set{String}() for (depot, parents) in scratch_parents_by_depot for parent in values(parents) union!(all_scratch_parents, parent) end end all_manifest_tomls = Set(filter(Pkg.isfile_nothrow, all_manifest_tomls)) all_artifact_tomls = Set(filter(Pkg.isfile_nothrow, all_artifact_tomls)) all_scratch_dirs = Set(filter(Pkg.isdir_nothrow, all_scratch_dirs)) all_scratch_parents = Set(filter(Pkg.isfile_nothrow, all_scratch_parents)) # Immediately write these back as condensed toml files function write_condensed_toml(f::Function, usage_by_depot, fname) for (depot, usage) in usage_by_depot # Run through user-provided filter/condenser usage = f(depot, usage) # Write out the TOML file for this depot usage_path = joinpath(logdir(depot), fname) if !(isempty(usage)::Bool) || isfile(usage_path) let usage=usage open(usage_path, "w") do io TOML.print(io, usage, sorted=true) end end end end end # Write condensed Manifest usage let all_manifest_tomls=all_manifest_tomls write_condensed_toml(manifest_usage_by_depot, "manifest_usage.toml") do depot, usage # Keep only manifest usage markers that are still existent let usage=usage filter!(((k,v),) -> k in all_manifest_tomls, usage) # Expand it back into a dict-of-dicts return Dict(k => [Dict("time" => v)] for (k, v) in usage) end end end # Write condensed Artifact usage let all_artifact_tomls=all_artifact_tomls write_condensed_toml(artifact_usage_by_depot, "artifact_usage.toml") do depot, usage let usage = usage filter!(((k,v),) -> k in all_artifact_tomls, usage) return Dict(k => [Dict("time" => v)] for (k, v) in usage) end end end # Write condensed scratch space usage let all_scratch_parents=all_scratch_parents, all_scratch_dirs=all_scratch_dirs write_condensed_toml(scratch_usage_by_depot, "scratch_usage.toml") do depot, usage # Keep only scratch directories that still exist filter!(((k,v),) -> k in all_scratch_dirs, usage) # Expand it back into a dict-of-dicts expanded_usage = Dict{String,Vector{Dict}}() for (k, v) in usage # Drop scratch spaces whose parents are all non-existent parents = scratch_parents_by_depot[depot][k] filter!(p -> p in all_scratch_parents, parents) if isempty(parents) continue end expanded_usage[k] = [Dict( "time" => v, "parent_projects" => collect(parents), )] end return expanded_usage end end function process_manifest_pkgs(path) # Read the manifest in manifest = try read_manifest(path) catch e @warn "Reading manifest file at $path failed with error" exception = e return nothing end # Collect the locations of every package referred to in this manifest pkg_dir(uuid, entry) = Operations.find_installed(entry.name, uuid, entry.tree_hash) return [pkg_dir(u, e) for (u, e) in manifest if e.tree_hash !== nothing] end # TODO: Merge with function above to not read manifest twice? function process_manifest_repos(path) # Read the manifest in manifest = try read_manifest(path) catch e # Do not warn here, assume that `process_manifest_pkgs` has already warned return nothing end # Collect the locations of every repo referred to in this manifest return [Types.add_repo_cache_path(e.repo.source) for (u, e) in manifest if e.repo.source !== nothing] end function process_artifacts_toml(path, pkgs_to_delete) # Not only do we need to check if this file doesn't exist, we also need to check # to see if it this artifact is contained within a package that is going to go # away. This places an implicit ordering between marking packages and marking # artifacts; the package marking must be done first so that we can ensure that # all artifacts that are solely bound within such packages also get reaped. if any(startswith(path, package_dir) for package_dir in pkgs_to_delete) return nothing end artifact_dict = try parse_toml(path) catch e @warn "Reading artifacts file at $path failed with error" exception = e return nothing end artifact_path_list = String[] for name in keys(artifact_dict) getpaths(meta) = artifact_paths(SHA1(hex2bytes(meta["git-tree-sha1"]))) if isa(artifact_dict[name], Vector) for platform_meta in artifact_dict[name] append!(artifact_path_list, getpaths(platform_meta)) end else append!(artifact_path_list, getpaths(artifact_dict[name])) end end return artifact_path_list end function process_scratchspace(path, pkgs_to_delete) # Find all parents of this path parents = String[] # It is slightly awkward that we need to reach out to our `*_by_depot` # datastructures here; that's because unlike Artifacts and Manifests we're not # parsing a TOML file to find paths within it here, we're actually doing the # inverse, finding files that point to this directory. for (depot, parent_map) in scratch_parents_by_depot if haskey(parent_map, path) append!(parents, parent_map[path]) end end # Look to see if all parents are packages that will be removed, if so, filter # this scratchspace out by returning `nothing` if all(any(startswith(p, dir) for dir in pkgs_to_delete) for p in parents) return nothing end return [path] end # Mark packages/artifacts as active or not by calling the appropriate user function function mark(process_func::Function, index_files, ctx::Context; do_print=true, verbose=false, file_str=nothing) marked_paths = String[] active_index_files = Set{String}() for index_file in index_files # Check to see if it's still alive paths = process_func(index_file) if paths !== nothing # Mark found paths, and record the index_file for printing push!(active_index_files, index_file) append!(marked_paths, paths) end end if do_print @assert file_str !== nothing n = length(active_index_files) printpkgstyle(ctx.io, :Active, "$(file_str): $(n) found") if verbose foreach(active_index_files) do f println(ctx.io, " $(pathrepr(f))") end end end # Return the list of marked paths return Set(marked_paths) end gc_time = now() function merge_orphanages!(new_orphanage, paths, deletion_list, old_orphanage = UsageDict()) for path in paths free_time = something( get(old_orphanage, path, nothing), gc_time, ) # No matter what, store the free time in the new orphanage. This allows # something terrible to go wrong while trying to delete the artifact/ # package and it will still try to be deleted next time. The only time # something is removed from an orphanage is when it didn't exist before # we even started the `gc` run. new_orphanage[path] = free_time # If this path was orphaned long enough ago, add it to the deletion list. # Otherwise, we continue to propagate its orphaning date but don't delete # it. It will get cleaned up at some future `gc`, or it will be used # again during a future `gc` in which case it will not persist within the # orphanage list. if gc_time - free_time >= collect_delay push!(deletion_list, path) end end end # Scan manifests, parse them, read in all UUIDs listed and mark those as active # printpkgstyle(ctx.io, :Active, "manifests:") packages_to_keep = mark(process_manifest_pkgs, all_manifest_tomls, ctx, verbose=verbose, file_str="manifest files") # Do an initial scan of our depots to get a preliminary `packages_to_delete`. packages_to_delete = String[] for depot in gc_depots depot_orphaned_packages = String[] packagedir = abspath(depot, "packages") if isdir(packagedir) for name in readdir(packagedir) !isdir(joinpath(packagedir, name)) && continue for slug in readdir(joinpath(packagedir, name)) pkg_dir = joinpath(packagedir, name, slug) !isdir(pkg_dir) && continue if !(pkg_dir in packages_to_keep) push!(depot_orphaned_packages, pkg_dir) end end end end merge_orphanages!(UsageDict(), depot_orphaned_packages, packages_to_delete) end # Next, do the same for artifacts. Note that we MUST do this after calculating # `packages_to_delete`, as `process_artifacts_toml()` uses it internally to discount # `Artifacts.toml` files that will be deleted by the future culling operation. # printpkgstyle(ctx.io, :Active, "artifacts:") artifacts_to_keep = let packages_to_delete=packages_to_delete mark(x -> process_artifacts_toml(x, packages_to_delete), all_artifact_tomls, ctx; verbose=verbose, file_str="artifact files") end repos_to_keep = mark(process_manifest_repos, all_manifest_tomls, ctx; do_print=false) # printpkgstyle(ctx.io, :Active, "scratchspaces:") spaces_to_keep = let packages_to_delete=packages_to_delete mark(x -> process_scratchspace(x, packages_to_delete), all_scratch_dirs, ctx; verbose=verbose, file_str="scratchspaces") end # Collect all orphaned paths (packages, artifacts and repos that are not reachable). These # are implicitly defined in that we walk all packages/artifacts installed, then if # they were not marked in the above steps, we reap them. packages_to_delete = String[] artifacts_to_delete = String[] repos_to_delete = String[] spaces_to_delete = String[] for depot in gc_depots # We track orphaned objects on a per-depot basis, writing out our `orphaned.toml` # tracking file immediately, only pushing onto the overall `*_to_delete` lists if # the package has been orphaned for at least a period of `collect_delay` depot_orphaned_packages = String[] depot_orphaned_artifacts = String[] depot_orphaned_repos = String[] depot_orphaned_scratchspaces = String[] packagedir = abspath(depot, "packages") if isdir(packagedir) for name in readdir(packagedir) !isdir(joinpath(packagedir, name)) && continue for slug in readdir(joinpath(packagedir, name)) pkg_dir = joinpath(packagedir, name, slug) !isdir(pkg_dir) && continue if !(pkg_dir in packages_to_keep) push!(depot_orphaned_packages, pkg_dir) end end end end reposdir = abspath(depot, "clones") if isdir(reposdir) for repo in readdir(reposdir) repo_dir = joinpath(reposdir, repo) !isdir(repo_dir) && continue if !(repo_dir in repos_to_keep) push!(depot_orphaned_repos, repo_dir) end end end artifactsdir = abspath(depot, "artifacts") if isdir(artifactsdir) for hash in readdir(artifactsdir) artifact_path = joinpath(artifactsdir, hash) !isdir(artifact_path) && continue if !(artifact_path in artifacts_to_keep) push!(depot_orphaned_artifacts, artifact_path) end end end scratchdir = abspath(depot, "scratchspaces") if isdir(scratchdir) for uuid in readdir(scratchdir) uuid_dir = joinpath(scratchdir, uuid) !isdir(uuid_dir) && continue for space in readdir(uuid_dir) space_dir_or_file = joinpath(uuid_dir, space) if isdir(space_dir_or_file) if !(space_dir_or_file in spaces_to_keep) push!(depot_orphaned_scratchspaces, space_dir_or_file) end elseif uuid == Operations.PkgUUID && isfile(space_dir_or_file) # special cleanup for the precompile cache files that Pkg saves if any(prefix->startswith(basename(space_dir_or_file), prefix), ("suspend_cache_", "pending_cache_")) if mtime(space_dir_or_file) < (time() - (24*60*60)) push!(depot_orphaned_scratchspaces, space_dir_or_file) end end end end end end # Read in this depot's `orphaned.toml` file: orphanage_file = joinpath(logdir(depot), "orphaned.toml") new_orphanage = UsageDict() old_orphanage = try TOML.parse(String(read(orphanage_file))) catch UsageDict() end # Update the package and artifact lists of things to delete, and # create the `new_orphanage` list for this depot. merge_orphanages!(new_orphanage, depot_orphaned_packages, packages_to_delete, old_orphanage) merge_orphanages!(new_orphanage, depot_orphaned_artifacts, artifacts_to_delete, old_orphanage) merge_orphanages!(new_orphanage, depot_orphaned_repos, repos_to_delete, old_orphanage) merge_orphanages!(new_orphanage, depot_orphaned_scratchspaces, spaces_to_delete, old_orphanage) # Write out the `new_orphanage` for this depot mkpath(dirname(orphanage_file)) open(orphanage_file, "w") do io TOML.print(io, new_orphanage, sorted=true) end end # Next, we calculate the space savings we're about to gain! pretty_byte_str = (size) -> begin bytes, mb = Base.prettyprint_getunits(size, length(Base._mem_units), Int64(1024)) return @sprintf("%.3f %s", bytes, Base._mem_units[mb]) end function recursive_dir_size(path) size = 0 try for (root, dirs, files) in walkdir(path) for file in files path = joinpath(root, file) try size += lstat(path).size catch ex @error("Failed to calculate size of $path", exception=ex) end end end catch ex @error("Failed to calculate size of $path", exception=ex) end return size end # Delete paths for unreachable package versions and artifacts, and computing size saved function delete_path(path) path_size = if isfile(path) try lstat(path).size catch ex @error("Failed to calculate size of $path", exception=ex) 0 end else recursive_dir_size(path) end try Base.Filesystem.prepare_for_deletion(path) Base.rm(path; recursive=true, force=true) catch e @warn("Failed to delete $path", exception=e) return 0 end if verbose printpkgstyle(ctx.io, :Deleted, pathrepr(path) * " (" * pretty_byte_str(path_size) * ")") end return path_size end package_space_freed = 0 repo_space_freed = 0 artifact_space_freed = 0 scratch_space_freed = 0 for path in packages_to_delete package_space_freed += delete_path(path) end for path in repos_to_delete repo_space_freed += delete_path(path) end for path in artifacts_to_delete artifact_space_freed += delete_path(path) end for path in spaces_to_delete scratch_space_freed += delete_path(path) end # Prune package paths that are now empty for depot in gc_depots packagedir = abspath(depot, "packages") !isdir(packagedir) && continue for name in readdir(packagedir) name_path = joinpath(packagedir, name) !isdir(name_path) && continue !isempty(readdir(name_path)) && continue Base.rm(name_path) end end # Prune scratch space UUID folders that are now empty for depot in gc_depots scratch_dir = abspath(depot, "scratchspaces") !isdir(scratch_dir) && continue for uuid in readdir(scratch_dir) uuid_dir = joinpath(scratch_dir, uuid) !isdir(uuid_dir) && continue if isempty(readdir(uuid_dir)) Base.rm(uuid_dir) end end end ndel_pkg = length(packages_to_delete) ndel_repo = length(repos_to_delete) ndel_art = length(artifacts_to_delete) ndel_space = length(spaces_to_delete) function print_deleted(ndel, freed, name) if ndel <= 0 return end s = ndel == 1 ? "" : "s" bytes_saved_string = pretty_byte_str(freed) printpkgstyle(ctx.io, :Deleted, "$(ndel) $(name)$(s) ($bytes_saved_string)") end print_deleted(ndel_pkg, package_space_freed, "package installation") print_deleted(ndel_repo, repo_space_freed, "repo") print_deleted(ndel_art, artifact_space_freed, "artifact installation") print_deleted(ndel_space, scratch_space_freed, "scratchspace") if ndel_pkg == 0 && ndel_art == 0 && ndel_repo == 0 && ndel_space == 0 printpkgstyle(ctx.io, :Deleted, "no artifacts, repos, packages or scratchspaces") end return end function build(ctx::Context, pkgs::Vector{PackageSpec}; verbose=false, kwargs...) Context!(ctx; kwargs...) if isempty(pkgs) if ctx.env.pkg !== nothing push!(pkgs, ctx.env.pkg) else for (uuid, entry) in ctx.env.manifest push!(pkgs, PackageSpec(entry.name, uuid)) end end end project_resolve!(ctx.env, pkgs) manifest_resolve!(ctx.env.manifest, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) Operations.build(ctx, Set{UUID}(pkg.uuid for pkg in pkgs), verbose) end function get_or_make_pkgspec(pkgspecs::Vector{PackageSpec}, ctx::Context, uuid) i = findfirst(ps -> ps.uuid == uuid, pkgspecs) if !isnothing(i) return pkgspecs[i] elseif !isnothing(ctx.env.pkg) && uuid == ctx.env.pkg.uuid # uuid is of the active Project return ctx.env.pkg elseif haskey(ctx.env.manifest, uuid) pkgent = ctx.env.manifest[uuid] # If we have an unusual situation such as an un-versioned package (like an stdlib that # is being overridden) its `version` may be `nothing`. pkgver = something(pkgent.version, VersionSpec()) return PackageSpec(uuid = uuid, name = pkgent.name, version = pkgver, tree_hash = pkgent.tree_hash) else # this branch should never be hit, so if it is throw a regular error with a stacktrace error("UUID $uuid not found. It does not match the Project uuid nor any dependency") end end function precompile(ctx::Context, pkgs::Vector{PackageSpec}; internal_call::Bool=false, strict::Bool=false, warn_loaded = true, already_instantiated = false, timing::Bool = false, _from_loading::Bool=false, kwargs...) Context!(ctx; kwargs...) already_instantiated || instantiate(ctx; allow_autoprecomp=false, kwargs...) time_start = time_ns() # Windows sometimes hits a ReadOnlyMemoryError, so we halve the default number of tasks. Issue #2323 # TODO: Investigate why this happens in windows and restore the full task limit default_num_tasks = Sys.iswindows() ? div(Sys.CPU_THREADS::Int, 2) + 1 : Sys.CPU_THREADS::Int + 1 default_num_tasks = min(default_num_tasks, 16) # limit for better stability on shared resource systems num_tasks = parse(Int, get(ENV, "JULIA_NUM_PRECOMPILE_TASKS", string(default_num_tasks))) parallel_limiter = Base.Semaphore(num_tasks) io = ctx.io fancyprint = can_fancyprint(io) && !timing hascolor = get(io, :color, false)::Bool color_string(cstr::String, col::Union{Int64, Symbol}) = _color_string(cstr, col, hascolor) recall_precompile_state() # recall suspended and force-queued packages !internal_call && precomp_unsuspend!() # when manually called, unsuspend all packages that were suspended due to precomp errors direct_deps = [ Base.PkgId(uuid, name) for (name, uuid) in ctx.env.project.deps if !Base.in_sysimage(Base.PkgId(uuid, name)) ] stale_cache = Dict{StaleCacheKey, Bool}() exts = Dict{Base.PkgId, String}() # ext -> parent # make a flat map of each dep and its direct deps depsmap = Dict{Base.PkgId, Vector{Base.PkgId}}() pkg_specs = PackageSpec[] pkg_exts_map = Dict{Base.PkgId, Vector{Base.PkgId}}() for dep in ctx.env.manifest pkg = Base.PkgId(first(dep), last(dep).name) Base.in_sysimage(pkg) && continue deps = [Base.PkgId(last(x), first(x)) for x in last(dep).deps] depsmap[pkg] = filter!(!Base.in_sysimage, deps) # add any extensions weakdeps = last(dep).weakdeps pkg_exts = Dict{Base.PkgId, Vector{Base.PkgId}}() prev_ext = nothing for (ext_name, extdep_names) in last(dep).exts ext_deps = Base.PkgId[] push!(ext_deps, pkg) # depends on parent package all_extdeps_available = true extdep_names = extdep_names isa String ? String[extdep_names] : extdep_names for extdep_name in extdep_names extdep_uuid = weakdeps[extdep_name] if extdep_uuid in keys(ctx.env.manifest.deps) || Base.in_sysimage(Base.PkgId(extdep_uuid, extdep_name)) push!(ext_deps, Base.PkgId(extdep_uuid, extdep_name)) else all_extdeps_available = false break end end all_extdeps_available || continue if prev_ext isa Base.PkgId # also make the exts depend on eachother sequentially to avoid race push!(ext_deps, prev_ext) end ext_uuid = Base.uuid5(pkg.uuid, ext_name) ext = Base.PkgId(ext_uuid, ext_name) prev_ext = ext push!(pkg_specs, PackageSpec(uuid = ext_uuid, name = ext_name)) # create this here as the name cannot be looked up easily later via the uuid filter!(!Base.in_sysimage, ext_deps) depsmap[ext] = ext_deps exts[ext] = pkg.name pkg_exts[ext] = ext_deps end if !isempty(pkg_exts) pkg_exts_map[pkg] = collect(keys(pkg_exts)) end end # this loop must be run after the full depsmap has been populated for (pkg, pkg_exts) in pkg_exts_map # find any packages that depend on the extension(s)'s deps and replace those deps in their deps list with the extension(s), # basically injecting the extension into the precompile order in the graph, to avoid race to precompile extensions for (_pkg, deps) in depsmap # for each manifest dep if !in(_pkg, keys(exts)) && pkg in deps # if not an extension and depends on pkg append!(deps, pkg_exts) # add the package extensions to deps filter!(!isequal(pkg), deps) # remove the pkg from deps end end end # if the active environment is a package, add that ctx_env_pkg = ctx.env.pkg if ctx_env_pkg !== nothing && isfile(joinpath(dirname(ctx.env.project_file), "src", "$(ctx_env_pkg.name).jl")) project_pkgid = Base.PkgId(ctx_env_pkg.uuid, ctx_env_pkg.name) depsmap[project_pkgid] = [ Base.PkgId(last(x), first(x)) for x in ctx.env.project.deps if !Base.in_sysimage(Base.PkgId(last(x), first(x))) ] push!(direct_deps, Base.PkgId(ctx_env_pkg.uuid, ctx_env_pkg.name)) else project_pkgid = nothing end # return early if no deps if isempty(depsmap) if isempty(pkgs) return elseif _from_loading # if called from loading precompilation it may be a package from another environment stack so # don't error and allow serial precompilation to try # TODO: actually handle packages from other envs in the stack return else pkgerror("No direct dependencies outside of the sysimage found matching $(repr([p.name for p in pkgs]))") end end # initialize signalling started = Dict{Base.PkgId,Bool}() was_processed = Dict{Base.PkgId,Base.Event}() was_recompiled = Dict{Base.PkgId,Bool}() for pkgid in keys(depsmap) started[pkgid] = false was_processed[pkgid] = Base.Event() was_recompiled[pkgid] = false push!(pkg_specs, get_or_make_pkgspec(pkg_specs, ctx, pkgid.uuid)) end # remove packages that are suspended because they errored before # note that when `Pkg.precompile` is manually called, all suspended packages are unsuspended precomp_prune_suspended!(pkg_specs) # find and guard against circular deps circular_deps = Base.PkgId[] # Three states # !haskey -> never visited # true -> cannot be compiled due to a cycle (or not yet determined) # false -> not depending on a cycle could_be_cycle = Dict{Base.PkgId, Bool}() function scan_pkg!(pkg, dmap) did_visit_dep = true inpath = get!(could_be_cycle, pkg) do did_visit_dep = false return true end if did_visit_dep ? inpath : scan_deps!(pkg, dmap) # Found a cycle. Delete this and all parents return true end return false end function scan_deps!(pkg, dmap) for dep in dmap[pkg] scan_pkg!(dep, dmap) && return true end could_be_cycle[pkg] = false return false end for pkg in keys(depsmap) if scan_pkg!(pkg, depsmap) push!(circular_deps, pkg) notify(was_processed[pkg]) end end if !isempty(circular_deps) @warn """Circular dependency detected. Precompilation will be skipped for:\n $(join(string.(circular_deps), "\n "))""" end # if a list of packages is given, restrict to dependencies of given packages if !isempty(pkgs) pkgs_names = [p.name for p in pkgs] function collect_all_deps(depsmap, dep, alldeps=Set{Base.PkgId}()) for _dep in depsmap[dep] if !(_dep in alldeps) push!(alldeps, _dep) collect_all_deps(depsmap, _dep, alldeps) end end return alldeps end keep = Set{Base.PkgId}() for dep in depsmap dep_pkgid = first(dep) if dep_pkgid.name in pkgs_names push!(keep, dep_pkgid) collect_all_deps(depsmap, dep_pkgid, keep) end end for ext in keys(exts) if issubset(collect_all_deps(depsmap, ext), keep) # if all extension deps are kept push!(keep, ext) end end filter!(d->in(first(d), keep), depsmap) if isempty(depsmap) if _from_loading # if called from loading precompilation it may be a package from another environment stack so # don't error and allow serial precompilation to try # TODO: actually handle packages from other envs in the stack return else pkgerror("No direct dependencies outside of the sysimage found matching $(repr(pkgs_names))") end end target = join(pkgs_names, ", ") else target = "project..." end pkg_queue = Base.PkgId[] failed_deps = Dict{Base.PkgId, String}() skipped_deps = Base.PkgId[] precomperr_deps = Base.PkgId[] # packages that may succeed after a restart (i.e. loaded packages with no cache file) print_lock = ctx.io isa Base.LibuvStream ? ctx.io.lock::ReentrantLock : ReentrantLock() first_started = Base.Event() printloop_should_exit::Bool = !fancyprint # exit print loop immediately if not fancy printing interrupted_or_done = Base.Event() ansi_moveup(n::Int) = string("\e[", n, "A") ansi_movecol1 = "\e[1G" ansi_cleartoend = "\e[0J" ansi_cleartoendofline = "\e[0K" ansi_enablecursor = "\e[?25h" ansi_disablecursor = "\e[?25l" n_done::Int = 0 n_already_precomp::Int = 0 n_loaded::Int = 0 interrupted = false function handle_interrupt(err, in_printloop = false) notify(interrupted_or_done) in_printloop || wait(t_print) # wait to let the print loop cease first if err isa InterruptException lock(print_lock) do println(io, " Interrupted: Exiting precompilation...") end interrupted = true return true else return false end end std_outputs = Dict{Base.PkgId,String}() taskwaiting = Set{Base.PkgId}() pkgspidlocked = Dict{Base.PkgId,String}() pkg_liveprinted = nothing function monitor_std(pkg, pipe; single_requested_pkg=false) try liveprinting = false while !eof(pipe) str = readline(pipe, keep=true) if single_requested_pkg && (liveprinting || !isempty(str)) lock(print_lock) do if !liveprinting printpkgstyle(io, :Info, "Given $(pkg.name) was explicitly requested, output will be shown live $ansi_cleartoendofline", color = Base.info_color()) liveprinting = true pkg_liveprinted = pkg end print(io, ansi_cleartoendofline, str) end end std_outputs[pkg] = string(get(std_outputs, pkg, ""), str) if !in(pkg, taskwaiting) && occursin("waiting for IO to finish", str) !fancyprint && lock(print_lock) do println(io, pkg.name, color_string(" Waiting for background task / IO / timer.", Base.warn_color())) end push!(taskwaiting, pkg) end if !fancyprint && in(pkg, taskwaiting) lock(print_lock) do print(io, str) end end end catch err err isa InterruptException || rethrow() end end ## fancy print loop t_print = @async begin try wait(first_started) (isempty(pkg_queue) || interrupted_or_done.set) && return fancyprint && lock(print_lock) do printpkgstyle(io, :Precompiling, target) print(io, ansi_disablecursor) end t = Timer(0; interval=1/10) anim_chars = ["โ—","โ—“","โ—‘","โ—’"] i = 1 last_length = 0 bar = MiniProgressBar(; indent=2, header = "Progress", color = Base.info_color(), percentage=false, always_reprint=true) n_total = length(depsmap) bar.max = n_total - n_already_precomp final_loop = false n_print_rows = 0 while !printloop_should_exit lock(print_lock) do term_size = Base.displaysize(ctx.io)::Tuple{Int,Int} num_deps_show = term_size[1] - 3 pkg_queue_show = if !interrupted_or_done.set && length(pkg_queue) > num_deps_show last(pkg_queue, num_deps_show) else pkg_queue end str_ = sprint() do iostr if i > 1 print(iostr, ansi_cleartoend) end bar.current = n_done - n_already_precomp bar.max = n_total - n_already_precomp # when sizing to the terminal width subtract a little to give some tolerance to resizing the # window between print cycles termwidth = displaysize(io)[2] - 4 if !final_loop str = sprint(io -> show_progress(io, bar; termwidth, carriagereturn=false); context=io) print(iostr, Base._truncate_at_width_or_chars(true, str, termwidth), "\n") end for dep in pkg_queue_show loaded = warn_loaded && haskey(Base.loaded_modules, dep) _name = haskey(exts, dep) ? string(exts[dep], " โ†’ ", dep.name) : dep.name name = dep in direct_deps ? _name : string(color_string(_name, :light_black)) line = if dep in precomperr_deps string(color_string(" ? ", Base.warn_color()), name) elseif haskey(failed_deps, dep) string(color_string(" โœ— ", Base.error_color()), name) elseif was_recompiled[dep] !loaded && interrupted_or_done.set && continue loaded || @async begin # keep successful deps visible for short period sleep(1); filter!(!isequal(dep), pkg_queue) end string(color_string(" โœ“ ", loaded ? Base.warn_color() : :green), name) elseif started[dep] # Offset each spinner animation using the first character in the package name as the seed. # If not offset, on larger terminal fonts it looks odd that they all sync-up anim_char = anim_chars[(i + Int(dep.name[1])) % length(anim_chars) + 1] anim_char_colored = dep in direct_deps ? anim_char : color_string(anim_char, :light_black) waiting = if haskey(pkgspidlocked, dep) who_has_lock = pkgspidlocked[dep] color_string(" Being precompiled by $(who_has_lock)", Base.info_color()) elseif dep in taskwaiting color_string(" Waiting for background task / IO / timer. Interrupt to inspect", Base.warn_color()) else "" end string(" ", anim_char_colored, " ", name, waiting) else string(" ", name) end println(iostr, Base._truncate_at_width_or_chars(true, line, termwidth)) end end last_length = length(pkg_queue_show) n_print_rows = count("\n", str_) print(io, str_) printloop_should_exit = interrupted_or_done.set && final_loop final_loop = interrupted_or_done.set # ensures one more loop to tidy last task after finish i += 1 printloop_should_exit || print(io, ansi_moveup(n_print_rows), ansi_movecol1) end wait(t) end catch err handle_interrupt(err, true) || rethrow() finally fancyprint && print(io, ansi_enablecursor) end end tasks = Task[] if !_from_loading Base.LOADING_CACHE[] = Base.LoadingCache() end ## precompilation loop for (pkg, deps) in depsmap cachepaths = Base.find_all_in_cache_path(pkg) sourcepath = Base.locate_package(pkg) if sourcepath === nothing failed_deps[pkg] = "Error: Missing source file for $(pkg)" notify(was_processed[pkg]) continue end # Heuristic for when precompilation is disabled if occursin(r"\b__precompile__\(\s*false\s*\)", read(sourcepath, String)) notify(was_processed[pkg]) continue end single_requested_pkg = if length(pkgs) == 1 only(pkgs).name == pkg.name elseif project_pkgid isa Base.PkgId pkg == project_pkgid # if a package project is being precompiled, consider the package requested else false end task = @async begin try loaded = haskey(Base.loaded_modules, pkg) for dep in deps # wait for deps to finish wait(was_processed[dep]) end pkgspec = get_or_make_pkgspec(pkg_specs, ctx, pkg.uuid) suspended = precomp_suspended(pkgspec) queued = precomp_queued(pkgspec) circular = pkg in circular_deps is_stale = true if !circular && (queued || (!suspended && (is_stale = !Base.isprecompiled(pkg; ignore_loaded=true, stale_cache, cachepaths, sourcepath)))) Base.acquire(parallel_limiter) is_direct_dep = pkg in direct_deps # std monitoring std_pipe = Base.link_pipe!(Pipe(); reader_supports_async=true, writer_supports_async=true) t_monitor = @async monitor_std(pkg, std_pipe; single_requested_pkg) _name = haskey(exts, pkg) ? string(exts[pkg], " โ†’ ", pkg.name) : pkg.name name = is_direct_dep ? _name : string(color_string(_name, :light_black)) !fancyprint && lock(print_lock) do isempty(pkg_queue) && printpkgstyle(io, :Precompiling, target) end push!(pkg_queue, pkg) started[pkg] = true fancyprint && notify(first_started) if interrupted_or_done.set notify(was_processed[pkg]) Base.release(parallel_limiter) return end try # allows processes to wait if another process is precompiling a given package to # a functionally identical package cache (except for preferences, which may differ) t = @elapsed ret = maybe_cachefile_lock(io, print_lock, fancyprint, pkg, pkgspidlocked, hascolor) do Logging.with_logger(Logging.NullLogger()) do # The false here means we ignore loaded modules, so precompile for a fresh session Base.compilecache(pkg, sourcepath, std_pipe, std_pipe, false) end end t_str = timing ? string(lpad(round(t * 1e3, digits = 1), 9), " ms") : "" if ret isa Base.PrecompilableError push!(precomperr_deps, pkg) precomp_queue!(get_or_make_pkgspec(pkg_specs, ctx, pkg.uuid)) !fancyprint && lock(print_lock) do println(io, t_str, color_string(" ? ", Base.warn_color()), name) end else queued && precomp_dequeue!(get_or_make_pkgspec(pkg_specs, ctx, pkg.uuid)) !fancyprint && lock(print_lock) do println(io, t_str, color_string(" โœ“ ", loaded ? Base.warn_color() : :green), name) end was_recompiled[pkg] = true end loaded && (n_loaded += 1) catch err close(std_pipe.in) # close pipe to end the std output monitor wait(t_monitor) if err isa ErrorException || (err isa ArgumentError && startswith(err.msg, "Invalid header in cache file")) failed_deps[pkg] = (strict || is_direct_dep) ? string(sprint(showerror, err), "\n", strip(get(std_outputs, pkg, ""))) : "" delete!(std_outputs, pkg) # so it's not shown as warnings, given error report !fancyprint && lock(print_lock) do println(io, timing ? " "^9 : "", color_string(" โœ— ", Base.error_color()), name) end queued && precomp_dequeue!(get_or_make_pkgspec(pkg_specs, ctx, pkg.uuid)) precomp_suspend!(get_or_make_pkgspec(pkg_specs, ctx, pkg.uuid)) else rethrow() end finally isopen(std_pipe.in) && close(std_pipe.in) # close pipe to end the std output monitor wait(t_monitor) Base.release(parallel_limiter) end else is_stale || (n_already_precomp += 1) suspended && push!(skipped_deps, pkg) end n_done += 1 notify(was_processed[pkg]) catch err_outer handle_interrupt(err_outer) || rethrow() notify(was_processed[pkg]) finally filter!(!istaskdone, tasks) length(tasks) == 1 && notify(interrupted_or_done) end end push!(tasks, task) end isempty(tasks) && notify(interrupted_or_done) try wait(interrupted_or_done) catch err handle_interrupt(err) || rethrow() finally Base.LOADING_CACHE[] = nothing end notify(first_started) # in cases of no-op or !fancyprint save_precompile_state() # save lists to scratch space fancyprint && wait(t_print) quick_exit = !all(istaskdone, tasks) || interrupted # if some not finished internal error is likely seconds_elapsed = round(Int, (time_ns() - time_start) / 1e9) ndeps = count(values(was_recompiled)) if ndeps > 0 || !isempty(failed_deps) || (quick_exit && !isempty(std_outputs)) str = sprint() do iostr if !quick_exit plural = ndeps == 1 ? "y" : "ies" print(iostr, " $(ndeps) dependenc$(plural) successfully precompiled in $(seconds_elapsed) seconds") if n_already_precomp > 0 || !isempty(circular_deps) || !isempty(skipped_deps) n_already_precomp > 0 && (print(iostr, ". $n_already_precomp already precompiled")) !isempty(circular_deps) && (print(iostr, ". $(length(circular_deps)) skipped due to circular dependency")) !isempty(skipped_deps) && (print(iostr, ". $(length(skipped_deps)) skipped during auto due to previous errors")) print(iostr, ".") end if n_loaded > 0 plural1 = n_loaded == 1 ? "y" : "ies" plural2 = n_loaded == 1 ? "a different version is" : "different versions are" plural3 = n_loaded == 1 ? "" : "s" print(iostr, "\n ", color_string(string(n_loaded), Base.warn_color()), " dependenc$(plural1) precompiled but $(plural2) currently loaded. Restart julia to access the new version$(plural3)" ) end if !isempty(precomperr_deps) pluralpc = length(precomperr_deps) == 1 ? "y" : "ies" print(iostr, "\n ", color_string(string(length(precomperr_deps)), Base.warn_color()), " dependenc$(pluralpc) failed but may be precompilable after restarting julia" ) end end # show any stderr output, even if Pkg.precompile has been interrupted (quick_exit=true), given user may be # interrupting a hanging precompile job with stderr output. julia#48371 filter!(kv -> !isempty(strip(last(kv))), std_outputs) # remove empty output if !isempty(std_outputs) plural1 = length(std_outputs) == 1 ? "y" : "ies" plural2 = length(std_outputs) == 1 ? "" : "s" print(iostr, "\n ", color_string("$(length(std_outputs))", Base.warn_color()), " dependenc$(plural1) had output during precompilation:") for (pkgid, err) in std_outputs err = if pkgid == pkg_liveprinted "[Output was shown above]" else join(split(strip(err), "\n"), color_string("\nโ”‚ ", Base.warn_color())) end name = haskey(exts, pkgid) ? string(exts[pkgid], " โ†’ ", pkgid.name) : pkgid.name print(iostr, color_string("\nโ”Œ ", Base.warn_color()), name, color_string("\nโ”‚ ", Base.warn_color()), err, color_string("\nโ”” ", Base.warn_color())) end end end let str=str lock(print_lock) do println(io, str) end end quick_exit && return err_str = "" n_direct_errs = 0 for (dep, err) in failed_deps if strict || (dep in direct_deps) err_str = string(err_str, "\n$dep\n\n$err", (n_direct_errs > 0 ? "\n" : "")) n_direct_errs += 1 end end if err_str != "" pluralde = n_direct_errs == 1 ? "y" : "ies" direct = strict ? "" : "direct " err_msg = "The following $n_direct_errs $(direct)dependenc$(pluralde) failed to precompile:\n$(err_str[1:end-1])" if internal_call # aka. auto-precompilation if isinteractive() && !get(ENV, "CI", false) plural1 = length(failed_deps) == 1 ? "y" : "ies" println(io, " ", color_string("$(length(failed_deps))", Base.error_color()), " dependenc$(plural1) errored.") println(io, " For a report of the errors see `julia> err`. To retry use `pkg> precompile`") setglobal!(Base.MainInclude, :err, PkgPrecompileError(err_msg)) else # auto-precompilation shouldn't throw but if the user can't easily access the # error messages, just show them print(io, "\n", err_msg) end else println(io) pkgerror(err_msg) end end end nothing end function _color_string(cstr::String, col::Union{Int64, Symbol}, hascolor) if hascolor enable_ansi = get(Base.text_colors, col, Base.text_colors[:default]) disable_ansi = get(Base.disable_text_style, col, Base.text_colors[:default]) return string(enable_ansi, cstr, disable_ansi) else return cstr end end function maybe_cachefile_lock(f, io::IO, print_lock::ReentrantLock, fancyprint::Bool, pkg::Base.PkgId, pkgspidlocked::Dict{Base.PkgId,String}, hascolor) stale_age = Base.compilecache_pidlock_stale_age pidfile = Base.compilecache_pidfile_path(pkg) cachefile = FileWatching.trymkpidlock(f, pidfile; stale_age) if cachefile === false pid, hostname, age = FileWatching.Pidfile.parse_pidfile(pidfile) pkgspidlocked[pkg] = if isempty(hostname) || hostname == gethostname() if pid == getpid() "an async task in this process (pidfile: $pidfile)" else "another process (pid: $pid, pidfile: $pidfile)" end else "another machine (hostname: $hostname, pid: $pid, pidfile: $pidfile)" end !fancyprint && lock(print_lock) do println(io, " ", pkg.name, _color_string(" Being precompiled by $(pkgspidlocked[pkg])", Base.info_color(), hascolor)) end # wait until the lock is available FileWatching.mkpidlock(pidfile; stale_age) do # double-check in case the other process crashed or the lock expired if Base.isprecompiled(pkg; ignore_loaded=true) # don't use caches for this as the env state will have changed return nothing # returning nothing indicates a process waited for another else delete!(pkgspidlocked, pkg) return f() # precompile end end end return cachefile end const pkgs_precompile_suspended = PackageSpec[] # packages that shouldn't be retried during autoprecomp const pkgs_precompile_pending = PackageSpec[] # packages that need to be retried after restart function save_precompile_state() path = Operations.pkg_scratchpath() for (prefix, store) in (("suspend_cache_", pkgs_precompile_suspended), ("pending_cache_", pkgs_precompile_pending)) fpath = joinpath(path, string(prefix, hash(string(Base.active_project(), Base.VERSION)))) if isempty(store) Base.rm(fpath, force=true) else mkpath(path); Base.rm(fpath, force=true) open(fpath, "w") do io serialize(io, store) end end end return nothing end function recall_precompile_state() for (prefix, store) in (("suspend_cache_", pkgs_precompile_suspended), ("pending_cache_", pkgs_precompile_pending)) fpath = joinpath(Operations.pkg_scratchpath(), string(prefix, hash(string(Base.active_project(), Base.VERSION)))) if isfile(fpath) open(fpath) do io try pkgspecs = deserialize(io)::Vector{PackageSpec} append!(empty!(store), pkgspecs) catch empty!(store) end end Base.rm(fpath, force=true) else empty!(store) end end return nothing end function precomp_suspend!(pkg::PackageSpec) precomp_suspended(pkg) || push!(pkgs_precompile_suspended, pkg) return end precomp_unsuspend!() = empty!(pkgs_precompile_suspended) precomp_suspended(pkg::PackageSpec) = pkg in pkgs_precompile_suspended function precomp_prune_suspended!(pkgs::Vector{PackageSpec}) filter!(in(pkgs), pkgs_precompile_suspended) unique!(pkgs_precompile_suspended) return end function precomp_queue!(pkg::PackageSpec) precomp_suspended(pkg) || push!(pkgs_precompile_pending, pkg) return end precomp_dequeue!(pkg::PackageSpec) = filter!(!isequal(pkg), pkgs_precompile_pending) precomp_queued(pkg::PackageSpec) = pkg in pkgs_precompile_pending function tree_hash(repo::LibGit2.GitRepo, tree_hash::String) try return LibGit2.GitObject(repo, tree_hash) catch err err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow() end return nothing end instantiate(; kwargs...) = instantiate(Context(); kwargs...) function instantiate(ctx::Context; manifest::Union{Bool, Nothing}=nothing, update_registry::Bool=true, verbose::Bool=false, platform::AbstractPlatform=HostPlatform(), allow_build::Bool=true, allow_autoprecomp::Bool=true, kwargs...) Context!(ctx; kwargs...) if Registry.download_default_registries(ctx.io) copy!(ctx.registries, Registry.reachable_registries()) end if !isfile(ctx.env.project_file) && isfile(ctx.env.manifest_file) _manifest = Pkg.Types.read_manifest(ctx.env.manifest_file) Types.check_warn_manifest_julia_version_compat(_manifest, ctx.env.manifest_file) deps = Dict{String,String}() for (uuid, pkg) in _manifest if pkg.name in keys(deps) # TODO, query what package to put in Project when in interactive mode? pkgerror("cannot instantiate a manifest without project file when the manifest has multiple packages with the same name ($(pkg.name))") end deps[pkg.name] = string(uuid) end Types.write_project(Dict("deps" => deps), ctx.env.project_file) return instantiate(Context(); manifest=manifest, update_registry=update_registry, allow_autoprecomp=allow_autoprecomp, verbose=verbose, platform=platform, kwargs...) end if (!isfile(ctx.env.manifest_file) && manifest === nothing) || manifest == false # given no manifest exists, only allow invoking a registry update if there are project deps allow_registry_update = isfile(ctx.env.project_file) && !isempty(ctx.env.project.deps) up(ctx; update_registry = update_registry && allow_registry_update) allow_autoprecomp && Pkg._auto_precompile(ctx, already_instantiated = true) return end if !isfile(ctx.env.manifest_file) && manifest == true pkgerror("expected manifest file at `$(ctx.env.manifest_file)` but it does not exist") end Types.check_warn_manifest_julia_version_compat(ctx.env.manifest, ctx.env.manifest_file) if Operations.is_manifest_current(ctx.env) === false @warn """The project dependencies or compat requirements have changed since the manifest was last resolved. It is recommended to `Pkg.resolve()` or consider `Pkg.update()` if necessary.""" end Operations.prune_manifest(ctx.env) for (name, uuid) in ctx.env.project.deps get(ctx.env.manifest, uuid, nothing) === nothing || continue pkgerror("`$name` is a direct dependency, but does not appear in the manifest.", " If you intend `$name` to be a direct dependency, run `Pkg.resolve()` to populate the manifest.", " Otherwise, remove `$name` with `Pkg.rm(\"$name\")`.", " Finally, run `Pkg.instantiate()` again.") end # check if all source code and artifacts are downloaded to exit early if Operations.is_instantiated(ctx.env; platform) allow_autoprecomp && Pkg._auto_precompile(ctx, already_instantiated = true) return end pkgs = Operations.load_all_deps(ctx.env) try # First try without updating the registry Operations.check_registered(ctx.registries, pkgs) catch e if !(e isa PkgError) || update_registry == false rethrow(e) end Operations.update_registries(ctx; force=false) Operations.check_registered(ctx.registries, pkgs) end new_git = UUID[] # Handling packages tracking repos for pkg in pkgs repo_source = pkg.repo.source repo_source !== nothing || continue sourcepath = Operations.source_path(ctx.env.manifest_file, pkg, ctx.julia_version) isdir(sourcepath) && continue ## Download repo at tree hash # determine canonical form of repo source if !isurl(repo_source) repo_source = normpath(joinpath(dirname(ctx.env.project_file), repo_source)) end if !isurl(repo_source) && !isdir(repo_source) pkgerror("Did not find path `$(repo_source)` for $(err_rep(pkg))") end repo_path = Types.add_repo_cache_path(repo_source) let repo_source=repo_source LibGit2.with(GitTools.ensure_clone(ctx.io, repo_path, repo_source; isbare=true)) do repo # We only update the clone if the tree hash can't be found tree_hash_object = tree_hash(repo, string(pkg.tree_hash)) if tree_hash_object === nothing GitTools.fetch(ctx.io, repo, repo_source; refspecs=Types.refspecs) tree_hash_object = tree_hash(repo, string(pkg.tree_hash)) end if tree_hash_object === nothing pkgerror("Did not find tree_hash $(pkg.tree_hash) for $(err_rep(pkg))") end mkpath(sourcepath) GitTools.checkout_tree_to_path(repo, tree_hash_object, sourcepath) push!(new_git, pkg.uuid) end end end # Install all packages new_apply = Operations.download_source(ctx) # Install all artifacts Operations.download_artifacts(ctx.env; platform, verbose, io=ctx.io) # Run build scripts allow_build && Operations.build_versions(ctx, union(new_apply, new_git); verbose=verbose) allow_autoprecomp && Pkg._auto_precompile(ctx, already_instantiated = true) end @deprecate status(mode::PackageMode) status(mode=mode) function status(ctx::Context, pkgs::Vector{PackageSpec}; diff::Bool=false, mode=PKGMODE_PROJECT, outdated::Bool=false, compat::Bool=false, extensions::Bool=false, io::IO=stdout_f()) if compat diff && pkgerror("Compat status has no `diff` mode") outdated && pkgerror("Compat status has no `outdated` mode") extensions && pkgerror("Compat status has no `extensions` mode") Operations.print_compat(ctx, pkgs; io) else Operations.status(ctx.env, ctx.registries, pkgs; mode, git_diff=diff, io, outdated, extensions) end return nothing end function activate(;temp=false, shared=false, prev=false, io::IO=stderr_f()) shared && pkgerror("Must give a name for a shared environment") temp && return activate(mktempdir(); io=io) if prev if isempty(PREV_ENV_PATH[]) pkgerror("No previously active environment found") else return activate(PREV_ENV_PATH[]; io=io) end end if !isnothing(Base.active_project()) PREV_ENV_PATH[] = Base.active_project() end Base.ACTIVE_PROJECT[] = nothing p = Base.active_project() p === nothing || printpkgstyle(io, :Activating, "project at $(pathrepr(dirname(p)))") add_snapshot_to_undo() return nothing end function _activate_dep(dep_name::AbstractString) Base.active_project() === nothing && return ctx = nothing try ctx = Context() catch err err isa PkgError || rethrow() return end uuid = get(ctx.env.project.deps, dep_name, nothing) if uuid !== nothing entry = manifest_info(ctx.env.manifest, uuid) if entry.path !== nothing return joinpath(dirname(ctx.env.manifest_file), entry.path::String) end end end function activate(path::AbstractString; shared::Bool=false, temp::Bool=false, io::IO=stderr_f()) temp && pkgerror("Can not give `path` argument when creating a temporary environment") if !shared # `pkg> activate path`/`Pkg.activate(path)` does the following # 1. if path exists, activate that # 2. if path exists in deps, and the dep is deved, activate that path (`devpath` above) # 3. activate the non-existing directory (e.g. as in `pkg> activate .` for initing a new env) if Pkg.isdir_nothrow(path) fullpath = abspath(path) else fullpath = _activate_dep(path) if fullpath === nothing fullpath = abspath(path) end end else # initialize `fullpath` in case of empty `Pkg.depots()` fullpath = "" # loop over all depots to check if the shared environment already exists for depot in Pkg.depots() fullpath = joinpath(Pkg.envdir(depot), path) isdir(fullpath) && break end # this disallows names such as "Foo/bar", ".", "..", etc if basename(abspath(fullpath)) != path pkgerror("not a valid name for a shared environment: $(path)") end # unless the shared environment already exists, place it in the first depots if !isdir(fullpath) fullpath = joinpath(Pkg.envdir(Pkg.depots1()), path) end end if !isnothing(Base.active_project()) PREV_ENV_PATH[] = Base.active_project() end Base.ACTIVE_PROJECT[] = Base.load_path_expand(fullpath) p = Base.active_project() if p !== nothing n = ispath(p) ? "" : "new " printpkgstyle(io, :Activating, "$(n)project at $(pathrepr(dirname(p)))") end add_snapshot_to_undo() return nothing end function activate(f::Function, new_project::AbstractString) old = Base.ACTIVE_PROJECT[] Base.ACTIVE_PROJECT[] = new_project try f() finally Base.ACTIVE_PROJECT[] = old end end function compat(ctx::Context; io = nothing) io = something(io, ctx.io) can_fancyprint(io) || pkgerror("Pkg.compat cannot be run interactively in this terminal") printpkgstyle(io, :Compat, pathrepr(ctx.env.project_file)) longest_dep_len = max(5, length.(collect(keys(ctx.env.project.deps)))...) opt_strs = String[] opt_pkgs = String[] compat_str = Operations.get_compat_str(ctx.env.project, "julia") push!(opt_strs, Operations.compat_line(io, "julia", nothing, compat_str, longest_dep_len, indent = "")) push!(opt_pkgs, "julia") for (dep, uuid) in sort(collect(ctx.env.project.deps); by = x->x.first) compat_str = Operations.get_compat_str(ctx.env.project, dep) push!(opt_strs, Operations.compat_line(io, dep, uuid, compat_str, longest_dep_len, indent = "")) push!(opt_pkgs, dep) end menu = TerminalMenus.RadioMenu(opt_strs, pagesize=length(opt_strs)) choice = try TerminalMenus.request(" Select an entry to edit:", menu) catch err if err isa InterruptException # if ^C is entered println(io) return false end rethrow() end choice == -1 && return false dep = opt_pkgs[choice] current_compat_str = something(Operations.get_compat_str(ctx.env.project, dep), "") resp = try prompt = " Edit compat entry for $(dep):" print(io, prompt) buffer = current_compat_str cursor = length(buffer) start_pos = length(prompt) + 2 move_start = "\e[$(start_pos)G" clear_to_end = "\e[0J" ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle, true) while true print(io, move_start, clear_to_end, buffer, "\e[$(start_pos + cursor)G") inp = TerminalMenus._readkey(stdin) if inp == '\r' # Carriage return println(io) break elseif inp == '\x03' # cltr-C println(io) return elseif inp == TerminalMenus.ARROW_RIGHT cursor = min(length(buffer), cursor + 1) elseif inp == TerminalMenus.ARROW_LEFT cursor = max(0, cursor - 1) elseif inp == TerminalMenus.HOME_KEY cursor = (0) elseif inp == TerminalMenus.END_KEY cursor = length(buffer) elseif inp == TerminalMenus.DEL_KEY if cursor == 0 buffer = buffer[2:end] elseif cursor < length(buffer) buffer = buffer[1:cursor] * buffer[(cursor + 2):end] end elseif inp isa TerminalMenus.Key # ignore all other escaped (multi-byte) keys elseif inp == '\x7f' # backspace if cursor == 1 buffer = buffer[2:end] elseif cursor == length(buffer) buffer = buffer[1:end - 1] elseif cursor > 0 buffer = buffer[1:(cursor-1)] * buffer[(cursor + 1):end] else continue end cursor -= 1 else if cursor == 0 buffer = inp * buffer elseif cursor == length(buffer) buffer = buffer * inp else buffer = buffer[1:cursor] * inp * buffer[(cursor + 1):end] end cursor += 1 end end buffer finally ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle, false) end new_entry = strip(resp) compat(ctx, dep, string(new_entry)) return end function compat(ctx::Context, pkg::String, compat_str::Union{Nothing,String}; io = nothing, kwargs...) io = something(io, ctx.io) pkg = pkg == "Julia" ? "julia" : pkg isnothing(compat_str) || (compat_str = string(strip(compat_str, '"'))) if haskey(ctx.env.project.deps, pkg) || pkg == "julia" success = Operations.set_compat(ctx.env.project, pkg, isnothing(compat_str) ? nothing : isempty(compat_str) ? nothing : compat_str) success === false && pkgerror("invalid compat version specifier \"$(compat_str)\"") write_env(ctx.env) if isnothing(compat_str) || isempty(compat_str) printpkgstyle(io, :Compat, "entry removed for $(pkg)") else printpkgstyle(io, :Compat, "entry set:\n $(pkg) = $(repr(compat_str))") end printpkgstyle(io, :Resolve, "checking for compliance with the new compat rules...") try resolve(ctx) catch e if e isa ResolverError printpkgstyle(io, :Error, string(e.msg), color = Base.warn_color()) printpkgstyle(io, :Suggestion, "Call `update` to attempt to meet the compatibility requirements.", color = Base.info_color()) else rethrow() end end return else pkgerror("No package named $pkg in current Project") end end compat(pkg::String; kwargs...) = compat(pkg, nothing; kwargs...) compat(pkg::String, compat_str::Union{Nothing,String}; kwargs...) = compat(Context(), pkg, compat_str; kwargs...) compat(;kwargs...) = compat(Context(); kwargs...) ####### # why # ####### function why(ctx::Context, pkgs::Vector{PackageSpec}; io::IO, kwargs...) require_not_empty(pkgs, :why) manifest_resolve!(ctx.env.manifest, pkgs) project_deps_resolve!(ctx.env, pkgs) ensure_resolved(ctx, ctx.env.manifest, pkgs) # Store all packages that has a dependency on us (all dependees) incoming = Dict{UUID, Set{UUID}}() for (uuid, dep_pkgs) in ctx.env.manifest for (dep, dep_uuid) in dep_pkgs.deps haskey(incoming, dep_uuid) || (incoming[dep_uuid] = Set{UUID}()) push!(incoming[dep_uuid], uuid) end end function find_paths!(final_paths, current, path = UUID[]) push!(path, current) current in values(ctx.env.project.deps) && push!(final_paths, path) # record once we've traversed to a project dep haskey(incoming, current) || return # but only return if we've reached a leaf that nothing depends on for p in incoming[current] if p in path # detected dependency cycle and none of the dependencies in the cycle # are in the project could happen when manually modifying # the project and running this function function before a # resolve continue end find_paths!(final_paths, p, copy(path)) end end first = true for pkg in pkgs !first && println(io) first = false final_paths = Set{Vector{UUID}}() find_paths!(final_paths, pkg.uuid) foreach(reverse!, final_paths) final_paths_names = map(x -> [ctx.env.manifest[uuid].name for uuid in x], collect(final_paths)) sort!(final_paths_names, by = x -> (x, length(x))) delimiter = sprint((io, args) -> printstyled(io, args...; color=:light_green), "โ†’", context=io) for path in final_paths_names println(io, " ", join(path, " $delimiter ")) end end end ######## # Undo # ######## struct UndoSnapshot date::DateTime project::Types.Project manifest::Types.Manifest end mutable struct UndoState idx::Int entries::Vector{UndoSnapshot} end UndoState() = UndoState(0, UndoSnapshot[]) const undo_entries = Dict{String, UndoState}() const max_undo_limit = 50 const saved_initial_snapshot = Ref(false) function add_snapshot_to_undo(env=nothing) # only attempt to take a snapshot if there is # an active project to be found if env === nothing if Base.active_project() === nothing return else env = EnvCache() end end state = get!(undo_entries, env.project_file) do UndoState() end # Is the current state the same as the previous one, do nothing if !isempty(state.entries) && env.project == env.original_project && env.manifest.deps == env.original_manifest.deps return end snapshot = UndoSnapshot(now(), env.project, env.manifest) deleteat!(state.entries, 1:(state.idx-1)) pushfirst!(state.entries, snapshot) state.idx = 1 resize!(state.entries, min(length(state.entries), max_undo_limit)) end undo(ctx = Context()) = redo_undo(ctx, :undo, 1) redo(ctx = Context()) = redo_undo(ctx, :redo, -1) function redo_undo(ctx, mode::Symbol, direction::Int) @assert direction == 1 || direction == -1 state = get(undo_entries, ctx.env.project_file, nothing) state === nothing && pkgerror("no undo state for current project") state.idx == (mode === :redo ? 1 : length(state.entries)) && pkgerror("$mode: no more states left") state.idx += direction snapshot = state.entries[state.idx] ctx.env.manifest, ctx.env.project = snapshot.manifest, snapshot.project write_env(ctx.env; update_undo=false) Operations.show_update(ctx.env, ctx.registries; io=ctx.io) end function setprotocol!(; domain::AbstractString="github.com", protocol::Union{Nothing, AbstractString}=nothing ) GitTools.setprotocol!(domain=domain, protocol=protocol) return nothing end @deprecate setprotocol!(proto::Union{Nothing, AbstractString}) setprotocol!(protocol = proto) false function handle_package_input!(pkg::PackageSpec) if pkg.path !== nothing && pkg.url !== nothing pkgerror("`path` and `url` are conflicting specifications") end pkg.repo = Types.GitRepo(rev = pkg.rev, source = pkg.url !== nothing ? pkg.url : pkg.path, subdir = pkg.subdir) pkg.path = nothing pkg.tree_hash = nothing if pkg.version === nothing pkg.version = VersionSpec() end if !(pkg.version isa VersionNumber) pkg.version = VersionSpec(pkg.version) end pkg.uuid = pkg.uuid isa String ? UUID(pkg.uuid) : pkg.uuid end function upgrade_manifest(man_path::String) dir = mktempdir() cp(man_path, joinpath(dir, "Manifest.toml")) Pkg.activate(dir) do Pkg.upgrade_manifest() end mv(joinpath(dir, "Manifest.toml"), man_path, force = true) end function upgrade_manifest(ctx::Context = Context()) before_format = ctx.env.manifest.manifest_format if before_format == v"2.0" pkgerror("Format of manifest file at `$(ctx.env.manifest_file)` already up to date: manifest_format == $(before_format)") elseif before_format != v"1.0" pkgerror("Format of manifest file at `$(ctx.env.manifest_file)` version is unrecognized: manifest_format == $(before_format)") end ctx.env.manifest.manifest_format = v"2.0" Types.write_manifest(ctx.env) printpkgstyle(ctx.io, :Updated, "Format of manifest file at `$(ctx.env.manifest_file)` updated from v$(before_format.major).$(before_format.minor) to v2.0") return nothing end end # module o���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/generate.jl������# This file is a part of Julia. License is MIT: https://julialang.org/license function generate(path::String; io::IO=stderr_f()) base = basename(path) pkg = endswith(lowercase(base), ".jl") ? chop(base, tail=3) : base Base.isidentifier(pkg) || pkgerror("$(repr(pkg)) is not a valid package name") isdir(path) && pkgerror("$(abspath(path)) already exists") printpkgstyle(io, :Generating, " project $pkg:") uuid = project(io, pkg, path) entrypoint(io, pkg, path) return Dict(pkg => uuid) end function genfile(f::Function, io::IO, dir::AbstractString, file::AbstractString) path = joinpath(dir, file) println(io, " $(Base.contractuser(path))") mkpath(dirname(path)) open(f, path, "w") return end function project(io::IO, pkg::AbstractString, dir::AbstractString) mkpath(dir) name = email = nothing gitname = LibGit2.getconfig("user.name", "") isempty(gitname) || (name = gitname) gitmail = LibGit2.getconfig("user.email", "") isempty(gitmail) || (email = gitmail) if name === nothing for env in ["GIT_AUTHOR_NAME", "GIT_COMMITTER_NAME", "USER", "USERNAME", "NAME"] name = get(ENV, env, nothing) name !== nothing && break end end name === nothing && (name = "Unknown") if email === nothing for env in ["GIT_AUTHOR_EMAIL", "GIT_COMMITTER_EMAIL", "EMAIL"]; email = get(ENV, env, nothing) email !== nothing && break end end authors = ["$name " * (email === nothing ? "" : "<$email>")] uuid = UUIDs.uuid4() genfile(io, dir, "Project.toml") do file_io toml = Dict{String,Any}("authors" => authors, "name" => pkg, "uuid" => string(uuid), "version" => "0.1.0", ) TOML.print(file_io, toml, sorted=true, by=key -> (Types.project_key_order(key), key)) end return uuid end function entrypoint(io::IO, pkg::AbstractString, dir) genfile(io, joinpath(dir, "src"), "$pkg.jl") do file_io print(file_io, """ module $pkg greet() = print("Hello World!") end # module $pkg """ ) end end x���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/REPLMode/REPLMode.jlซi������# This file is a part of Julia. License is MIT: https://julialang.org/license module REPLMode using Markdown, UUIDs, Dates import REPL import REPL: LineEdit, REPLCompletions import REPL: TerminalMenus import ..casesensitive_isdir, ..OFFLINE_MODE, ..linewrap, ..pathrepr using ..Types, ..Operations, ..API, ..Registry, ..Resolve import ..stdout_f, ..stderr_f const TEST_MODE = Ref{Bool}(false) const PRINTED_REPL_WARNING = Ref{Bool}(false) ######################### # Specification Structs # ######################### #---------# # Options # #---------# const OptionDeclaration = Vector{Pair{Symbol,Any}} struct OptionSpec name::String short_name::Union{Nothing,String} api::Pair{Symbol, Any} takes_arg::Bool end # TODO assert names matching lex regex # assert now so that you don't fail at user time # see function `REPLMode.api_options` function OptionSpec(;name::String, short_name::Union{Nothing,String}=nothing, takes_arg::Bool=false, api::Pair{Symbol,<:Any})::OptionSpec takes_arg && @assert hasmethod(api.second, Tuple{String}) return OptionSpec(name, short_name, api, takes_arg) end function OptionSpecs(decs::Vector{OptionDeclaration}) specs = Dict{String, OptionSpec}() for x in decs opt_spec = OptionSpec(;x...) @assert !haskey(specs, opt_spec.name) # don't overwrite specs[opt_spec.name] = opt_spec if opt_spec.short_name !== nothing @assert !haskey(specs, opt_spec.short_name::String) # don't overwrite specs[opt_spec.short_name::String] = opt_spec end end return specs end #-----------# # Arguments # #-----------# struct ArgSpec count::Pair parser::Function end #----------# # Commands # #----------# const CommandDeclaration = Vector{Pair{Symbol,Any}} struct CommandSpec canonical_name::String short_name::Union{Nothing,String} api::Function should_splat::Bool argument_spec::ArgSpec option_specs::Dict{String,OptionSpec} completions::Union{Nothing,Function} description::String help::Union{Nothing,Markdown.MD} end default_parser(xs, options) = unwrap(xs) function CommandSpec(;name::Union{Nothing,String} = nothing, short_name::Union{Nothing,String} = nothing, api::Union{Nothing,Function} = nothing, should_splat::Bool = true, option_spec::Vector{OptionDeclaration} = OptionDeclaration[], help::Union{Nothing,Markdown.MD} = nothing, description::Union{Nothing,String} = nothing, completions::Union{Nothing,Function} = nothing, arg_count::Pair = (0=>0), arg_parser::Function = default_parser, )::CommandSpec name === nothing && error("Supply a canonical name") description === nothing && error("Supply a description") api === nothing && error("Supply API dispatch function for `$(name)`") # TODO assert isapplicable completions dict, string return CommandSpec(name, short_name, api, should_splat, ArgSpec(arg_count, arg_parser), OptionSpecs(option_spec), completions, description, help) end function CommandSpecs(declarations::Vector{CommandDeclaration}) specs = Dict{String,CommandSpec}() for dec in declarations spec = CommandSpec(;dec...) @assert !haskey(specs, spec.canonical_name) "duplicate spec entry" specs[spec.canonical_name] = spec if spec.short_name !== nothing @assert !haskey(specs, spec.short_name::String) "duplicate spec entry" specs[spec.short_name::String] = spec end end return specs end function CompoundSpecs(compound_declarations) compound_specs = Dict{String,Dict{String,CommandSpec}}() for (name, command_declarations) in compound_declarations specs = CommandSpecs(command_declarations) @assert !haskey(compound_specs, name) "duplicate super spec entry" compound_specs[name] = specs end return compound_specs end ########### # Parsing # ########### # QString: helper struct for retaining quote information struct QString raw::String isquoted::Bool end unwrap(xs::Vector{QString}) = map(x -> x.raw, xs) #---------# # Options # #---------# struct Option val::String argument::Union{Nothing,String} Option(val::AbstractString) = new(val, nothing) Option(val::AbstractString, arg::Union{Nothing,String}) = new(val, arg) end Base.show(io::IO, opt::Option) = print(io, "--$(opt.val)", opt.argument === nothing ? "" : "=$(opt.argument)") wrap_option(option::String) = length(option) == 1 ? "-$option" : "--$option" is_opt(word::AbstractString) = first(word) == '-' && word != "-" function parse_option(word::AbstractString)::Option m = match(r"^(?: -([a-z]) | --((?:[a-z]{1,}-?)*)(?:\s*=\s*(\S*))? )$"ix, word) m === nothing && pkgerror("malformed option: ", repr(word)) option_name = m.captures[1] !== nothing ? something(m.captures[1]) : something(m.captures[2]) option_arg = m.captures[3] === nothing ? nothing : String(something(m.captures[3])) return Option(option_name, option_arg) end #-----------# # Statement # #-----------# # Statement: text-based representation of a command Base.@kwdef mutable struct Statement super::Union{Nothing,String} = nothing spec::Union{Nothing,CommandSpec} = nothing options::Union{Vector{Option},Vector{String}} = String[] arguments::Vector{QString} = QString[] end function lex(cmd::String)::Vector{QString} replace_comma = (nothing!=match(r"^(add|rm|remove|status|precompile)+\s", cmd)) in_doublequote = false in_singlequote = false qstrings = QString[] token_in_progress = Char[] push_token!(is_quoted) = begin push!(qstrings, QString(String(token_in_progress), is_quoted)) empty!(token_in_progress) end for c in cmd if c == '"' if in_singlequote # raw char push!(token_in_progress, c) else # delimiter in_doublequote ? push_token!(true) : push_token!(false) in_doublequote = !in_doublequote end elseif c == '\'' if in_doublequote # raw char push!(token_in_progress, c) else # delimiter in_singlequote ? push_token!(true) : push_token!(false) in_singlequote = !in_singlequote end elseif c == ' ' if in_doublequote || in_singlequote # raw char push!(token_in_progress, c) else # delimiter push_token!(false) end elseif c == ';' if in_doublequote || in_singlequote # raw char push!(token_in_progress, c) else # special delimiter push_token!(false) push!(qstrings, QString(";", false)) end elseif c == ',' if in_doublequote || in_singlequote || !replace_comma # raw char # don't replace ',' in quotes push!(token_in_progress, c) else push_token!(false) push!(qstrings, QString("", false)) end else push!(token_in_progress, c) end end (in_doublequote || in_singlequote) ? pkgerror("unterminated quote") : push_token!(false) # to avoid complexity in the main loop, empty tokens are allowed above and # filtered out before returning return filter(x->!isempty(x.raw), qstrings) end function tokenize(cmd::String) cmd = replace(replace(cmd, "\r\n" => "; "), "\n" => "; ") # for multiline commands qstrings = lex(cmd) statements = foldl(qstrings; init=[QString[]]) do collection, next (next.raw == ";" && !next.isquoted) ? push!(collection, QString[]) : push!(collection[end], next) return collection end return statements end function core_parse(words::Vector{QString}; only_cmd=false) statement = Statement() word::Union{Nothing,QString} = nothing function next_word!() isempty(words) && return false word = popfirst!(words) return true end # begin parsing next_word!() || return statement, ((word === nothing) ? nothing : word.raw) # handle `?` alias for help # It is special in that it requires no space between command and args if word.raw[1]=='?' && !word.isquoted length(word.raw) > 1 && pushfirst!(words, QString(word.raw[2:end],false)) word = QString("?", false) end # determine command super = get(SPECS, word.raw, nothing) if super !== nothing # explicit statement.super = word.raw next_word!() || return statement, word.raw command = get(super, word.raw, nothing) command !== nothing || return statement, word.raw else # try implicit package super = SPECS["package"] command = get(super, word.raw, nothing) command !== nothing || return statement, word.raw end statement.spec = command only_cmd && return statement, word.raw # hack to hook in `help` command next_word!() || return statement, word.raw # full option parsing is delayed so that the completions parser can use the raw string while is_opt(word.raw) push!(statement.options, word.raw) next_word!() || return statement, word.raw end pushfirst!(words, word) statement.arguments = words return statement, words[end].raw end parse(input::String) = map(Base.Iterators.filter(!isempty, tokenize(input))) do words statement, input_word = core_parse(words) statement.spec === nothing && pkgerror("`$input_word` is not a recognized command. Type ? for help with available commands") statement.options = map(parse_option, statement.options) statement end #------------# # APIOptions # #------------# # Do NOT introduce a constructor for APIOptions # as long as it's an alias for Dict const APIOptions = Dict{Symbol, Any} function api_options(options::Vector{Option}, specs::Dict{String, OptionSpec}) api_opts = APIOptions() enforce_option(options, specs) for option in options spec = specs[option.val] api_opts[spec.api.first] = spec.takes_arg ? spec.api.second(option.argument) : spec.api.second end return api_opts end Context!(ctx::APIOptions)::Context = Types.Context!(collect(ctx)) #---------# # Command # #---------# Base.@kwdef struct Command spec::Union{Nothing,CommandSpec} = nothing options::APIOptions = APIOptions() arguments::Vector = [] end function enforce_option(option::Option, specs::Dict{String,OptionSpec}) spec = get(specs, option.val, nothing) spec !== nothing || pkgerror("option '$(option.val)' is not a valid option") if spec.takes_arg option.argument !== nothing || pkgerror("option '$(option.val)' expects an argument, but no argument given") else # option is a switch option.argument === nothing || pkgerror("option '$(option.val)' does not take an argument, but '$(option.argument)' given") end end """ checks: - options are understood by the given command - options do not conflict (e.g. `rm --project --manifest`) - options which take an argument are given arguments - options which do not take arguments are not given arguments """ function enforce_option(options::Vector{Option}, specs::Dict{String,OptionSpec}) unique_keys = Symbol[] get_key(opt::Option) = specs[opt.val].api.first # per option checking foreach(x->enforce_option(x,specs), options) # checking for compatible options for opt in options key = get_key(opt) if key in unique_keys conflicting = filter(opt->get_key(opt) == key, options) pkgerror("Conflicting options: $conflicting") else push!(unique_keys, key) end end end """ Final parsing (and checking) step. This step is distinct from `parse` in that it relies on the command specifications. """ function Command(statement::Statement)::Command # options options = api_options(statement.options, statement.spec.option_specs) # arguments arg_spec = statement.spec.argument_spec arguments = arg_spec.parser(statement.arguments, options) if !((arg_spec.count.first <= length(arguments) <= arg_spec.count.second)::Bool) pkgerror("Wrong number of arguments") end return Command(statement.spec, options, arguments) end ############# # Execution # ############# function do_cmd(repl::REPL.AbstractREPL, input::String; do_rethrow=false) if !isinteractive() && !TEST_MODE[] && !PRINTED_REPL_WARNING[] @warn "The Pkg REPL mode is intended for interactive use only, and should not be used from scripts. It is recommended to use the functional API instead." PRINTED_REPL_WARNING[] = true end try statements = parse(input) commands = map(Command, statements) xs = [] for command in commands push!(xs, do_cmd!(command, repl)) end return TEST_MODE[] ? xs : nothing catch err do_rethrow && rethrow() if err isa PkgError || err isa Resolve.ResolverError Base.display_error(repl.t.err_stream, ErrorException(sprint(showerror, err)), Ptr{Nothing}[]) else Base.display_error(repl.t.err_stream, err, Base.catch_backtrace()) end end end function do_cmd!(command::Command, repl) # REPL specific commands command.spec === SPECS["package"]["help"] && return Base.invokelatest(do_help!, command, repl) # API commands if command.spec.should_splat TEST_MODE[] && return command.spec.api, command.arguments..., command.options command.spec.api(command.arguments...; collect(command.options)...) # TODO is invokelatest still needed? else TEST_MODE[] && return command.spec.api, command.arguments, command.options command.spec.api(command.arguments; collect(command.options)...) end end function parse_command(words::Vector{QString}) statement, word = core_parse(words; only_cmd=true) if statement.super === nothing && statement.spec === nothing pkgerror("invalid input: `$word` is not a command") end return statement.spec === nothing ? statement.super : statement.spec end function do_help!(command::Command, repl::REPL.AbstractREPL) disp = REPL.REPLDisplay(repl) if isempty(command.arguments) Base.display(disp, help) return end help_md = md"" cmd = parse_command(command.arguments) if cmd isa String # gather all helps for super spec `cmd` all_specs = sort!(unique(values(SPECS[cmd])); by=(spec->spec.canonical_name)) for spec in all_specs isempty(help_md.content) || push!(help_md.content, md"---") push!(help_md.content, spec.help) end elseif cmd isa CommandSpec push!(help_md.content, cmd.help) end !isempty(command.arguments) && @warn "More than one command specified, only rendering help for first" Base.display(disp, help_md) end ###################### # REPL mode creation # ###################### # Provide a string macro pkg"cmd" that can be used in the same way # as the REPLMode `pkg> cmd`. Useful for testing and in environments # where we do not have a REPL, e.g. IJulia. struct MiniREPL <: REPL.AbstractREPL display::TextDisplay t::REPL.Terminals.TTYTerminal end function MiniREPL() MiniREPL(TextDisplay(stdout_f()), REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), stdin, stdout_f(), stderr_f())) end REPL.REPLDisplay(repl::MiniREPL) = repl.display const minirepl = Ref{MiniREPL}() __init__() = minirepl[] = MiniREPL() macro pkg_str(str::String) :($(do_cmd)(minirepl[], $str; do_rethrow=true)) end pkgstr(str::String) = do_cmd(minirepl[], str; do_rethrow=true) struct PkgCompletionProvider <: LineEdit.CompletionProvider end function LineEdit.complete_line(c::PkgCompletionProvider, s) partial = REPL.beforecursor(s.input_buffer) full = LineEdit.input_string(s) ret, range, should_complete = completions(full, lastindex(partial)) return ret, partial[range], should_complete end prev_project_file = nothing prev_project_timestamp = nothing prev_prefix = "" function projname(project_file::String) project = try Types.read_project(project_file) catch nothing end if project === nothing || project.name === nothing name = basename(dirname(project_file)) else name = project.name::String end for depot in Base.DEPOT_PATH envdir = joinpath(depot, "environments") if startswith(abspath(project_file), abspath(envdir)) return "@" * name end end return name end function promptf() global prev_project_timestamp, prev_prefix, prev_project_file project_file = try Types.find_project_file() catch nothing end prefix = "" if project_file !== nothing if prev_project_file == project_file && prev_project_timestamp == mtime(project_file) prefix = prev_prefix else project_name = projname(project_file) if project_name !== nothing if textwidth(project_name) > 30 project_name = first(project_name, 27) * "..." end prefix = "($(project_name)) " prev_prefix = prefix prev_project_timestamp = mtime(project_file) prev_project_file = project_file end end end if OFFLINE_MODE[] prefix = "$(prefix)[offline] " end return "$(prefix)pkg> " end # Set up the repl Pkg REPLMode function create_mode(repl::REPL.AbstractREPL, main::LineEdit.Prompt) pkg_mode = LineEdit.Prompt(promptf; prompt_prefix = repl.options.hascolor ? Base.text_colors[:blue] : "", prompt_suffix = "", complete = PkgCompletionProvider(), sticky = true) pkg_mode.repl = repl hp = main.hist hp.mode_mapping[:pkg] = pkg_mode pkg_mode.hist = hp search_prompt, skeymap = LineEdit.setup_search_keymap(hp) prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, pkg_mode) pkg_mode.on_done = (s, buf, ok) -> begin ok || return REPL.transition(s, :abort) input = String(take!(buf)) REPL.reset(repl) do_cmd(repl, input) REPL.prepare_next(repl) REPL.reset_state(s) s.current_mode.sticky || REPL.transition(s, main) end mk = REPL.mode_keymap(main) shell_mode = nothing for mode in Base.active_repl.interface.modes if mode isa LineEdit.Prompt mode.prompt == "shell> " && (shell_mode = mode) end end repl_keymap = Dict() if shell_mode !== nothing let shell_mode=shell_mode repl_keymap[';'] = function (s,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) LineEdit.transition(s, shell_mode) do LineEdit.state(s, shell_mode).input_buffer = buf end else LineEdit.edit_insert(s, ';') end end end end b = Dict{Any,Any}[ skeymap, repl_keymap, mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults ] pkg_mode.keymap_dict = LineEdit.keymap(b) return pkg_mode end function repl_init(repl::REPL.AbstractREPL) main_mode = repl.interface.modes[1] pkg_mode = create_mode(repl, main_mode) push!(repl.interface.modes, pkg_mode) keymap = Dict{Any,Any}( ']' => function (s,args...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) LineEdit.transition(s, pkg_mode) do LineEdit.state(s, pkg_mode).input_buffer = buf end else LineEdit.edit_insert(s, ']') end end ) main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, keymap) return end ######## # SPEC # ######## include("completions.jl") include("argument_parsers.jl") include("command_declarations.jl") const SPECS = CompoundSpecs(compound_declarations) ######## # HELP # ######## function canonical_names() # add "package" commands xs = [(spec.canonical_name => spec) for spec in unique(values(SPECS["package"]))] sort!(xs, by=first) # add other super commands, e.g. "registry" for (super, specs) in SPECS super != "package" || continue # skip "package" temp = [(join([super, spec.canonical_name], " ") => spec) for spec in unique(values(specs))] append!(xs, sort!(temp, by=first)) end return xs end function gen_help() help = md""" **Welcome to the Pkg REPL-mode**. To return to the `julia>` prompt, either press backspace when the input line is empty or press Ctrl+C. Full documentation available at https://pkgdocs.julialang.org/ **Synopsis** pkg> cmd [opts] [args] Multiple commands can be given on the same line by interleaving a `;` between the commands. Some commands have an alias, indicated below. **Commands** """ for (command, spec) in canonical_names() short_name = spec.short_name === nothing ? "" : ", `" * spec.short_name::String * '`' push!(help.content, Markdown.parse("`$command`$short_name: $(spec.description)")) end return help end const help = gen_help() const REG_WARNED = Ref{Bool}(false) function try_prompt_pkg_add(pkgs::Vector{Symbol}) ctx = try Context() catch # Context() will error if there isn't an active project. # If we can't even do that, exit early. return false end if isempty(ctx.registries) if !REG_WARNED[] printstyled(ctx.io, " โ”‚ "; color=:green) printstyled(ctx.io, "Attempted to find missing packages in package registries but no registries are installed.\n") printstyled(ctx.io, " โ”” "; color=:green) printstyled(ctx.io, "Use package mode to install a registry. `pkg> registry add` will install the default registries.\n\n") REG_WARNED[] = true end return false end available_uuids = [Types.registered_uuids(ctx.registries, String(pkg)) for pkg in pkgs] # vector of vectors filter!(u -> all(!isequal(Operations.JULIA_UUID), u), available_uuids) # "julia" is in General but not installable isempty(available_uuids) && return false available_pkgs = pkgs[isempty.(available_uuids) .== false] isempty(available_pkgs) && return false resp = try plural1 = length(pkgs) == 1 ? "" : "s" plural2 = length(available_pkgs) == 1 ? "a package" : "packages" plural3 = length(available_pkgs) == 1 ? "is" : "are" plural4 = length(available_pkgs) == 1 ? "" : "s" missing_pkg_list = length(pkgs) == 1 ? String(pkgs[1]) : "[$(join(pkgs, ", "))]" available_pkg_list = length(available_pkgs) == 1 ? String(available_pkgs[1]) : "[$(join(available_pkgs, ", "))]" msg1 = "Package$(plural1) $(missing_pkg_list) not found, but $(plural2) named $(available_pkg_list) $(plural3) available from a registry." for line in linewrap(msg1, io = ctx.io, padding = length(" โ”‚ ")) printstyled(ctx.io, " โ”‚ "; color=:green) println(ctx.io, line) end printstyled(ctx.io, " โ”‚ "; color=:green) println(ctx.io, "Install package$(plural4)?") msg2 = string("add ", join(available_pkgs, ' ')) for (i, line) in pairs(linewrap(msg2; io = ctx.io, padding = length(string(" | ", REPLMode.promptf())))) printstyled(ctx.io, " โ”‚ "; color=:green) if i == 1 printstyled(ctx.io, REPLMode.promptf(); color=:blue) else print(ctx.io, " "^length(REPLMode.promptf())) end println(ctx.io, line) end printstyled(ctx.io, " โ”” "; color=:green) Base.prompt(stdin, ctx.io, "(y/n/o)", default = "y") catch err if err isa InterruptException # if ^C is entered println(ctx.io) return false end rethrow() end if isnothing(resp) # if ^D is entered println(ctx.io) return false end resp = strip(resp) lower_resp = lowercase(resp) if lower_resp in ["y", "yes"] API.add(string.(available_pkgs)) elseif lower_resp in ["o"] editable_envs = filter(v -> v != "@stdlib", LOAD_PATH) option_list = String[] keybindings = Char[] shown_envs = String[] # We use digits 1-9 as keybindings in the env selection menu # That's why we can display at most 9 items in the menu for i in 1:min(length(editable_envs), 9) env = editable_envs[i] expanded_env = Base.load_path_expand(env) isnothing(expanded_env) && continue n = length(option_list) + 1 push!(option_list, "$(n): $(pathrepr(expanded_env)) ($(env))") push!(keybindings, only("$n")) push!(shown_envs, expanded_env) end menu = TerminalMenus.RadioMenu(option_list, keybindings=keybindings, pagesize=length(option_list)) default = something( # select the first non-default env by default, if possible findfirst(!=(Base.active_project()), shown_envs), 1 ) print(ctx.io, "\e[1A\e[1G\e[0J") # go up one line, to the start, and clear it printstyled(ctx.io, " โ”” "; color=:green) choice = try TerminalMenus.request("Select environment:", menu, cursor=default) catch err if err isa InterruptException # if ^C is entered println(ctx.io) return false end rethrow() end choice == -1 && return false API.activate(shown_envs[choice]) do API.add(string.(available_pkgs)) end elseif (lower_resp in ["n"]) return false else println(ctx.io, "Selection not recognized") return false end if length(available_pkgs) < length(pkgs) return false # declare that some pkgs couldn't be installed else return true end end end #module {���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/REPLMode/completions.jlึ!������######################## # Completion Functions # ######################## function _shared_envs() possible = String[] for depot in Base.DEPOT_PATH envdir = joinpath(depot, "environments") isdir(envdir) || continue append!(possible, readdir(envdir)) end return possible end function complete_activate(options, partial, i1, i2) shared = get(options, :shared, false) if shared return _shared_envs() elseif !isempty(partial) && first(partial) == '@' return "@" .* _shared_envs() else return complete_local_dir(partial, i1, i2) end end function complete_local_dir(s, i1, i2) expanded_user = false oldi2 = i2 if !isempty(s) && s[1] == '~' expanded_user = true s = expanduser(s) i2 += textwidth(homedir()) - 1 end return complete_expanded_local_dir(s, i1, i2, expanded_user, oldi2) # easiest way to avoid #15276 from boxing `s` end function complete_expanded_local_dir(s, i1, i2, expanded_user, oldi2) cmp = REPL.REPLCompletions.complete_path(s, i2, shell_escape=true) cmp2 = cmp[2] completions = [REPL.REPLCompletions.completion_text(p) for p in cmp[1]] completions = filter!(x -> isdir(s[1:prevind(s, first(cmp2)-i1+1)]*x), completions) if expanded_user if length(completions) == 1 && endswith(joinpath(homedir(), ""), first(completions)) completions = [joinpath(s, "")] else completions = [joinpath(dirname(s), x) for x in completions] end return completions, i1:oldi2, true end return completions, cmp[2], !isempty(completions) end const JULIA_UUID = UUID("1222c4b2-2114-5bfd-aeef-88e4692bbb3e") function complete_remote_package(partial) isempty(partial) && return String[] cmp = Set{String}() for reg in Registry.reachable_registries() for (uuid, regpkg) in reg name = regpkg.name name in cmp && continue if startswith(regpkg.name, partial) pkg = Registry.registry_info(regpkg) compat_info = Registry.compat_info(pkg) # Filter versions for (v, uncompressed_compat) in compat_info Registry.isyanked(pkg, v) && continue # TODO: Filter based on offline mode is_julia_compat = nothing for (pkg_uuid, vspec) in uncompressed_compat if pkg_uuid == JULIA_UUID found_julia_compat = true is_julia_compat = VERSION in vspec is_julia_compat && continue end end # Found a compatible version or compat on julia at all => compatible if is_julia_compat === nothing || is_julia_compat push!(cmp, name) break end end end end end return sort!(collect(cmp)) end function complete_help(options, partial) names = String[] for cmds in values(SPECS) append!(names, [spec.canonical_name for spec in values(cmds)]) end return sort!(unique!(append!(names, collect(keys(SPECS))))) end function complete_installed_packages(options, partial) env = try EnvCache() catch err err isa PkgError || rethrow() return String[] end mode = get(options, :mode, PKGMODE_PROJECT) return mode == PKGMODE_PROJECT ? collect(keys(env.project.deps)) : unique!([entry.name for (uuid, entry) in env.manifest]) end function complete_all_installed_packages(options, partial) env = try EnvCache() catch err err isa PkgError || rethrow() return String[] end return unique!([entry.name for (uuid, entry) in env.manifest]) end function complete_installed_packages_and_compat(options, partial) env = try EnvCache() catch err err isa PkgError || rethrow() return String[] end return map(vcat(collect(keys(env.project.deps)), "julia")) do d compat_str = Operations.get_compat_str(env.project, d) isnothing(compat_str) ? d : string(d, " ", compat_str) end end function complete_add_dev(options, partial, i1, i2) comps, idx, _ = complete_local_dir(partial, i1, i2) if occursin(Base.Filesystem.path_separator_re, partial) return comps, idx, !isempty(comps) end comps = vcat(comps, sort(complete_remote_package(partial))) if !isempty(partial) append!(comps, filter!(startswith(partial), first.(values(Types.stdlibs())))) end return comps, idx, !isempty(comps) end ######################## # COMPLETION INTERFACE # ######################## function default_commands() names = collect(keys(SPECS)) append!(names, map(x -> getproperty(x, :canonical_name), values(SPECS["package"]))) return sort(unique(names)) end function complete_command(statement::Statement, final::Bool, on_sub::Bool) if statement.super !== nothing if (!on_sub && final) || (on_sub && !final) # last thing determined was the super -> complete canonical names of subcommands specs = SPECS[statement.super] names = map(x -> getproperty(x, :canonical_name), values(specs)) return sort(unique(names)) end end # complete default names return default_commands() end complete_opt(opt_specs) = unique(sort(map(wrap_option, map(x -> getproperty(x, :name), collect(values(opt_specs)))))) function complete_argument(spec::CommandSpec, options::Vector{String}, partial::AbstractString, offset::Int, index::Int) spec.completions === nothing && return String[] # finish parsing opts local opts try opts = api_options(map(parse_option, options), spec.option_specs) catch e e isa PkgError && return String[] rethrow() end return applicable(spec.completions, opts, partial, offset, index) ? spec.completions(opts, partial, offset, index) : spec.completions(opts, partial) end function _completions(input, final, offset, index) statement, word_count, partial = nothing, nothing, nothing try words = tokenize(input)[end] word_count = length(words) statement, partial = core_parse(words) if final partial = "" # last token is finalized -> no partial end catch return String[], 0:-1, false end # number of tokens which specify the command command_size = count([statement.super !== nothing, true]) command_is_focused() = !((word_count == command_size && final) || word_count > command_size) if statement.spec === nothing # spec not determined -> complete command !command_is_focused() && return String[], 0:-1, false x = complete_command(statement, final, word_count == 2) else command_is_focused() && return String[], 0:-1, false if final # complete arg by default x = complete_argument(statement.spec, statement.options, partial, offset, index) else # complete arg or opt depending on last token x = is_opt(partial) ? complete_opt(statement.spec.option_specs) : complete_argument(statement.spec, statement.options, partial, offset, index) end end # In the case where the completion function wants to deal with indices, it will return a fully # computed completion tuple, just return it # Else, the completions function will just deal with strings and will return a Vector{String} if isa(x, Tuple) return x else possible = filter(possible -> startswith(possible, partial), x) return possible, offset:index, !isempty(possible) end end function completions(full, index)::Tuple{Vector{String},UnitRange{Int},Bool} pre = full[1:index] isempty(pre) && return default_commands(), 0:-1, false # empty input -> complete commands offset_adjust = 0 if length(pre) >= 2 && pre[1] == '?' && pre[2] != ' ' # supports completion on things like `pkg> ?act` with no space pre = string(pre[1], " ", pre[2:end]) offset_adjust = -1 end last = split(pre, ' ', keepempty=true)[end] offset = isempty(last) ? index+1+offset_adjust : last.offset+1+offset_adjust final = isempty(last) # is the cursor still attached to the final token? return _completions(pre, final, offset, index) end €���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/REPLMode/argument_parsers.jlบ ������import ..isdir_nothrow, ..Registry.RegistrySpec, ..isurl struct PackageIdentifier val::String end struct VersionToken version::String end struct Rev rev::String end struct Subdir dir::String end const PackageToken = Union{PackageIdentifier, VersionToken, Rev, Subdir} packagetoken(word::String)::PackageToken = first(word) == '@' ? VersionToken(word[2:end]) : first(word) == '#' ? Rev(word[2:end]) : first(word) == ':' ? Subdir(word[2:end]) : PackageIdentifier(word) ############### # PackageSpec # ############### """ Parser for PackageSpec objects. """ function parse_package(args::Vector{QString}, options; add_or_dev=false)::Vector{PackageSpec} wordsโ€ฒ = package_lex(args) words = String[] for word in wordsโ€ฒ if (m = match(r"https://github.com/(.*?)/(.*?)/(?:tree|commit)/(.*?)$", word)) !== nothing push!(words, "https://github.com/$(m.captures[1])/$(m.captures[2])") push!(words, "#$(m.captures[3])") else push!(words, word) end end args = PackageToken[packagetoken(pkgword) for pkgword in words] return parse_package_args(args; add_or_dev=add_or_dev) end # Match a git repository URL. This includes uses of `@` and `:` but # requires that it has `.git` at the end. let url = raw"((git|ssh|http(s)?)|(git@[\w\-\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git$)(/)?", # Match a `NAME=UUID` package specifier. name_uuid = raw"[^@\#\s:]+\s*=\s*[^@\#\s:]+", # Match a `#BRANCH` branch or tag specifier. branch = raw"\#\s*[^@\#\s]*", # Match an `@VERSION` version specifier. version = raw"@\s*[^@\#\s]*", # Match a `:SUBDIR` subdir specifier. subdir = raw":[^@\#\s]+", # Match any other way to specify a package. This includes package # names, local paths, and URLs that don't match the `url` part. In # order not to clash with the branch, version, and subdir # specifiers, these cannot include `@` or `#`, and `:` is only # allowed if followed by `/` or `\`. For URLs matching this part # of the regex, that means that `@` (e.g. user names) and `:` # (e.g. port) cannot be used but it doesn't have to end with # `.git`. other = raw"([^@\#\s:] | :(/|\\))+" # Combine all of the above. global const package_id_re = Regex( "$url | $name_uuid | $branch | $version | $subdir | $other", "x") end function package_lex(qwords::Vector{QString})::Vector{String} words = String[] for qword in qwords qword.isquoted ? push!(words, qword.raw) : append!(words, map(m->m.match, eachmatch(package_id_re, qword.raw))) end return words end function parse_package_args(args::Vector{PackageToken}; add_or_dev=false)::Vector{PackageSpec} # check for and apply PackageSpec modifier (e.g. `#foo` or `@v1.0.2`) function apply_modifier!(pkg::PackageSpec, args::Vector{PackageToken}) (isempty(args) || args[1] isa PackageIdentifier) && return modifier = popfirst!(args) if modifier isa Subdir pkg.subdir = modifier.dir (isempty(args) || args[1] isa PackageIdentifier) && return modifier = popfirst!(args) end if modifier isa VersionToken pkg.version = modifier.version elseif modifier isa Rev pkg.rev = modifier.rev else pkgerror("Package name/uuid must precede subdir specifier `$args`.") end end pkgs = PackageSpec[] while !isempty(args) arg = popfirst!(args) if arg isa PackageIdentifier pkg = parse_package_identifier(arg; add_or_develop=add_or_dev) apply_modifier!(pkg, args) push!(pkgs, pkg) # Modifiers without a corresponding package identifier -- this is a user error else arg isa VersionToken ? pkgerror("Package name/uuid must precede version specifier `@$arg`.") : arg isa Rev ? pkgerror("Package name/uuid must precede revision specifier `#$(arg.rev)`.") : pkgerror("Package name/uuid must precede subdir specifier `[$arg]`.") end end return pkgs end let uuid = raw"(?i)[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}(?-i)", name = raw"(\w+)(?:\.jl)?" global const name_re = Regex("^$name\$") global const uuid_re = Regex("^$uuid\$") global const name_uuid_re = Regex("^$name\\s*=\\s*($uuid)\$") end # packages can be identified through: uuid, name, or name+uuid # additionally valid for add/develop are: local path, url function parse_package_identifier(pkg_id::PackageIdentifier; add_or_develop=false)::PackageSpec word = pkg_id.val if add_or_develop if isurl(word) return PackageSpec(; url=word) elseif any(occursin.(['\\','/'], word)) || word == "." || word == ".." if casesensitive_isdir(expanduser(word)) return PackageSpec(; path=normpath(expanduser(word))) else pkgerror("`$word` appears to be a local path, but directory does not exist") end end if occursin(name_re, word) && casesensitive_isdir(expanduser(word)) @info "Use `./$word` to add or develop the local directory at `$(Base.contractuser(abspath(word)))`." end end if occursin(uuid_re, word) return PackageSpec(;uuid=UUID(word)) elseif occursin(name_re, word) m = match(name_re, word) return PackageSpec(String(something(m.captures[1]))) elseif occursin(name_uuid_re, word) m = match(name_uuid_re, word) return PackageSpec(String(something(m.captures[1])), UUID(something(m.captures[2]))) else pkgerror("Unable to parse `$word` as a package.") end end ################ # RegistrySpec # ################ function parse_registry(raw_args::Vector{QString}, options; add=false) regs = RegistrySpec[] foreach(x -> push!(regs, parse_registry(x; add=add)), unwrap(raw_args)) return regs end # Registries can be identified through: uuid, name, or name+uuid # when updating/removing. When adding we can accept a local path or url. function parse_registry(word::AbstractString; add=false)::RegistrySpec word = expanduser(word) registry = RegistrySpec() if add && isdir_nothrow(word) # TODO: Should be casesensitive_isdir if isdir(joinpath(word, ".git")) # add path as url and clone it from there registry.url = abspath(word) else # put the path registry.path = abspath(word) end elseif occursin(uuid_re, word) registry.uuid = UUID(word) elseif occursin(name_re, word) m = match(name_re, word) registry.name = String(something(m.captures[1])) elseif occursin(name_uuid_re, word) m = match(name_uuid_re, word) registry.name = String(something(m.captures[1])) registry.uuid = UUID(something(m.captures[2])) elseif add # Guess it is a url then registry.url = String(word) else pkgerror("`$word` cannot be parsed as a registry") end return registry end # # # Other # function parse_activate(args::Vector{QString}, options) isempty(args) && return [] # nothing to do if length(args) == 1 x = first(args) if x.isquoted return [x.raw] end x = x.raw if x == "-" options[:prev] = true return [] elseif first(x) == '@' options[:shared] = true return [x[2:end]] else return [expanduser(x)] end end return args # this is currently invalid input for "activate" end # # # Option Maps # function do_preserve(x::String) x == "installed" && return Types.PRESERVE_ALL_INSTALLED x == "all" && return Types.PRESERVE_ALL x == "direct" && return Types.PRESERVE_DIRECT x == "semver" && return Types.PRESERVE_SEMVER x == "none" && return Types.PRESERVE_NONE x == "tiered_installed" && return Types.PRESERVE_TIERED_INSTALLED x == "tiered" && return Types.PRESERVE_TIERED pkgerror("`$x` is not a valid argument for `--preserve`.") end „���/cache/build/builder-amdci4-2/julialang/julia-release-1-dot-10/usr/share/julia/stdlib/v1.10/Pkg/src/REPLMode/command_declarations.jlwV������const PSA = Pair{Symbol,Any} compound_declarations = [ "package" => CommandDeclaration[ PSA[:name => "test", :api => API.test, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_package, :option_spec => [ PSA[:name => "coverage", :api => :coverage => true], ], :completions => complete_installed_packages, :description => "run tests for packages", :help => md""" test [--coverage] pkg[=uuid] ... Run the tests for package `pkg`. This is done by running the file `test/runtests.jl` in the package directory. The option `--coverage` can be used to run the tests with coverage enabled. The `startup.jl` file is disabled during testing unless julia is started with `--startup-file=yes`. """, ], PSA[:name => "help", :short_name => "?", :api => identity, # dummy API function :arg_count => 0 => Inf, :arg_parser => ((x,y) -> x), :completions => complete_help, :description => "show this message", :help => md""" [?|help] List available commands along with short descriptions. [?|help] cmd If `cmd` is a partial command, display help for all subcommands. If `cmd` is a full command, display help for `cmd`. """, ], PSA[:name => "instantiate", :api => API.instantiate, :option_spec => [ PSA[:name => "project", :short_name => "p", :api => :manifest => false], PSA[:name => "manifest", :short_name => "m", :api => :manifest => true], PSA[:name => "verbose", :short_name => "v", :api => :verbose => true], ], :description => "downloads all the dependencies for the project", :help => md""" instantiate [-v|--verbose] instantiate [-v|--verbose] [-m|--manifest] instantiate [-v|--verbose] [-p|--project] Download all the dependencies for the current project at the version given by the project's manifest. If no manifest exists or the `--project` option is given, resolve and download the dependencies compatible with the project. After packages have been installed the project will be precompiled. For more information see `pkg> ?precompile`. """, ], PSA[:name => "remove", :short_name => "rm", :api => API.rm, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_package, :option_spec => [ PSA[:name => "project", :short_name => "p", :api => :mode => PKGMODE_PROJECT], PSA[:name => "manifest", :short_name => "m", :api => :mode => PKGMODE_MANIFEST], PSA[:name => "all", :api => :all_pkgs => true], ], :completions => complete_installed_packages, :description => "remove packages from project or manifest", :help => md""" [rm|remove] [-p|--project] pkg[=uuid] ... [rm|remove] [-p|--project] [--all] Remove package `pkg` from the project file. Since the name `pkg` can only refer to one package in a project this is unambiguous, but you can specify a `uuid` anyway, and the command is ignored, with a warning, if package name and UUID do not match. When a package is removed from the project file, it may still remain in the manifest if it is required by some other package in the project. Project mode operation is the default, so passing `-p` or `--project` is optional unless it is preceded by the `-m` or `--manifest` options at some earlier point. All packages can be removed by passing `--all`. [rm|remove] [-m|--manifest] pkg[=uuid] ... [rm|remove] [-m|--manifest] [--all] Remove package `pkg` from the manifest file. If the name `pkg` refers to multiple packages in the manifest, `uuid` disambiguates it. Removing a package from the manifest forces the removal of all packages that depend on it, as well as any no-longer-necessary manifest packages due to project package removals. All packages can be removed by passing `--all`. """, ], PSA[:name => "add", :api => API.add, :should_splat => false, :arg_count => 1 => Inf, :arg_parser => ((x,y) -> parse_package(x,y; add_or_dev=true)), :option_spec => [ PSA[:name => "preserve", :takes_arg => true, :api => :preserve => do_preserve], ], :completions => complete_add_dev, :description => "add packages to project", :help => md""" add [--preserve=<opt>] pkg[=uuid] [@version] [#rev] ... Add package `pkg` to the current project file. If `pkg` could refer to multiple different packages, specifying `uuid` allows you to disambiguate. `@version` optionally allows specifying which versions of packages to add. Version specifications are of the form `@1`, `@1.2` or `@1.2.3`, allowing any version with a prefix that matches, or ranges thereof, such as `@1.2-3.4.5`. A git revision can be specified by `#branch` or `#commit`. If a local path is used as an argument to `add`, the path needs to be a git repository. The project will then track that git repository just like it would track a remote repository online. If the package is not located at the top of the git repository, a subdirectory can be specified with `path:subdir/path`. `Pkg` resolves the set of packages in your environment using a tiered approach. The `--preserve` command line option allows you to key into a specific tier in the resolve algorithm. The following table describes the command line arguments to `--preserve` (in order of strictness). | Argument | Description | |:-------------------|:-----------------------------------------------------------------------------------| | `installed` | Like `all` except also only add versions that are already installed | | `all` | Preserve the state of all existing dependencies (including recursive dependencies) | | `direct` | Preserve the state of all existing direct dependencies | | `semver` | Preserve semver-compatible versions of direct dependencies | | `none` | Do not attempt to preserve any version information | | `tiered_installed` | Like `tiered` except first try to add only installed versions | | **`tiered`** | Use the tier that will preserve the most version information while | | | allowing version resolution to succeed (this is the default) | Note: To make the default strategy `tiered_installed` set the env var `JULIA_PKG_PRESERVE_TIERED_INSTALLED` to true. After the installation of new packages the project will be precompiled. For more information see `pkg> ?precompile`. With the `installed` strategy the newly added packages will likely already be precompiled, but if not this may be because either the combination of package versions resolved in this environment has not been resolved and precompiled before, or the precompile cache has been deleted by the LRU cache storage (see `JULIA_MAX_NUM_PRECOMPILE_FILES`). **Examples** ``` pkg> add Example pkg> add --preserve=all Example pkg> add Example@0.5 pkg> add Example#master pkg> add Example#c37b675 pkg> add https://github.com/JuliaLang/Example.jl#master pkg> add git@github.com:JuliaLang/Example.jl.git pkg> add "git@github.com:JuliaLang/Example.jl.git"#master pkg> add https://github.com/Company/MonoRepo:juliapkgs/Package.jl pkg> add Example=7876af07-990d-54b4-ab0e-23690620f79a ``` """, ], PSA[:name => "develop", :short_name => "dev", :api => API.develop, :should_splat => false, :arg_count => 1 => Inf, :arg_parser => ((x,y) -> parse_package(x,y; add_or_dev=true)), :option_spec => [ PSA[:name => "strict", :api => :strict => true], PSA[:name => "local", :api => :shared => false], PSA[:name => "shared", :api => :shared => true], PSA[:name => "preserve", :takes_arg => true, :api => :preserve => do_preserve], ], :completions => complete_add_dev, :description => "clone the full package repo locally for development", :help => md""" [dev|develop] [--preserve=<opt>] [--shared|--local] pkg[=uuid] ... [dev|develop] [--preserve=<opt>] path Make a package available for development. If `pkg` is an existing local path, that path will be recorded in the manifest and used. Otherwise, a full git clone of `pkg` is made. The location of the clone is controlled by the `--shared` (default) and `--local` arguments. The `--shared` location defaults to `~/.julia/dev`, but can be controlled with the `JULIA_PKG_DEVDIR` environment variable. When `--local` is given, the clone is placed in a `dev` folder in the current project. This is not supported for paths, only registered packages. This operation is undone by `free`. The preserve strategies offered by `add` are also available via the `preserve` argument. See `add` for more information. **Examples** ```jl pkg> develop Example pkg> develop https://github.com/JuliaLang/Example.jl pkg> develop ~/mypackages/Example pkg> develop --local Example ``` """, ], PSA[:name => "free", :api => API.free, :should_splat => false, :arg_count => 0 => Inf, :option_spec => [ PSA[:name => "all", :api => :all_pkgs => true], ], :arg_parser => parse_package, :completions => complete_installed_packages, :description => "undoes a `pin`, `develop`, or stops tracking a repo", :help => md""" free pkg[=uuid] ... free [--all] Free pinned packages, which allows it to be upgraded or downgraded again. If the package is checked out (see `help develop`) then this command makes the package no longer being checked out. Specifying `--all` will free all dependencies (direct and indirect). """, ], PSA[:name => "why", :api => API.why, :should_splat => false, :arg_count => 1 => 1, :arg_parser => parse_package, :completions => complete_all_installed_packages, :description => "shows why a package is in the manifest", :help => md""" why pkg[=uuid] ... Show the reason why packages are in the manifest, printed as a path through the dependency graph starting at the direct dependencies. !!! compat "Julia 1.9" The `why` function is added in Julia 1.9 """, ], PSA[:name => "pin", :api => API.pin, :should_splat => false, :arg_count => 0 => Inf, :option_spec => [ PSA[:name => "all", :api => :all_pkgs => true], ], :arg_parser => parse_package, :completions => complete_installed_packages, :description => "pins the version of packages", :help => md""" pin pkg[=uuid] ... pin [--all] Pin packages to given versions, or the current version if no version is specified. A pinned package has its version fixed and will not be upgraded or downgraded. A pinned package has the symbol `โšฒ` next to its version in the status list.. Specifying `--all` will pin all dependencies (direct and indirect). **Examples** ``` pkg> pin Example pkg> pin Example@0.5.0 pkg> pin Example=7876af07-990d-54b4-ab0e-23690620f79a@0.5.0 pkg> pin --all ``` """, ], PSA[:name => "build", :api => API.build, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_package, :option_spec => [ PSA[:name => "verbose", :short_name => "v", :api => :verbose => true], ], :completions => complete_installed_packages, :description => "run the build script for packages", :help => md""" build [-v|--verbose] pkg[=uuid] ... Run the build script in `deps/build.jl` for `pkg` and all of its dependencies in depth-first recursive order. If no packages are given, run the build scripts for all packages in the manifest. The `-v`/`--verbose` option redirects build output to `stdout`/`stderr` instead of the `build.log` file. The `startup.jl` file is disabled during building unless julia is started with `--startup-file=yes`. """, ], PSA[:name => "resolve", :api => API.resolve, :description => "resolves to update the manifest from changes in dependencies of developed packages", :help => md""" resolve Resolve the project i.e. run package resolution and update the Manifest. This is useful in case the dependencies of developed packages have changed causing the current Manifest to be out of sync. """, ], PSA[:name => "activate", :api => API.activate, :arg_count => 0 => 1, :arg_parser => parse_activate, :option_spec => [ PSA[:name => "shared", :api => :shared => true], PSA[:name => "temp", :api => :temp => true], ], :completions => complete_activate, :description => "set the primary environment the package manager manipulates", :help => md""" activate activate [--shared] path activate --temp Activate the environment at the given `path`, or use the first project found in `LOAD_PATH` if no `path` is specified. The active environment is the environment that is modified by executing package commands. When the option `--shared` is given, `path` will be assumed to be a directory name and searched for in the `environments` folders of the depots in the depot stack. In case no such environment exists in any of the depots, it will be placed in the first depot of the stack. Use the `--temp` option to create temporary environments which are removed when the julia process is exited. """ , ], PSA[:name => "update", :short_name => "up", :api => API.up, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_package, :option_spec => [ PSA[:name => "project", :short_name => "p", :api => :mode => PKGMODE_PROJECT], PSA[:name => "manifest", :short_name => "m", :api => :mode => PKGMODE_MANIFEST], PSA[:name => "major", :api => :level => UPLEVEL_MAJOR], PSA[:name => "minor", :api => :level => UPLEVEL_MINOR], PSA[:name => "patch", :api => :level => UPLEVEL_PATCH], PSA[:name => "fixed", :api => :level => UPLEVEL_FIXED], PSA[:name => "preserve", :takes_arg => true, :api => :preserve => do_preserve], ], :completions => complete_installed_packages, :description => "update packages in manifest", :help => md""" [up|update] [-p|--project] [opts] pkg[=uuid] [@version] ... [up|update] [-m|--manifest] [opts] pkg[=uuid] [@version] ... opts: --major | --minor | --patch | --fixed --preserve=<all/direct/none> Update `pkg` within the constraints of the indicated version specifications. These specifications are of the form `@1`, `@1.2` or `@1.2.3`, allowing any version with a prefix that matches, or ranges thereof, such as `@1.2-3.4.5`. In `--project` mode, package specifications only match project packages, while in `--manifest` mode they match any manifest package. Bound level options force the following packages to be upgraded only within the current major, minor, patch version; if the `--fixed` upgrade level is given, then the following packages will not be upgraded at all. After any package updates the project will be precompiled. For more information see `pkg> ?precompile`. """, ], PSA[:name => "generate", :api => API.generate, :arg_count => 1 => 1, :arg_parser => ((x,y) -> map(expanduser, unwrap(x))), :description => "generate files for a new project", :help => md""" generate pkgname Create a minimal project called `pkgname` in the current folder. For more featureful package creation, please see `PkgTemplates.jl`. """, ], PSA[:name => "precompile", :api => API.precompile, :arg_count => 0 => Inf, :completions => complete_installed_packages, :description => "precompile all the project dependencies", :help => md""" precompile precompile pkgs... Precompile all or specified dependencies of the project in parallel. The `startup.jl` file is disabled during precompilation unless julia is started with `--startup-file=yes`. Errors will only throw when precompiling the top-level dependencies, given that not all manifest dependencies may be loaded by the top-level dependencies on the given system. This method is called automatically after any Pkg action that changes the manifest. Any packages that have previously errored during precompilation won't be retried in auto mode until they have changed. To disable automatic precompilation set the environment variable `JULIA_PKG_PRECOMPILE_AUTO=0`. To manually control the number of tasks used set the environment variable `JULIA_NUM_PRECOMPILE_TASKS`. """, ], PSA[:name => "status", :short_name => "st", :api => API.status, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_package, :option_spec => [ PSA[:name => "project", :short_name => "p", :api => :mode => PKGMODE_PROJECT], PSA[:name => "manifest", :short_name => "m", :api => :mode => PKGMODE_MANIFEST], PSA[:name => "diff", :short_name => "d", :api => :diff => true], PSA[:name => "outdated", :short_name => "o", :api => :outdated => true], PSA[:name => "compat", :short_name => "c", :api => :compat => true], PSA[:name => "extensions", :short_name => "e", :api => :extensions => true], ], :completions => complete_installed_packages, :description => "summarize contents of and changes to environment", :help => md""" [st|status] [-d|--diff] [-o|--outdated] [pkgs...] [st|status] [-d|--diff] [-o|--outdated] [-p|--project] [pkgs...] [st|status] [-d|--diff] [-o|--outdated] [-m|--manifest] [pkgs...] [st|status] [-d|--diff] [-e|--extensions] [-p|--project] [pkgs...] [st|status] [-d|--diff] [-e|--extensions] [-m|--manifest] [pkgs...] [st|status] [-c|--compat] [pkgs...] Show the status of the current environment. Packages marked with `โŒƒ` have new versions that may be installed, e.g. via `pkg> up`. Those marked with `โŒ…` have new versions available, but cannot be installed due to compatibility constraints. To see why use `pkg> status --outdated` which shows any packages that are not at their latest version and if any packages are holding them back. Use `pkg> status --extensions` to show dependencies with extensions and what extension dependencies of those that are currently loaded. In `--project` mode (default), the status of the project file is summarized. In `--manifest` mode the output also includes the recursive dependencies of added packages given in the manifest. If there are any packages listed as arguments the output will be limited to those packages. The `--diff` option will, if the environment is in a git repository, limit the output to the difference as compared to the last git commit. The `--compat` option alone shows project compat entries. !!! compat "Julia 1.8" The `โŒƒ` and `โŒ…` indicators were added in Julia 1.8 The `--outdated` and `--compat` options require at least Julia 1.8. """, ], PSA[:name => "compat", :api => API.compat, :arg_count => 0 => 2, :completions => complete_installed_packages_and_compat, :description => "edit compat entries in the current Project and re-resolve", :help => md""" compat [pkg] [compat_string] Edit project [compat] entries directly, or via an interactive menu by not specifying any arguments. When directly editing use tab to complete the package name and any existing compat entry. Specifying a package with a blank compat entry will remove the entry. After changing compat entries a `resolve` will be attempted to check whether the current environment is compliant with the new compat rules. """, ], PSA[:name => "gc", :api => API.gc, :option_spec => [ PSA[:name => "all", :api => :collect_delay => Hour(0)], PSA[:name => "verbose", :short_name => "v", :api => :verbose => true], ], :description => "garbage collect packages not used for a significant time", :help => md""" gc [-v|--verbose] [--all] Free disk space by garbage collecting packages not used for a significant time. The `--all` option will garbage collect all packages which can not be immediately reached from any existing project. Use verbose mode for detailed output. """, ], PSA[:name => "undo", :api => API.undo, :description => "undo the latest change to the active project", :help => md""" undo Undoes the latest change to the active project. """, ], PSA[:name => "redo", :api => API.redo, :description => "redo the latest change to the active project", :help => md""" redo Redoes the changes from the latest [`undo`](@ref). """, ], ], #package "registry" => CommandDeclaration[ PSA[:name => "add", :api => Registry.add, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => ((x,y) -> parse_registry(x,y; add = true)), :description => "add package registries", :help => md""" registry add reg... Add package registries `reg...` to the user depot. Without arguments it adds known registries, i.e. the General registry and registries served by the configured package server. **Examples** ``` pkg> registry add General pkg> registry add https://www.my-custom-registry.com pkg> registry add ``` """, ], PSA[:name => "remove", :short_name => "rm", :api => Registry.rm, :should_splat => false, :arg_count => 1 => Inf, :arg_parser => parse_registry, :description => "remove package registries", :help => md""" registry [rm|remove] reg... Remove package registries `reg...`. **Examples** ``` pkg> registry [rm|remove] General ``` """, ], PSA[:name => "update", :short_name => "up", :api => Registry.update, :should_splat => false, :arg_count => 0 => Inf, :arg_parser => parse_registry, :description => "update package registries", :help => md""" registry [up|update] registry [up|update] reg... Update package registries `reg...`. If no registries are specified all registries will be updated. **Examples** ``` pkg> registry up pkg> registry up General ``` """, ], PSA[:name => "status", :short_name => "st", :api => Registry.status, :description => "information about installed registries", :help => md""" registry [st|status] Display information about installed registries. **Examples** ``` pkg> registry status ``` """, ] ], #registry ] #command_declarations ����